Сообщений 1    Оценка 2        Оценить  
Система Orphus

Отладка приложений

Автор: Александр Шаргин
Источник: RSDN Magazine #0
Опубликовано: 27.01.2002
Исправлено: 13.03.2005
Версия текста: 1.0
Отладочные и финальные версии
Отладка приложений, запускаемых другими приложениями
Ручное подключение отладчика к процессу
Использование DebugBreak
Автоматическое подключение отладчика к процессу
Что делать дальше
Отладка сервисов
Упрощенный вариант отладки
Отладка кода запуска
Режим Allow Service To Interact With Desktop
Отладка локальных COM-серверов

В этом разделе мы перейдём от теории к практике и рассмотрим некоторые аспекты отладки приложений.

Отладочные и финальные версии

Одной из самых коварных ошибок, которые подстерегают разработчика на Visual C++, является ошибка, которая возникает только в финальной версии приложения (release version), когда оно, казалось бы, уже полностью отлажено. В ряде статей подробно рассматриваются возможные причины этого явления (рекомендую прочитать перевод статьи Джозефа Ньюкамера "Как пережить release-версию" (http://rsdn.ru/article/?vcpp/survrls.xml), посвящённой этому вопросу). Мы же поговорим о том, что нужно делать, чтобы найти и исправить ошибку.

Начинать поиск ошибки следует с отладочной версии программы. В первую очередь внимательно просмотрите предупреждения, которые выдаёт компилятор. Возможно, одно из них указывает на причину проблемы. Лучше всего переключить компилятор в режим предупреждений четвертого уровня (4 level warnings). При этом действительно нежелательные предупреждения (в том числе предупреждения в стандартных заголовочных файлах Visual C++) можно отключить директивой препроцессора #pragma warning. Желательно полностью избавиться от предупреждений, прежде чем двигаться дальше. Кроме этого, просмотрите содержимое окна Debug после завершения программы. Возможно, вы обнаружите диагностические сообщения о записи в уже освобождённый блок памяти, о выходе за границы динамического массива или о других подобных ошибках. Если в настройках проекта не указан ключ /GZ, добавьте его (для этого нужно раскрыть окно Project->Settings, перейти на закладку C/C++ в категорию General и вручную вписать ключ в окно Project Options). Этот ключ позволяет найти в отладочной версии некоторые ошибки, характерные для финальной версии. Для этой цели компилятор включает в программу дополнительные проверки:

HMODULE hKern = GetModuleHandle(_T("kernel32.dll"));

// "забыли" __stdcall
DWORD (/*__stdcall*/ *pRegisterServiceProcess)(DWORD, DWORD);

(FARPROC&)pRegisterServiceProcess =
    GetProcAddress(hKern, "RegisterServiceProcess");

pRegisterServiceProcess(NULL, 1); // некорректный вызов!

Если ошибка до сих пор не найдена, придётся перейти непосредственно к отладке финальной версии. Прежде всего, существует очень небольшая вероятность того, что ошибку в программу внёс оптимизатор Visual C++. Чтобы выяснить этот вопрос, отключите оптимизацию. Для этого нужно раскрыть окно Project->Settings, перейти на закладку C/C++ в категорию General и задать Optimizations: Disable (Debug). Если ошибка исчезла, оптимизацию придётся включить снова, чтобы найти проблемное место. Если же нет, не включайте её, так как это упростит пошаговую отладку (не будет сюрпризов вроде выкинутого компилятором из программы ненужного, по его мнению, кода).

Далее следует включить генерацию отладочных символов для финальной версии, чтобы отладчик мог показывать в процессе отладки исходный код программы, а не её ассемблерные листинги. Для этого нужно в очередной раз раскрыть окно Project->Settings, перейти на закладку C/C++ в категорию General и установить режим Debug info: Program Database for Edit and Continue. А на закладке Link в категории General следует установить флажок Generate Debug Information.

Некоторые считают, что после отключения оптимизации и генерации отладочных символов финальная версия ничем не отличается от отладочной. Это не так. Во-первых, отладочная версия программы линкуется с отладочной версией библиотеки языка C, а финальная версия – с обычной. Во-вторых, в финальной версии не определяется макрос _DEBUG, что приводит к удалению из программы всех диагностических проверок. Есть и другие, менее заметные отличия. Например, в финальной версии не происходит инициализации переменных и областей памяти специальными отладочными значениями, типа 0xCD, 0xCC. Данная помощь компилятора выливается в то, что многие проверки указателей переменных, дающие стабильный результат в отладочной версии, приводят к нестабильностям в финальной версии.

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

ПРИМЕЧАНИЕ

В большинстве случаев ошибки, внесённые оптимизатором, являются следствием ошибок в вашей программе. Подробнее об этом можно прочитать в уже упомянутой мною статье "Как пережить release-версию".

Отладка приложений, запускаемых другими приложениями

Если приложение, которое необходимо отладить, запускается из другого приложения (в частности, это относится к локальным COM-серверам и сервисам Windows NT), задача несколько усложняется. Необходимо подключить отладчик к отлаживаемому процессу до того, как в нём выполнится ошибочный код. Для этого можно использовать следующие подходы.

Ручное подключение отладчика к процессу

Вы уже знаете, что подключить отладчик к уже запущенному процессу можно двумя способами: из Task Manager'а или посредством команды Build->Start Debug->Attach to Process.

ПРИМЕЧАНИЕ

Если вы используете команду Attach to Process для присоединения к сервису, не забудьте установить галочку Show System Processes, чтобы увидеть его в списке процессов. Для отладки сервисов вам необходимы права администратора (точнее, учетная запись должна обладать привилегией SeDebugPrivilege).

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

Использование DebugBreak

Функцию DebugBreak можно использовать, чтобы прервать программу в самом начале её работы, например:

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
    DebugBreak();
    
    ...
}

Вызов функции DebugBreak эквивалентен срабатыванию точки останова. Если программа выполняется без отладчика, точка останова ничем не отличается от любого другого исключения. Поэтому система приостановит программу и выдаст знакомое окно Application Error (рис. 10).


Рисунок 10. Исключение типа Breakpoint

Нажимайте Cancel (режим Just-in-time debugging в Visual C++ должен быть включён!), и отладчик будет присоединён к программе в самом начале её выполнения. Этот способ можно использовать на всех платформах, но он недостаточно надёжен (исключение может быть кем-то перехвачено, и окно Application Error никогда не появится). Так, например, COM+ перехватывает это исключение и возвращает клиенту ошибку в виде HRESULT.

Автоматическое подключение отладчика к процессу

В Windows NT4 и выше можно заставить систему присоединять отладчик к некоторому процессу всякий раз, когда он запускается. Для этого запустите редактор реестра, раскройте ключ HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options и добавьте в него подключ, имя которого совпадает с именем exe-файла вашего приложения (без пути!). Затем создайте в этом подключе строковый параметр Debugger и запишите в него полный путь к отладчику Visual C++ (этот путь можно позаимствовать из свойств ярлыка Microsoft Visual C++ 6.0).

С параметром Debugger связано одно неприятное ограничение: длина пути к отладчику не должна превышать 64 символа. Путь к каталогу, в который Visual C++ ставится по умолчанию, обычно бывает длиннее. В этом случае нужно найти способ записать путь в более компактной форме – например, сделать каталог с Visual C++ разделяемым (shared) и задать в параметре Debugger его сетевое имя.

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

Что делать дальше

После того, как отладчик присоединён к приложению, порядок действий может быть примерно таким.

ПРИМЕЧАНИЕ

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

После этого сеанс отладки будет протекать, как обычно.

Отладка сервисов

DВсе сервисы Windows NT запускает Диспетчер управления службами (Service Control Manager, SCM). Присоединить отладчик к процессу сервиса можно как в момент его запуска, так и позже. Для этого можно использовать методы, изложенные в предыдущем разделе. Тем не менее, сервисы имеют несколько особенностей, о которых следует упомянуть.

Упрощенный вариант отладки

Так как любой сервис является обычным приложением Windows, в большинстве случаев его можно запустить как любой другой исполняемый файл и отлаживать в обыкновенном режиме. Для сервисов, созданных с помощью ATL Wizard, генерируется код, который позволяет зарегистрировать этот сервис не только в качестве сервиса, но и в качестве обыкновенного локального COM-сервера. При этом становятся доступными стандартные методы отладки EXE-приложений. Такой режим не всегда подходит, так как приложение, работающее в режиме сервиса, имеет свои особенности.

Отладка кода запуска

В Windows 2000 сервису даётся 30 секунд с момента запуска, чтобы выполнить инициализацию и вызвать функцию StartServiceCtrlDispatcher. Если он этого не сделает, SCM принудительно завершит весь процесс. Поэтому сеанс отладки кода запуска сервиса также может продолжаться не более 30 секунд. Если за это время выявить проблему в отладчике не удаётся, придётся использовать "доисторические методы", связанные с выводом массы диагностической информации в процессе работы программы и последующим анализом полученных данных.

Режим Allow Service To Interact With Desktop

В процессе отладки сервиса (как и любого другого приложения) удобно использовать отладочные макросы (такие, как _ASSERT) для проверки различных условий по ходу программы. Чтобы сообщения от этих макросов могли появляться на экране, необходимо открыть свойства сервиса, перейти на закладку Log On, убедиться, что сервис запускается под учетной записью SYSTEM, и установить флажок Allow Service To Interact With Desktop. Когда сервис полностью отлажен, этот флажок можно будет отключить.

Отладка локальных COM-серверов

При отладке локальных COM-серверов удобно использовать режим OLE RPC debugging. В этом режиме можно "заходить" в отладчике прямо в методы COM-сервера (для этого "на лету" запускается второй экземпляр отладчика и подключается к серверу). Режим OLE RPC debugging включается в окне Tools->Options (закладка Debug). Чтобы он стал доступен, необходимо предварительно включить режим Just-in-time debugging.


Эта статья опубликована в журнале RSDN Magazine #0. Информацию о журнале можно найти здесь
    Сообщений 1    Оценка 2        Оценить