
Глава 5. Отладка программ
В этой главе вы познакомитесь с основными приемами отладки кода с помощью встроенного отладчика IDE. Это мощный инструмент, обладающий широкими возможностями вплоть до отладки на уровне машинного кода.
Программисты часто пренебрегают имеющимися в их распоряжении отладчиками и не используют их в полной мере, полагаясь на собственную сообразительность и пользуясь кустарными приемами отладки. Не берите с них пример, тем более что работать со встроенным отладчиком C++Builder очень просто, как вы сами скоро убедитесь. Следует также подчеркнуть, что отладчик является еще и прекрасным инструментом изучения языка, который позволяет производить наглядные эксперименты с различными языковыми конструкциями.
Предварительные шаги
Прежде всего, для исследования отладчика нам понадобится программа. В листинге 5.1 показана тестовая программа, вызывающая функции DoSomeMath () и DoSort () . Первая из них довольно бессмысленна и включена в программу только для того, чтобы продемонстрировать плавающую арифметику и соглашение _pascal. Вторая представляет собой вариант пузырьковой сортировки, частично реализованный на языке ассемблера.
При
создании нового модуля debugC.C автоматически
создается заготовка включаемого файла
debugC.h, причем C++Builder сразу вводит в него
директивы защиты от повторных включений,
о которых мы говорили в прошлой главе.
Кстати, главный исходный файл консольного
проекта называется в 5-й версии Debug. bpr,
а не Debug.срр
Листинг 5.1. Тексты программы Debug
/**********************************************
* * Debug.срр: Главный файл проекта.
*/
#pragma hdrstop
#include <condefs.h>
USEUNIT("debugC.с") ;
#define main
/**********************************************
* * debugC.h: Заголовок для модуля debugC.с.
*/
#ifndef debugCH
#define debugCH
double _pascal DoSomeMath(double r, double h);
void DoSort(int array[], int n) ;
*endif
/*******************************************
* * debugC.с: Программа для демонстрации отладчика.
*/
#pragma inline
#pragma hdrstop
#include <stdio.h>
#include "debugC.h"
const double Pi = 3.14159265;
#pragma argsused
int main(int argc, char *argv[])
{
double rad, vol;
int i, n = 8;
int iArr[8] = {-1, 23, 7, -16, 0, 11, 24, 3};
rad = 2.0;
vol = DoSomeMath(rad, 3.0);
printf("Volume = %10.6f\n", vol);
DoSort(iArr, n) ;
printf("Sorted array:");
for (i=0; i<n; i++)
printf("%6d", iArr[i]);
printf("\n") ;
return 0;
}
//----------------------------------------------
// Просто чтобы продемонстрировать вызов pascal.
//----------------------------------------------
double _pascal DoSomeMath(double r, double h)
{
double s;
s = Pi * r*r;
return s * h;
}
//------------------------------------------
// Сортировка с inline-ассемблером.
//-------------------------------------------
void DoSort(int array[], int n)
{
int i, j;
for (i = n-1; i > 0; i-)
for (j = 0; j < i; j++)
_asm {
push esi
mov ecx, j
mov eax, array
mov edx, [eax+ecx*4]
mov esi, [eax+ecx*4+0x04]
cmp edx, esi
jle skip
mov [eax+ecx*4], esi
mov [еах+есх*4+0хб4], edx
skip:
pop esi
}
} /* DoSort */
Прежде чем компилировать программу, нужно убедиться, что сделаны все необходимые установки проекта (диалог Project Options) и отладчика (диалог, вызываемый выбором Tools | Debugger Options... в главном меню).
Открыв уже известный вам диалог Project Options на странице Compiler, нажмите кнопку Full debug. Будут установлены все параметры компилятора и компоновщика, необходимые для отладки.
Диалог Debugger Options, показанный на рис. 5.1, имеет четыре страницы, из которых нам пока понадобится только одна — General. Рекомендую вам пометить на этой странице флажки Inspectors stay on top и Rearrange editor local menu on run — просто для удобства. При установленном втором флажке, например, контекстное меню редактора при запуске программы преобразуется таким образом, чтобы упростить доступ к пунктам управления отладкой.
Рис. 5.1 Страница General диалога Debugger Options
Если
вы хотите во время отладки иметь доступ
к исходным текстам библиотеки VCL, то
нужно установить флажок Use debug libraries на
странице Linker диалога Project Options. Правда,
компоновка отладочных библиотек может
значительно замедлить компиляцию,
поэтому не стоит прибегать к этому
средству без необходимости.
Теперь я предлагаю вам посмотреть на различные меню, имеющие отношение к отладке.
При отладке вам понадобится обращаться в основном к трем меню:
каскадное меню View | Debug Windows
меню Run
контекстное меню редактора кода.
Пункты этих меню для управления отладкой приведены ниже в таблицах 5.1 - 5.3.
Таблица 5.1. Пункты меню Viev | Debug Windows
Пункт |
Клавиша |
Описание |
Breakpoints |
Ctrl+Alt+B |
Открывает окно списка контрольных точек, показывающее активные контрольные точки и их свойства. |
Call Stack |
Ctrl+Alt+S |
Открывает окно стека вызовов. Стек показывает, какие и в каком порядке вызывались функции, прежде чем управление достигло текущей точки программы. |
Watches |
Ctrl+Alt+W |
Открывает окно наблюдения за переменными. Окно отображает список наблюдаемых переменных с их текущими значениями. |
Local Variables |
Ctrl+Alt+L |
Открывает окно локальных переменных. В нем отображаются значения всех локальных переменных текущей функции. |
Threads |
Ctrl+Alt+T |
Окно активных процессов и линий потока управления (threads). |
Modules |
Ctrl+Alt+M |
Окно загруженных модулей — исполняемых файлов, динамических библиотек и пакетов запущенного проекта. |
Event Log |
Ctrl+Alt+E |
Отображает протокол событий, происходящих при запуске проекта; какие события будут регистрироваться, можно задать на странице Event Log диалога Debugger Options. |
CPU |
Ctrl+Alt+C |
Открывает окно состояния процессора. Отображает, в частности, компилированный код программы и содержимое регистров. |
FPU |
Ctrl+Alt+F |
Открывает окно состояния FPU, отражающее содержимое его регистров и флагов. |
Таблица 5.2. Пункты меню Run
Пункт |
Клавиша |
Описание |
Run |
F9 |
Запускает программу, при необходимости производя перед этим её сборку (Make). |
Attach to Process... |
|
Прикрепляет отладчик к уже выполняющемуся в данный момент процессу. |
Parameters... |
|
Позволяет ввести аргументы командной строки или указать приложение, которое является “хозяином” отлаживаемой DLL. |
Step Over |
F8 |
Исполняет текущую строку исходного кода и переходит к следующей строке. |
Trace Into |
F7 |
Исполняет текущую строку исходного кода; если строка содержит вызов функции, переходит к трассировке последней. |
Trace to Next Source Line |
Shift+F7 |
Исполняет программу до следующей строки исходного кода. Например, если программа вызывает функцию API, требующую возвратно-вызываемой процедуры, отладчик остановит выполнение на входе в эту процедуру. |
Run to Cursor |
F4 |
Исполняет программу до строки исходного кода, в которой установлен курсор редактора. |
Run Until Return |
Shift+F8 |
Исполняет программу до возврата из текущей функции |
Show Execution Point |
|
Устанавливает курсор редактора кода на строку, в которой приостановлена программа. |
Program Pause |
|
Приостанавливает выполнение программы, как только управление попадает в наличный исходный код. |
Program Reset |
Ctrl+F2 |
Закрывает программу. |
Inspect... |
|
Открывает диалог Inspect, в котором можно ввести имя инспектируемого объекта. |
Evaluate/Modify... |
Ctrl+F7 |
Открывает диалог Evaluate/Modify |
Add Watch... |
Ctrl+F5 |
Открывает диалог Watch Properties |
Add Breakpoint |
|
Каскадное меню, позволяющее устанавливать контрольные точки различного вида (в исходном коде, на адресе, на данных, точки загрузки модуля). |
Для
любой из вышеперечисленных команд
'меню можно поместить на инструментальную
панель соответствующую кнопку. (Откройте
правой кнопкой мыши контекстное меню
инструментальной панели и выберите
Customize...; на странице Commands открывшегося
диалога выберите нужную кнопку и.
перетащите ее на инструментальную
панель. Чтобы убрать с панели какую-нибудь
кнопку, просто вытащите ее мышью за
пределы главного окна C++Builder.) По умолчанию
на панели инструментов размещены кнопки
Run, Pause, Trace Into и Step Over.
Следующая таблица показывает пункты контекстного меню редактора в режиме приостановленной программы. В основном они дублируют перечисленные пункты главного меню, но в ряде случаев более удобны.
Таблица 5.3. Отладочные пункты контекстного меню редактора
Пункт |
Клавиша |
Описание |
Toggle Breakpoint |
F5 |
Переключает (устанавливает или сбрасывает) контрольную точку в строке, где находится курсор редактора. |
Run to Cursor |
F4 |
То же, что и в меню Run. |
Goto Address... |
|
Позволяет указать адрес области памяти, которая будет отображаться в панели дизассемблера окна CPU. |
Inspect... |
Alt+F5 |
Открывает окно инспекции объекта, на имени которого находится курсор. |
Evaluate/Modify... |
|
То же, что и в меню Run. |
Add Watch at Cursor |
Ctrl+F5 |
Вносит в список наблюдения переменную, на имени которой находится курсор. |
View CPU |
|
То же, что Viev меню.| Debug Windows| CPU в главном |
Ну а теперь мы поговорим о том, чем управляют все эти меню и какие вообще инструментальные средства отладки имеются в распоряжении программиста.
Элементы отладки
Наиболее общими приемами отладки являются
установка контрольных точек
наблюдение за переменными
пошаговое исполнение кода.
Контрольные точки
Программа, запущенная под управлением отладчика IDE, исполняется как обычно, т. е. с полной скоростью, пока не будет встречена контрольная точка (breakpoint). Тогда отладчик приостанавливает программу, и вы можете исследовать и изменять содержимое переменных, исполнять операторы в пошаговом режиме и т. д.
Контрольные точки в C++Builder могут быть четырех видов: в исходном коде, на адресе, на данных и точки загрузки модуля.
Контрольные точки в исходном коде
Это самый распространенный вид контрольных точек. Точка представляет собой маркер, установленный на некоторой строке исходного кода. Когда управление достигает этой строки, программа приостанавливается.
Проще всего установить контрольную точку такого типа прямо в редакторе кода, щелкнув кнопкой мыши на пробельном поле редактора (слева от текста) рядом со строкой, на которой требуется приостановить программу. В пробельном поле появится красный маркер, и сама строка будет выделена красным цветом фона (рис. 5.2). Повторный щелчок кнопкой мыши удаляет контрольную точку.
Рис. 5.2 Установка контрольных точек
Если теперь запустить программу кнопкой Run, она будет остановлена на контрольной точке (рис. 5.3).
Рис. 5.3 Остановка программы на контрольной точке
Зеленая пометка на маркере контрольной точки означает, что точка проверена и признана действительной. Могут быть и недействительные контрольные точки — такие, что установлены на строках, не генерирующих исполняемого кода. Это могут быть комментарии, объявления, пустые строки или операторы, исключенные при оптимизации программы (!!!).
Текущая точка исполнения показана в пробельном поле зеленой стрелкой. Она указывает строку, которая должна исполняться следующей.
Программу можно продолжить кнопкой Run или выполнять ее операторы в пошаговом режиме, о чем будет сказано ниже.
То, что мы сейчас показали — это простые контрольные точки в исходном коде; контрольные точки могут быть также условными, со счетчиком проходов или комбинированного типа.
Если вы в данный момент экспериментируете с отладчиком, откройте окно списка контрольных точек (View Debug Windows Breakpoints). Оно отображает все имеющиеся контрольные точки. Контекстное меню окна позволяет запретить остановку программы на контрольной точки, не удаляя ее (пункт Enable). Кроме того, выбрав пункт Properties..., вы получите доступ к свойствам выбранной точки (рис. 5.4 и 5.5).
Рис. 5.4 Окно Breakpoint List
Рис. 5.5 Диалог Source Breakpoint
В поле Condition диалога Source Breakpoint Properties можно задать условие остановки на контрольной точке. Условие может быть любым допустимым выражением языка C/C++, которое можно оценить как истинное или ложное. Остановка по достижении контрольной точки будет происходить только в том случае, если условие истинно.
Контрольные точки со счетчиком проходов можно считать разновидностью условных. Требуемое число проходов вводится в поле Pass count. Если число проходов установлено равным п, остановка программы произойдет только на п-ом проходе через контрольную точку. Точки со счетчиком удобны при отладке циклов, когда вам нужно выполнить тело цикла определенное число раз и только потом перейти к пошаговому выполнению программы.
Счетчик
может быть очень полезен, когда вам
нужно определить, на каком проходе цикла
возникает ошибка, вызывающая завершение
программы. В окне списка
контрольных точек отображается не
только заданное, но и текущее число
проходов точки (например, “7 of 16”). Задав
число проходов, равное или большее
максимальному числу итераций цикла, вы
при завершении программы сразу увидите,
сколько раз на самом деле он выполнялся.
Возможна комбинация этих двух типов контрольных точек, которую можно назвать точкой с условным счетчиком. Если для контрольной точки задано и условие, и число проходов, то остановка произойдет только на п-ом “истинном” проходе через нее. Проходы, для которых условие оказывается ложным, “не считаются”.
Условия
и счетчик можно задавать для всех видов
контрольных точек кроме точек загрузки
модуля, т. е. для исходных, .адресных и
точек данных.