Обзор методов отладки программного обеспечения

Время от времени на форумах «пролетает» вопрос: «Какие методы отладки вы использовали?». Этот пост — мой ответ.

Отладка — это комплексный процесс по выявлению и исправлению дефектов в программном обеспечении.  Сами же дефекты, обычно, обнаруживается в процессе тестирования ПО.

(Прим. Часто под термином «отладка» подразумевают «тестирование» + «непосредственно отладка». В данном посте эти термины я буду разделять.)

Отладка состоит из следующих этапов:

  1. воспроизведение дефекта (любым из доступных способов);
  2. анализ дефекта (поиск причины возникновения дефекта — root-cause);
  3. дизайн исправления дефекта (и возможно ревью, если есть альтернативы);
  4. кодирование исправления дефекта (и какие-либо активности связанные с кодированием);
  5. валидация исправления;
  6. интеграция исправления в кодовую базу или целевую систему;
  7. дополнительные валидации после интеграции (если необходимости).

Пункты 1 и 2 — самые длительные этапы. Они могут объединяться, например, если сложность отладки именно в воспроизведение проблемы и  имеется достаточно assert-ов в коде, то тогда после воспроизведение дефекта root-cause будет автоматически выявлен за счёт детального сообщения об ошибке в assert-е. Но бывает и по-другому, когда дефект воспроизводится легко, но root-cause абсолютно не ясен.

Если root-cause дефекта найден, то разработать исправление не составляет большого труда (конечно, в зависимости от требований к качеству ПО). Поэтому с этапами 1 и 2 в большинстве случаев ассоциируется термин «отладка». Более того, отладка — это рекурсивный процесс. На  любом этапе отладки могут возникнуть новые дефекты, которые придётся отлаживать. Например, какая-то часть исправления в коде работает не так как ожидается и соответственно придётся отлаживать эту часть в изоляции и снова основное время уходит на пункты 1 и 2 и т.д. Таким образом,  под методами отладки дефектов я понимаю методики и подходы выполнения пунктов 1 и 2. (Прим. Другие пункты тоже достаточно важны, но они не входят в скоуп данного поста.)

Намерено не даю никакие ссылки на инструментальные средства, чтобы не делать рекламы. Методы предотвращения дефектов (защитное программирование, инспекции кода и т.п.) вне скоупа данного поста. Хотя, assert-ы как часть методики защитного программирования могут считаться отдельным вполне полезным методом отладки ПО. Также существуют различные методики рассуждений при поиске root-cause-а, но они тоже вне скоупа ;).

И так, методы отладки ПО используемые на данный момент в индустрии (которыми я пользовался) перечислены ниже.

  1. Запуск программы из под отладчика (софтварного, железячного или удалённого дебагера) с пошаговой отладкой, просмотром состояний (переменных, стека, памяти, регистров, тредов и т.п.) в требуемых точках исполнения программы.
  2. Логирования кода — вывод в файл (или консоль и т.п.) входных, выходных аргументов функций, промежуточных состояний (переменных, стека, памяти, передаваемых или получаемых каким-либо образом данных и т.п.) в процессе исполнения программы. Детальный лог является историческим описанием исполнения программы. При сложностях с воспроизведением сценария дефекта, логирование становится основной методикой отладки.
  3. Анализ кода без исполнения программы — поиск причин возникновения дефекта с помощью анализа исходного кода программы, проблемного контента, конфигурации, состояния базы данных и т.п.
  4. Анализ поведения системы или её части (в т.ч. в более простых use-case-ах) — изолирование проблемы, путём упрощения сценария (используя ручное или автоматическое тестирование). Аксиома звучит так: чем проще сценарий, тем проще отладить проблему. Если найти более простой сценарий, то отладка может упроститься.
  5. Unit тестирование — выполнение автоматических unit test-ов в основном изолировано (т.е. в более простых сценариях) для функций (модулей, компонентов и т.п.), и таким образом автоматическое выявление проблемных участков кода. Unit тестирование в каком-то смысле одна из разновидностей отладки путём «анализа поведения системы».
  6. Прототипирование — проверка функций (модулей, библиотек, и т.п.) в изоляции с помощью небольших примеров кода (прототипов). Прототип легче отлаживать, чем целевую систему. Если проблема воспроизводиться с помощью прототипа, отладка упрощается. Unit тестирование в этом смысле более эффективный метод отладки, поскольку unit test-ы выполняются автоматически и «накапливаются» для будущего реюза, а прототипы редко становятся частью системы.
  7. Отладка с помощью memory-dump-ов или crash-дампов (применимо в основном для анализа паник) — разновидность логирования кода, только здесь логируется не просто некая структура памяти, а целиком вся память процесса и состояния регистров, когда возникает exception. По такому дампу памяти, имея дебажные символы, можно «раскрутить» состояние программы (стеков, очередей, переменных и т.п.), в котором она находилась во время паники. Достаточно много существует инструментальных средств для выполнения этой операции.
  8. Отладка с помощью перехватов (hook-ов, spy-ев) — в основном используется в случаях утечки ресурсов, разновидность логирования кода. Основная идея перехват и логирование вызова функций выделения и освобождения ресурса, а также анализ состояния ресурсов (например, памяти) в требуемый момент времени или в нужной точке исполнения программы.
  9. Профилирование кода (если необходима оптимизация производительности) — разновидность логирования кода, хотя часто выполняется с использованием специализированных инструментальных средств (профилировщиков). Этот метод отладки позволяет получить профиль исполнения программы — сколько и какая функция, строчка кода, модуль, и т.п. отнимают процессорного времени, и таким образом найти узкие места.
  10. Выполнения программы (или её части) в другой среде (операционной системе, эмуляторе, симуляторе) — основная идея в том, что если нет инструментальных средств на целевой платформе, то можно спортировать код на другую платформу, где они есть. Также можно изначально писать кросс-платформенный код системы или какой-то её части, и таким образом, при необходимости практически без портирования отлаживать код на другой платформе.
  11. Отладка методом RPC (remote procedure call)  — применимо в основном для встроенного программирования. Суть метода в возможности вызвать любую функцию (модуль и т.п.) передавая аргументы и получая результаты исполнения удалённо с одного хоста на другом вместо того, чтобы тратить время на компиляцию или обновление софта на удалённом хосте (или железке). Существуют множество готовых фреймворков (правда в основном платных), которые инструментируют код и позволяют вызывать любые функции кода через USB или IP соединения.
  12. Отладка путём анализа документации, дизайна, требований или ограничений модулей (программных или аппаратных) — применимо в основном для сложных и крупных проектов. Основная идея понять по имеющейся документации допустимо ли поведение, происходящее в дефекте. Например, поддерживается ли сложная комбинация одновременно работающих фич. Если  поведение не поддерживается, то необходимо просто программно закрыть use-case, вместо того, чтобы пытаться глубоко анализировать код или пытаться найти root-cause в third-party компонентах.
  13. Отладка трансляцией кода — сложный алгоритм пишется или прототипируется на одном языке программирования (возможно медленном или интерпретируемом) с наличием всех доступных инструментальных средств (дебагера и т.п.), а потом исходный код отлаженного алгоритма транслируется в ручную или автоматически в другой язык программирования (целевой системы), для которого отсутствуют необходимые инструментальный средства. При таком подходе отладится можно на практически любом удобном для себя языке программирования, а потом заново странслировать программу на целевой язык программирования. Возможны и другие варианты, например,  дисассемблерование с целью более низкоуровневого понимания, что происходит при выполнении программы. Т.е. анализируется некий промежуточный вариант кода, который в некоторых ситуациях легче отладить или понять.
  14. Отладка разработкой интерпретатора — это не только метод отладки, но и паттерн проектирования. Этот метод используется, когда модуль требует частых изменений (из-за плавающих требований или поддержки большого количества фич, железок и т.п.), а время построения приложения очень большое. Для ускорения процесса и гибкости пишется небольшой интерпретатор кода с наличием управляющих конструкций if, циклов, goto. При наличии такого интерпретатора разработчик сравнительно не сложно создаёт скрипты, которые  можно быстрее исправить и отладить. Как упрощённый вариант такого способа отладки, например, использование дебажных флагов в коде, которые конфигурируют код и позволяют проверить разные варианты исполнения кода сделав лишь один build.

Существуют также неправильные методы отладки. Они обобщаются следующим pattern-ами:

  1. занимают слишком много времени;
  2. root-cause не обнаружен или никого не волнует в чём был root-cause;
  3. исправление ухудшает качество ПО, несмотря на то, что оригинальный дефект исправлен.

Однако, это тема для отдельных обсуждений.

это «unit тестирование»

Один комментарий

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

@ 2010- Кодубец Алексей Александрович
При копировании материалов обратная ссылка обязательна.