Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

DevDoc - статьи для разработчика ПО под Windows Отладка приложений на C++. Часть 2


DevDoc home page
 
   
 

Автор: Кудинов Александр
Последняя модификация: 2007-01-05 16:38:47

Отладка приложений на C++. Часть 2.

В Части 1 мы начали рассматривать методы пассивной отладки приложений. Были даны всего несколько технических приемов, которые позволят сильно облегчить жизнь программисту. Несмотря на их очевидность, они приносят реальную пользу, особенно в проектах, которые разрабатываются несколькими программистами.

Что можно вообще отнести к ошибкам? Каждый понимает под термином баг что то свое. И прежде чем искать этих загадочных «жучков» надо понять, как они выглядят. Я хочу предложить собственное определение бага: «это все, что не соответствует ожиданиям пользователя». Т.е. помимо написания стабильного кода в задачу команды разработчиков входит и правильное понимание желаний пользователей системы. Обычно я разбиваю ошибки на несколько категорий:

  • «падение» программы или разрушение данных
  • ошибки в пользовательском интерфейсе
  • не соответствие требованиям ТЗ
  • низкая производительность
  • «некрасивый» код.

Аварийное завершение программы или разрушение данных

Это, пожалуй, наиболее критический баг, который все пытаются ловить. В понимании начинающих программистов (и не только) это единственный тип ошибки, достойный внимания. Именно баги такого типа останавливают деятельность пользователя. Именно поэтому в основном мы будем рассматривать способы поиска и исправления багов этого типа.

«Не критические баги»

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

Пассивная отладка. Продолжение

А теперь, когда мы узнали что такое баг, вернемся к техническим приемам, которые позволяют искать ошибки, затрачивая минимум энергии.

Планирование. План отладки.

Обычно планирование связывают со временем. При отладке приложений план подразумевает перечень действий, которые надо выполнить. Во-первых, хорошо бы обзавестись планом тестирования. Это документ, который описывает что вообще предполагается проверять, и последовательность действий для проверки. Для небольших проектов нет нужды тратить силы на составления этого документа по всем правилам. Это может быть набросок на клочке бумаги или салфетке, которая подвернулась под руки. Я не рекомендую хранить такой план в памяти по той простой причине, что разработчик может понадеяться на авось: «я не менял этот кусок кода и поэтому он должен работать». Имея перед собой бумажку, на которой надо ставить галочки, после выполнения каждого теста, себя проще заставить двигаться в нужном направлении.

Помимо плана тестирования надо иметь представление, как и когда будет выполняться тестирование. Большинство людей задумываются о поисках ошибок, когда баги начинают лезть отовсюду. Я уже упоминал, что стоимость исправления бага растет по экспоненте с течением времени его жизни.

Часто баги возникают при добавлении новых функций в программу, либо при исправлении существующих ошибок. При внесении каждого изменения надо изучить, на что могут повлиять изменения и тщательно проверить работу этих участков кода.

Запомните! Никогда не вносите изменений, если вы не понимаете как это может повлиять на остальную часть программы.

Я настоятельно рекомендую планировать и думать о том, что и как вы будите отлаживать еще до того, как возникнет ошибка.

Думайте о том, как вы будите отлаживать код еще до того, как начнете его писать. Ваш код должен быть легким для отладки и тестирования.

Контроль ошибок и версий

Это напрямую не относиться к отладке как таковой, но позволяет получать более качественный продукт.

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

Контроль ошибок не менее важен. У бага также есть жизненный цикл:

  • Обнаружение
  • Воспроизведение
  • Исправление
  • Закрытие

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

Трассировка программы

Большинство средств разработки позволяют делать пошаговую отладку программы. Этим свойством очень часто пользуются при поиске конкретных багов. Я предлагаю еще один тривиальный и, тем не менее, полезный способ использовать трассировку.

После того как вы реализовали кусок кода, обязательно выполните его в пошаговом режиме, проверяя значения всех переменных.

Просто? Безусловно! Все эффективные решения, как правило, простые. Я был сильно удивлен, когда многие программисты пренебрегали этим методом. Это как минимум означает, что код может работать не так, как задумывалось и до поры до времени эта ошибка будет скрытой. В свою очередь это ведет к тому, что исправлять ее придется на более поздних стадиях и затратить на это гораздо больше усилий.

Не пренебрегайте такой простой вещью как пошаговое выполнение кода сразу после того, как он написан, чтобы убедиться что Вы не допустили ошибок.

Отладка релиза

Программа написана, протестирована и отдана заказчику. И через некоторое время вы получаете от клиента сообщение об ошибке. Не секрет, что конечный пользователь слабо представляет себе процесс тестирования и разработки ПО. Поэтому его описания часто бывают очень расплывчатыми. Не редко, когда пользователь просто говорит: «программа падает» и добиться более вразумительного описания просто невозможно. Об отладке на компьютере клиента часто невозможно, т.к. он может находиться на другом конце земного шара.

Поэтому еще на этапе разработки надо предусмотреть способы, которые вы будите использовать для поиска ошибок в таких ситуациях. Ниже я хочу поделиться некоторыми мыслями на этот счет.

Во-первых, надо обязательно собирать релиз с отладочными символами. Visual Studion .NET 2003 позволяет выполнять эту операцию. Символьная информация храниться в отдельном файле с расширением PDB. Ни в коем случае не отдавайте его клиенту, если конечно вы не хотите, облегчить ему задачу дизассемблирования вашего кода. Этот файл надо сохранять в архиве. Т.е. каждая версия программы, которая идет к пользователю, должна иметь такой файл в вашем архиве. Это незаменимый помощник, если надо найти место, в котором программа упала. Помимо этого вы должны сохранить в архиве все исходные коды, которые относятся к этой версии. Если вы не используете систему контроля версий для ваших исходных кодов и инсталляционных пакетов, самое время начать. Это сэкономит вам массу времени и нервов.

Для полноценного использования символьной информацией надо иметь возможность записи дампа памяти программы во время аварии. О том как это сделать я подробно расскажу в Части 3.

Следующий уровень – это регистрация процесса работы программы. Чем больше отладочных сведений генерирует программа, тем проще искать ошибки. Можно выводить все, вплоть до порядка вызова функций. Очевидно, что это весьма ресурсоемкий процесс, поэтому рекомендуется делать его в виде включаемой опции. Если у клиента возникает проблема, вы даете ему инструкцию как активировать эту функцию. После того как у клиента возникла ошибка, он просто отсылает вам отчет о выполнении программы. Дальше вы можете локализовать ошибку. Меня очень часто спасал именно этот прием, поэтому в большинство более менее крупных программ я встраиваю систему регистрации потока выполнения. Регистрация ошибок чем то похожа на вывод информации в окно отладчика с помощью макроса TRACE.

TRACE это еще один инструмент, который наряду с ASSERT помогает искать нештатные ситуации. Если вы не хотите прерывать программу во время выполнения (например, если программа умеет обрабатывать возникшую ошибку), можно вывести сообщение об ошибке с помощью TRACE. Тогда анализируя вывод в окне отладчика можно узнать, что в процессе работы что то пошло не так и на это стоит обратить свое внимание.

Замечание. Не забывайте добавлять символы \r\n в конце отладочной строки, чтобы вывод получался более читабельный.

HRESULT foo(IUnknown *p)
{
  if(!p)
  {
   TRACE("foo: Используется плохой аргумент.\r\n");
   return E_POINTER;
  }
}

Макрос TRACE не работает в релизе, поэтому его надо заменять своей системой регистрации. В следующей части, я приведу пример такой системы.

Модульное тестирование

Хорошей практикой является автоматическое модульное тестирование. Суть тестирование состоит в том, что пишется дополнительный код, который проверяет разные части вашей программы в автоматическом режиме. Этот код может быть также написан на С++ и быть частью вашей системы или же быть частью специальных утилит для проведения автоматического тестирования.

Метод тестирования прост: на вход некоторой функции подаются строго определенные данные, а потом результаты сравниваются с эталоном. Если результат не совпадает, значит это признак того, что в логике работы что то изменилось. Это не обязательно баг – возможно, что тест просто устарел.

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

Инсталляционный пакет

Изготовление программы для инсталляции надо начинать практически сразу после начала работ над программой. Чем раньше вы его сделаете, тем лучше. Инсталляционный пакет это лицо вашей программы, которое видит пользователь в первую очередь. Кроме того, он тоже должен быть хорошо отлажен и протестирован. Часто об этом забывают, и проблема появляется в последний момент, когда программа уже должна быть готова к сдаче.

Инсталляционный пакет позволяет команде тестировщиков работать с программой так же, как это будет делать пользователь.

Комментарии

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

Даже если код понятен для Вас на 100%, не поленитесь и напишите комментарий к нему. При написании комментариев нет нужды описывать очевидные вещи, главное указать ключевые точки программы, почему здесь сделано так, а не иначе. Надо найти разумный баланс между объемом комментариев и их полезностью. Запомните не надо писать больше, чем требуется, но и меньше тоже не стоит.

Тем, кто программирует не первый год хорошо известна ситуация, когда через несколько месяцев (или даже лет) возвращаешься к старому коду и задумчиво его разглядываешь, пытаясь понять тайный смысл написанного. А ведь всего несколько строк комментариев могут изменить ситуацию и помешать вам новыми исправлениями разрушить половину программы.

Я как минимум рекомендую писать комментарий к каждой функции: что она делает, какие параметры она принимает, возвращаемое значение и обязательно особые случае применения или ограничения на использование.

Также комментарии надо обязательно писать в ключевых точках, которые определяют логику работы программы в целом.

В процессе наблюдения за программистами я заметил, что грамотность и уместность комментариев тесно связана с квалификацией программиста.

Покрытие кода (Code coverage)

Это еще один полезный инструмент в битве за качество. Если вы не знаете что это такое, эта глава для Вас. Этот термин описывает процентное отношение строк кода которые были выполнены. Например, ваша программа состоит из 1000 строк, и в процессе теста вы выполнили 700. Это означает, что покрытие составляет 70%. Фактически, строки, которые не были выполнены это потенциальные места возникновения ошибок. Есть утилиты, которые подсчитывают % покрытия кода. Получение высокого процента покрытия долгая работа, зато, если вы получите цифру больше 90%, можете смело считать, что вы отыграли 60-70% битвы за качество.

Модульные тесты это хороший способ достичь высокого % покрытия и сосредоточить усилия на выполнении других тестов.

Продолжение следует….

Copyright (C) Kudinov Alexander, 2006-2007

Перепечатка материалов с данного сайта запрещена без писменного разрешения автора. При перепечатке обязательно указывать ссылку на оригинал.


В избранное