суббота, 28 ноября 2009 г.

[comp.prog.bugs] Серия заметок про отладку в блоге “Алёна С++”

В блоге “Алёна С++” началась публикация серии заметок про отладку, уже опубликованы первая (“Искусство отладки”) и вторая (“Искусство отладки: как предупредить появление багов”) части.

Если вы начинающий программист, то имеет смысл прочитать и их, и пройтись по ссылкам, данным как в самих заметках, так и в комментариях к ним. В частности, начинающим программистам нужно как можно раньше усвоить одну простую вещь:

Вообще написали что-то - проверьте. Не надо надеяться, что багов в вашем коде нет. Потому что они там есть. И чем раньше вы их поймаете, тем лучше.

Впрочем, старым и матерым программистам так же может быть интересно. Я, например, с удивлением для себя обнаружил, что существует такой феномен, как “отладка – это не престижно”. Никогда с таким не сталкивался раньше. Встречал мнение, что программирование – это ерунда, а вот разработка архитектуры – это круто. А так же с мнением, что и разработка архитектуры это ерунда, а вот создание математических моделей… Теперь вот узнал про то, что для некоторых отладка – это не престижно. Век живи, век учись.

Нужно отдать должное Алёне Сагалаевой. Она решилась затронуть большую и сложную тему. Я бы не отважился. Во-первых, у меня вылавливать баги получается, но как я это делаю – я и сам понятия не имею. Во-вторых, я убежден, что гораздо лучше обучают отладке описания конкретных примеров поиска ошибок, а не абстрактные правильные вещи (вроде хорошей архитектуры и использования unit-тестов). Например, многие начинающие программисты не знают, что при отладке можно просто напросто комментировать часть кода или же заменять его специальными заглушками-имитаторами. Поскольку про пошаговый отладчик они быстро узнают, а вот о таких приемах могут не знать довольно долго.

Ну и в завершение размышлизм :) Придумалась метафора. Разработка программ с помощью методов, которые значительно уменьшают количество багов (unit-тесты, статическая типизация, Design By Contract, хардкорная функциональщина с зависимыми типами и пр., языки спецификаций и инструменты верификации программ) – это как проектирование корабля с повышенной живучестью. Фанаты подобных инструментов могут искренне верить в то, что можно построить непотопляемый корабль.

А средства поиска и устранения багов – это как оснащение такого корабля средствами спасения на случай, если он все-таки пошел ко дну.

И фокус в том, что разработчик ПО должен стремится к достижению максимального успеха в первой части (разработке без ошибок). Но он так же должен мастерски владеть и спасательными средствами (отладкой). Поскольку прагматичный разработчик знает, что непотопляемых кораблей не бывает ;)

7 комментариев:

Alexander P. комментирует...

Про реальные баги мне нравилось читать ещё вот это: http://ddima.livejournal.com/?skip=80 (Специфика геймдев, но думаю актуально не только там :-)).

Идея вашей метафоры мне нравится. Только я бы к спасательным средствам назвал добавил бы систему самовосстановления программы (http://bishop-it.ru/2009/10/reliability/).

Евгений Охотников комментирует...

>систему самовосстановления программы (http://bishop-it.ru/2009/10/reliability/).

Ох хотел я тогда по этому поводу написать, да приостановился на сборе материалов по надежности и отказоустойчивости программ. Поскольку, имхо, bishop-it там такую профанацию на эту тему устроил, что просто держись.

Quaker комментирует...

А я вот недавно интересовался такими инструментами, как статические анализаторы кода. На мой взгляд, для C/C++ программ - это очень значимые инструменты. Среди бесплатных можно отметить CppCheck (http://sourceforge.net/projects/cppcheck/), правда эта система еще далека до идеала. А вот коммерческие системы стоят очень дорого (более 1000$). Есть отдельные системы для отслеживания ошибок в 64-битных и параллельных программах (http://www.viva64.com/ru/main/, там же почитайте интересный блог). Заинтересовали системы Polyspace от MathWorks (http://www.mathworks.com/products/polyspace/), а также C++ Test (http://www.parasoft.com/jsp/products/home.jsp?product=CppTest&) и Insure++ той же компании для динамического анализа и поиска ошибок памяти. Но самый интересный раздел, на который я наткнулся - Bug of the Month (http://www.gimpel.com/html/bugs.htm). Интересно, если опытному c++ программисту показать эти примеры, то сколько ошибок он сможет найти без использования дополнительных средств (а лучше без использования компьютера вообще)?

Quaker комментирует...

Для С/C++ есть еще более продвинутые инструменты - динамический детектор инвариантов Daikon (http://groups.csail.mit.edu/pag/daikon/) и система проверки функциональных спецификаций Frama-C (http://frama-c.cea.fr/).

Евгений Охотников комментирует...

2Quaker:
>Интересно, если опытному c++ программисту показать эти примеры, то сколько ошибок он сможет найти без использования дополнительных средств (а лучше без использования компьютера вообще)?

Ну давайте попробуем :) Я этих примеров не видел, так что давайте какой-нибудь один, посмотрю завтра.

Quaker комментирует...

Эти примеры я привел просто для интереса. Можете сами их посмотреть и порешать, тем более, что они небольшие по объему (20-30 строк).

Евгений Охотников комментирует...

Глянул несколько примеров:

http://www.gimpel.com/html/newbugs/bug591.htm -- это оказалось просто.

http://www.gimpel.com/html/newbugs/bug650.htm -- остаток от деления на 4 никогда не может быть равен 4-м, так что здесь так же просто (хотя объяснение там было какое-то уж слишком большое).

http://www.gimpel.com/html/newbugs/bug444.htm -- здесь так же все просто, цикл бы продолжался проходом по памяти, пока не наткнулся бы на случайный ноль. Раньше в C/C++ ных программах было обычной практикой объявлять массивы таких структур, в последнем элементе которого хранить нулевой указатель в каком-то поле. И цикл бы записывался так: for(p=A;p->name;++p)
Вот вместо p->name по ошибке легко было написать p. Или тупо забыть правильно задать массив (что со мной, кажется, бывало).

http://www.gimpel.com/html/newbugs/bug777.htm -- ну это вообще обнаруживается на раз, если в детстве повезло прослушать курс по вычислительной математике :) В котором чуть ли не сразу учат, что сранивать вещественные числа в программах бесполезно ;) И это, имхо, совсем не C-specific ошибка.

http://www.gimpel.com/html/newbugs/bug864.htm -- это очевидно: модификация одного и того же объекта несколько раз в одном выражении.

http://www.gimpel.com/html/newbugs/bug850.htm -- вот это хорошо! Когда знаешь, что в этом коде ошибка, то она вылавливается на раз (двойной инкремент переменной цикла во втором for). Но если не искать специально ошибку -- и не заметишь ее.

Забавно. Все ошибки такие, что если придется делать code review большого фрагмента кода, то взгляд за них вряд ли зацепится. Исключение разве что для случев bug444 и bug777 -- все таки там ошибки очевидные. А вот места, где результат зависит от порядка вычисления, вряд ли просто так заметишь.