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

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


DevDoc home page
 
   
 

Анонс

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

Присылайте пожалуйста свои пожелания о том, на какие темы Вы бы хотели видеть статьи на адрес: sub12@devdoc.ru. В конце концов, я работаю для Вас, и буду стараться следовать за интересами подписчиков. Конструктивная критика тоже приветствуется.

Все статьи этой рассылки Вы можете найти на сайте http://www.devdoc.ru. Он находится в постоянном развитии. Движок все еще находится в стадии разработки – пробуйте и присылайте ваши замечания.

Если у Вас есть материал, интересные библиотеки или компоненты Вы можете опубликовать их на нашем сайте. Инструкция и правила находятся по адресу: http://www.devdoc.ru/index.php/content/view/publish.html.


Автор: Кудинов Александр
Последняя модификация: 2007-01-26 18:14:56

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

Введение

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

Точки останова

Прежде чем описывать способы установки точек останова всех типов я хочу подробнее остановиться на формате отображения информации о точках останова в окне отладчика Breakpoints. Окно с точками останова можно отобразить через меню Debug->Window->Breakpoints. Это окно может иметь следующие поля:

  • Name. Имя точки останова создается отладчиком автоматически. Галочка рядом с именем позволяет разрешать или запрещать точку останова. Это очень удобно. Например, в программе может быть множество точек останова, а вас интересует только несколько из них. Причем остальные тоже должны быть сохранены для будущего использования.
  • Condition. Опциональный параметр, который определяет, когда отладчик должен останавливаться, а когда нет. Этот параметр будет рассмотрен ниже.
  • Hit Count. Еще одни опциональный параметр. Если он не задан, отладчик будет останавливаться на точке каждый раз, если удовлетворено условие в поле Condition. Подробности и примеры использования ниже.
  • Language. Название языка программирования, для которого задана точка останова. В нашем случае это всегда C++.
  • Function. Имя функции, в которой установлена точка останова и положение точки внутри функции.
  • File. Имя файла, в котором задана точка останова.
  • Address. Адрес в памяти, для которого задана точка останова.
  • Data. Для точки останова на модификацию данных поле содержит имя переменной и контекст, для которого задана точка останова.
  • Program. Имя программы, в которой находится точка останова.

Набор полей пользователь может выбрать самостоятельно.

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

Окно добавления точки останова вызывается Debug->New breakpoint:


Каждая закладка предназначена для установки точки останова определенного типа. Параметры Hit Count и Condition общие для всех точек останова, поэтому начнем с них.

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

Условия могут быть:

  1. Break always, regardless of hit count. Выполнение прерывается всегда. Счетчик игнорируется.
  2. Break when the hit count is equal to the counter. Выполнение прерывается, когда счетчик равен количеству срабатываний точки останова.
  3. Break when the hit count is a multiple of the counter. Выполнение прерывается, когда количество срабатываний на точке останова кратно счетчику.
  4. Break when the hit count is greater than or equal to the counter. Выполнение прерывается, когда количество срабатываний точки останова больше или равно счетчику.

Параметр «количество срабатываний» (Current Hit Count) увеличивается на единицу автоматически каждый раз, когда выполнение достигло точки останова. Для новых точек останова оно равно 0. Для уже существующих точек это значение можно посмотреть в свойствах точки останова. Его можно принудительно сбросить на ноль.

Рассмотрим пример использования этого механизма:

void revstr(char *pStr)
{
  ASSERT(pStr);   //Проверяем, что указатель не нулевой.
  char *pEnd = pStr + strlen(pStr) - 1; //Указатель на конец строки
  while(pStr < pEnd)
  {
   swap(*pStr, *pEnd);   //меняем символы местами.
   PostProcess(*pStr);
   pStr++;
   pEnd--;
  }
}

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

  • Предположим, что в какой-то момент она «падает» и мы хотим найти номер итерации, на которой это происходит. Это можно сделать, если поставить обычную точку останова с любым условием, которое заведомо не будет выполнено. Т.е. отладчик не будет прерывать выполнение программы вплоть до момента, когда функция «упадет». Например, это может быть Break when the hit count is equal to the counter с очень большим счетчиком. Тогда в момент падения можно заглянуть в свойства точки останова и увидеть текущий счетчик срабатываний. Это и будет номер итерации.
  • Часто ошибки возникают на границах диапазона обработки (или значений). Если мы знаем длину строки, мы можем настроить точку останова так, что выполнение прервется на последней итерации. Для этого достаточно посмотреть, что возвращает функция strlen и настроить соответственно параметры точки останова.
  • Если строка имеет чередующуюся структуру, то нам может быть интересна остановка каждую N-ную итерацию. В этом случае можно использовать Break when the hit count is a multiple of the counter.

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

Теперь рассмотрим, как нам может помочь параметр Condition. Он служит той же цели, что и Hit Count, только момент остановки исполнения программы определяется на основании выражения, которое рассчитывается в момент срабатывания точки останова. Параметр Condition имеет приоритет по сравнению с Hit Count. Выражение может использоваться двумя способами:

  • Выполнение прерывается, если выражение Истина (true)
  • Если результат вычисления выражения изменился по сравнению с предыдущим срабатыванием точки останова.

MS Visual Studio имеет общий анализатор выражений, который используется в окнах Quick Watch, Watch, Autos, Local и This. Он же используется для расчета выражений для точек останова. Подробное описание анализатора выражений можно найти в документации на Visual Studio. Здесь я опишу только общие моменты. Анализатор обрабатывает большинство выражений, которые записаны с использованием правил ANSI C/C++ и расширений от Microsoft. Он не поддерживает операторы: , (запятая) и ?:

На выражения дополнительно накладываются следующие ограничения:

  • Выражение не может содержать вызовов функций
  • Приведение типов ограничено одним уровнем косвенности. Вы можете посмотреть (char*)p, но не (char **)sym или char far *( far *).
  • Если вы выполняете приведение типов, то тип должен быть известен отладчику. Т.е. ваша программа должна содержать как минимум один объект такого типа. Типы, созданные с помощью typedef, не поддерживаются.
  • Оператор области видимости (::) имеет более низкий приоритет, чем в коде на чистом C++.

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

Существуют также правила, по которым используются переопределенные операторы, функции преобразования, конструкторы и т.п. Подробности смотрите в документации на Visual Studio.

Дополнительно к указанным операциям анализатор выражений распознает оператор контекста, о котором я скажу ниже, в разделе о точках останова на данных. Также можно использовать операторы доступа к памяти (BY, WO, и DW), которые полезны при отладке кода в окне дизассемблера.

Возвращаясь к условным выражениям в точках останова, хочется в качестве примера использовать уже приведенный код. Если нам, как и раньше, хочется посмотреть значения переменных в последней итерации цикла, мы можем использовать условную точку останова. Выражение может быть следующим: pStr >= pEnd с прерыванием выполнения, когда выражение будет Истина (true).

Теперь рассмотрим все типы точек останова, которые предоставляются отладчиком. Для установки точки каждого типа надо воспользоваться одноименной закладкой в окне New breakpoint, которое было описано выше.

Точка останова на функции

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

Function, New Breakpoint Dialog Box

Если вы хотите поставить точку останова на метод класса, надо использовать оператор разрешения видимости, например: class_name::function_name.

Номер строки, на которой будет точка останова, отсчитывается от начала функции. Если номер равен 1 - это эквивалентно тому, что точка останова ставится на начало функции.

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

Точка останова на строке файла.

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

File, New Breakpoint Dialog Box

Позиция точки останова отсчитывается от начала файла.

Точка останова на адресе.

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

Address, New Breakpoint Dialog Box

Точка останова на переменной, данных.

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

Data, New Breakpoint Dialog Box

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

Для установки точки такого типа надо указать имя переменной и контекст, если переменная находится за пределами текущей области видимости. Если переменная представляет собой массив, то необходимо указать количество элементов для отслеживания всех изменений.

Оператор контекста может использоваться, чтобы определить область видимости переменных, выражений или местоположение точки останова. Если помните, он используется синтаксическим анализатором выражений в Visual Studio. Оператор контекста имеет следующий формат:

  • {[функция],[файл],[модуль] } номер_строки
  • {[функция],[файл],[ модуль] } имя_переменной
  • {[функция],[файл],[ модуль] } выражение

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

С точки зрения отладчика, контекст - это недвусмысленное указание, где находится переменная. Контекст состоит из функции, файла с исходниками, двоичного модуля.

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

На этом я хочу закончить описание точек останова.

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

Copyright (C) Kudinov Alexander, 2006-2007

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


В избранное