
Мы подобрались к Злу вплотную. Вот они, баги - порождения человеческого разума и несчастного стечения обстоятельств. Они бывают разные. Забавные ( Алён, у нас по уровню ходят глаза, это нормально? ) и не очень ( CRASH BUG on the first level! FIX ASAP...).
Что меня всегда огорчало в вопросах починки багов - это многочисленные советы. Причем большинство из них, они как из страны эльфов. "Запустите дебаггер и в пошаговой отладке вы увидите что происходит". ОК, запустили. Под дебаггером у меня все хорошо. У меня вообще многопоточное приложение, его бессмысленно дебажить пошагово. Или я работаю со звуком, иногда проскакивает треск. Как такое пошагово отлаживать?
Вот еще совет. "Если у вас не работает релизная версия, запустите дебагную и посмотрите там". Запустили. В дебаге все нормально. И чего делать-то?
И это не такая уж и редкая ситуация, когда дебаг с релизом вот так кардинально различаются. Обычно здесь смотрять чем отличаются дебагная и релизная версия и что из этих различий оказалось причиной бага.
Рассказ про такой баг.
Еще в различных форумах можно полчить мудрые советы, начинающиеся со слов "надо было". Крайне полезно, ага.
Поэтому тут реальные советы по поводу реальных багов. Не на все случаи жизни советы, но на многие.
Первое и главное. Отладка багов требует
активного сотрудничества между разработчиками и отделом тестирования, искренней заинтересованности в результате. Конфликты с отделом тестирования и попытки себя уговорить, что "им показалось" бессмысленны. Им не показалось.
И нет такого понятия "чужая бага". Любая бага в проекте - моя. Ведущий программист (иногда задачи в проекте распределяет менеджер, тогда это будет менеджер) назначает чинить багу как правило автора кода, в котором это бага возникла, не потому что этот человек виноват и теперь должен за это поплатиться, а потому что он лучше знает код и быстрее ее починит.
Про пошаговую отладку под дебаггером рассказывать не буду. Это вы все сами знаете и умеете. Баги, которые ловятся таким образом, тривиальны. Единственный момент тут - если вы проводите слишком много времени под дебаггером, это плохой признак. Значит код изначально написан криво.
Если код по каким-то причинам нельзя дебажить пошагово, может спасти вывод в лог. Как правило, в приложениях есть несколько уровней дебагного вывода, устанавливают их ключом.
Можно еще комментарить куски кода и смотреть как это влияет на баг. Вообще бывает полезно код, не относящийся к багу, убирать, чтобы не мешал.
Если "раньше все работало", а потом вдруг перестало, имеет смысл пошерстить
репозиторий и найти методом бинарного поиска версию, с которой начались проблемы.
Трудновоспроизводимые баги заслуживают самого пристального внимания. Здесь спасает анализ ситуации и придирчивое чтение кода. Когда, при каких условиях бага проявлялась? Почему именно тогда? Как повысить воспроизводимость? Если повезет, то через какое-то время у вас будет не один, а несколько случаев проявления баги, их можно сравнивать, искать закономерности.
Выводы могут оказаться неожиданными. Пример:
Open Office не печатал по вторникам. Бага оказалась не в Open Office, а в утилите file, которая определяла тип файла. По вторникам она определяла файл, отправленный на печать, не как PostScript файл и получалось, что напечатать его нельзя. Дело в том, что в начало файла записывалась дата. Начиная с четвертого байта по вторникам там лежало Tue, от Tuesday. Для утилиты это было знаком того, что это файл Erlang JAM.
Первый шаг работы с трудновоспроизводимыми багами - научиться воспроизводить их чаще, повысить воспроизводимость. Например, если бага проявляется только на конных юнитах - побольше конных юинтов, убрать всех остальных. У нас в арканоиде была бага - шарики сквозь кирпичи пролетали. По коду смотрю, ну не может такого быть никак. Вообще непонятно как им это удается, воспроизводится редко. Поставила вниз стену, запустила 20 шариков, скорость побольше. Вот тут оно все и вылезло.
Решение "вот у нас была такая бага, случалась редко, мы над ней поработали и теперь она случается еще реже" - это не прогресс, это не решение проблемы. Возможно, эта фраза и успокоит неопытного менеджера. Но на самом деле тем самым вы ухудшили ситуацию, а не улучшили ее.
Одним из видов трудновоспроизводимых багов являются ошибки по памяти. Работа с "мусором", произвольная порча памяти и подобные. Тут можно смотреть чем именно была затерта память, на что это похоже. Отладка ошибок по памяти подробно проанализирована тут:
Debugging Memory Corruption in Game Development.
Еще один вид трудновоспроизводимых багов - так называемые
гейзенбаги, названные так в честь
принципа неопределенности Гейзенберга. Они пропадают как только пытаешься их отладить. Тут можно анализировать проявления багов, пытаться поэкспериментировать с кодом, поизменять его, что может помочь найти причину возникновения гейзенбага. Подробная статья про гейзенбаги:
Debugging Heisenbugs.
Ошибки в многопоточных приложениях крайне неприятны и плохо воспроизводятся. Тут кроме анализа ситуации можно использовать волшебные тулзы типа
Intel Threads Checker. Вот кроме него ничего и не знаю, прям стыдно. Под другие платформы наверяка есть свои тулзы.
Процесс починки багов любят объяснять, разбивая его на шаги. Давайте я тоже шаги напишу что ли...
Итак, хорошая последовательность действий при починке багов:
1. Понять в чем именно состоит проблема. Воспроизвести.
2. Прикинуть как ее можно решить, выбрать наилучший способ.
3. Имплементировать.
4. Проверить что проблема решена и что новых не возникло. (!не надо пропускать этот пункт)
5. Подумать над тем, что к проблеме привело, чтобы исключить подобные проявления в дальнейшем.
Классическая плохая последовательность действий:
Вот, кажется тут! Да, починил. А, не, не тут и не починил, отдел тестирования опять поймал это... ОК, смотрю опять, опять чиню.
И так по кругу. Процесс и не думает сходиться. Старый код починки остается, он бессмысленно замусоривает проект и служит источником новых багов. Багов остается столько же или их количество растет.
Как замаскироватьЕсли есть проблемы с починкой баги, ее можно временно замаскировать, чтобы потом вернуться к ней позже.
Итак, поздний вечер, завтра надо проект поставлять, пять минут назад была обнаружена злая бага (почему мы не замечали это раньше???), как чинить непонятно вообще. Очень хочется спать и есть. В таком случае, преисполнившись ненавистью к себе, обрамляем код комментариями //FIXME или тем, что принято в вашем стандарте кодирования. И пишем что-то вроде
if( value == 6 ) value = 5;
Ну или что-то такое же мерзкое, что решит проблему, хотя бы частично.
Хинт: иногда надо делать поиск в коде по FIXME. Сокрушаться над количеством найденного не надо. Надо чинить.
Байки:
Байка про стенуБайка про return в пустотуОписание одного бага, другого наведённого бага, и одной отладкиСсылки:
Дядя Дима - Рецепты отладки. 3 типа нестабильностей.Дядя Дима - Теория ошибок. Нестабильности первого рода.Дядя Дима - Теория ошибок. Нестабильности второго рода.Дядя Дима - Теория ошибок. Нестабильности третьего и четвертого рода.