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

       

Отладчики режима ядра


Отладчики режима ядра находятся между CPU и операционной системой. Это означает, что, когда вы останавливаете отладчик режима ядра, операционная система также полностью останавливается. Нетрудно сообразить, что переход операционной системы к резкому останову полезен, когда вы работаете с таймером и над проблемами синхронизации. Все-таки, за исключением одного отладчика, о котором будет рассказано ниже (в разделе "Отладчик SoftlCE" данной главы), нельзя отлаживать код пользовательского режима с помощью отладчиков режима ядра.

Отладчиков режима ядра не так много. Вот некоторые из них: Windows 80386 Debugger (WDEB386), Kernel Debugger (1386KD), WinDBG и SoftlCE. Каждый из этих отладчиков кратко описан в следующих разделах.

Отладчик WDEB386

WDEB386 — это отладчик режима ядра Windows 98, распространяемый в составе Platform SDK. Этот отладчик полезен только для разработчиков, пишущих драйверы виртуальных устройств Windows 98 (VxD). Подобно большинству отладчиков режима ядра для операционных систем Windows, отладчик WDEB386 требует для работы две машины и нуль-модемный кабель. Две машины необходимы потому, что часть отладчика, которая выполняется на целевой машине, имеет ограниченный доступ к ее аппаратным средствам, так что он посылает свой вывод и получает команды от другой машины.

Отладчик WDEB386 имеет интересную историю. Он начинался как внутренний фоновый инструмент Microsoft в эпоху Windows 3.0. Его было трудно использовать, и он не имел достаточной поддержки для отладки исходного кода и других приятных свойств, которыми нас испортили отладчики Visual C++ и Visual Basic.

"Точечные" (DOT) команды — наиболее важная особенность WDEB386. Через прерывание INT 41 можно расширять WDEB386 с целью добавления команд. Эта расширяемость позволяет авторам VxD-драйверов создавать заказные отладочные команды, которые дают им свободный доступ к информации в их виртуальных устройствах. Отладочная версия Windows 98 поддерживает множество DOT-команд, которые позволяют наблюдать точное состояние операционной системы в любой точке процесса отладки.


Отладчик I386KD

Windows 2000 отличается от Windows 98 тем, что реально действующая часть отладчика режима ядра является частью NTOSKRNL.EXE — файла главного ядра операционной системы Windows 2000. Этот отладчик доступен как в свободных (выпускных), так и в проверенных (отладочных) конфигурациях операционной системы. Чтобы включить отладку в режиме ядра, установите параметр загрузчика /DEBUG в BOOT.INI и, дополнительно, опцию загрузчика /DEBUGPORT, если необходимо установить значение коммуникационного порта отладчика режима ядра, отличающееся от умалчиваемого (СОМ1). I386KD выполняется на своей собственной машине и сообщается с машиной Windows 2000 через кабель нуль-модема.

Отладчик режима ядра NTOSKRNL.EXE делает только то, что достаточно для управления CPU, так чтобы операционная система могла быть отлажена. Большая часть отладочной работы — обработка символов, расширенные точки прерывания и дизассемблирование — выполняется на стороне 1386KD. Одно время Windows NT 4 Device Driver Kit (DDK) документировал протокол, используемый в кабеле нуль-модема. Однако Microsoft больше его не документирует.

Мощь 1386KD очевидна, если посмотреть на все команды, которые он предлагает для доступа к внутреннему состоянию Windows 2000. Знание механизма работы драйверов устройств в Windows 2000 поможет программисту следить за выводом многих команд. Не смотря на всю свою мощь, i386KD почти никогда не применяется, потому что это консольное приложение, которое очень утомительно использовать для отладок исходного уровня.

Отладчик Win DBG

WinDBG — это отладчик, который поставляется в составе Platform SDK. Можно также загрузить его с http://msdn.microsoft.com/developer/sdVdebidt.asp. Это гибридный отладчик, который может работать как в режиме ядра, так и в режиме пользователя, но WinDBG не позволяет отлаживать программы пользовательского режима и режима ядра одновременно. Для отладки в режиме ядра WinDBG предоставляет всю мощь отладчика i386KD, но с более легким в использовании внешним GUI-интерфейсом, который значительно упрощает отладку на уровне исходного кода.


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

Как отладчик пользовательского режима WinDBG великолепен, и я настоятельно рекомендую установить его, если вы этого еще не сделали. WinDBG — более мощный отладчик, по сравнению с отладчиком Visual C++, в том отношении, что он показывает намного больше информации в процессе отладки. Однако за эту мощь надо платить: WinDBG труднее использовать, чем отладчик Visual C++. Тем не менее я посоветовал бы потратить время на изучение WinDBG. Это может окупиться намного более быстрым исправлением ошибок, чем с помощью отладчика Visual C++. Лично я провожу, в среднем, приблизительно 70% времени в отладчике Visual C++, а остальное — в WinDBG.

При первом запуске WinDBG можно заметить, что у него имеется специальное окно команд (Command). Подобно отладчику Visual C++, WinDBG выполняет отладку на уровне исходного кода. Однако реальная мощь WinDBG проявляется в интерфейсе окна его команд. Почувствовав удобство различных команд, вы вскоре обнаружите, что с помощью окна Command можно выполнять отладку намного быстрее, чем с помощью только GUI-интерфейса.

Возможности окна команд WinDBG возрастают, если добавить в него свои собственные команды, называемые WinDBG-расширениями. В то время как отладчик Visual C++ не очень гибок при остановке отлаживаемого процесса, в WinDBG имеется полный набор API-функций, позволяющих использовать все функциональные возможности отладчика, включая дизассемблер, символьную машину и машину трассировки стека. Дополнительную информацию о WinDBG-расширениях можно найти в разделе "Debugger Extension" (Расширение отладчика) на MSDN.

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



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

В дополнение к лучшей расширяемости, чем у отладчика Visual C++, WinDBG имеет еще одно свойство, которое обязательно нужно рассмотреть, если приложение выполняется в Windows 2000 или Windows NT 4: WinDBG может читать файлы дампов пользовательского режима, созданные программой Dr. Watson. Это означает, что можно загружать в отладчик точное состояние программы во время аварийного прерывания, подробно его просмотреть и проанализировать. Дополнительную информацию о том, что нужно для установки этого свойства, можно найти в колонках "Bugslayer" журнала "Microsoft Systems Journal" ж декабрь 1999 и январь 2000.

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

Отладчик SoftICE

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

На первый взгляд, нас мало должен беспокоить тот факт, что SoftICE может привести к остановке операционной системы.


Но зададим такой вопрос: " Что случится, если нужно отладить код, работающий с таймером?" Если вы используете такую API-функцию, как SendMessageTimeout, то можете легко попасть в ситуацию тайм-аута, когда выполняете пошаговый проход другого потока с помощью типового GUI-отладчика. Работая с SoftICE, можно "шагать" везде где угодно, потому что таймер, с которым имеет дело SendMessageTimeout, не будет выполняться, пока вы работаете под SoftICE. SoftICE — единственный отладчик, который позволяет эффективно отлаживать многопоточные приложения. Сам факт, что SoftICE останавливает всю операционную систему, когда он активен, означает, что решение таймерных проблем становится гораздо более легким.

Другим преимуществом размещения SoftICE между CPU и операционной системой является то, что отладка межпроцессных взаимодействий становится очень легкой. Если вы занимаетесь СОМ-программированием с множеством внепроцессных служб, то можете легко устанавливать точки прерывания во всех процессах и выполнять пошаговые переходы между ними. Наконец, если нужно переходить от режима пользователя в режим ядра или обратно, то SoftICE делает такие переключения тривиально.

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

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


Хотя в отладчиках 1386KD и WinDBG довольно много таких команд, в SoftICE их намного больше. В SoftICE можно просматривать почти все — от состояния всех событий синхронизации до полной HWND-информации и расширенной информации о любом потоке в системе.

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

Общий вопрос отладки

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

В Windows 2000 сведения о том, что нужно вызывать для отладки приложения, завершившегося аварийно, содержатся под ключом реестра

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion \AeDebug

В Windows 98 эта информация "прописана" в секции [AeDebug] файла WIN.INI. Если под указанным ключом (или в секции) нет никаких параметров, Windows 2000 сообщает только адрес аварийного сбоя. Если сбой произошел из-за нарушения доступа, Windows 2000 сообщает также адрес той области

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

Под указанным ключом (Windows 2000) или в секции [AeDebug] (Windows 98) возможно размещение трех строчных параметров:

• Auto

• Debugger

• UserDebuggerHotKey

Если значение параметра Auto установить в 0, то операционная система будет генерировать стандартную диалоговую панель аварийного сбоя и активизирует в ней кнопку Cancel (Windows 2000) или Debug (Windows 98) на тот случай, если вы сами захотите присоединить отладчик. Если параметр Auto установить в 1, то отладчик стартует автоматически. Параметр Debugger указывает отладчик, который операционная система запустит на сбойном приложении.


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

Раздел реестра AeDebug можно устанавливать вручную, но только утилита Dr. Watson (ее версия в Windows 2000), WinDBG и отладчик Visual C++ позволяют устанавливать в нем различные значения параметров. Dr. Watson и WinDBG используют ключ командной строки -I, который определяет их в качестве отладчика по умолчанию. Чтобы установить отладчик Visual C++ в качестве отладчика, который будет вызывать операционная система, включите флажок Just-In-Time Debugging на вкладке Debug диалогового окна Options (которое открывает команда ToolsjOptions... в IDE Microsoft Visual C++).

Если заглянуть в раздел реестра AeDebug, то можно увидеть, что значение, которое введено для параметра Debugger, выглядит точно так же, как строка, передаваемая в API-функцию wsprintf:

drwtsn32 -p %d -e %d -g

где -p — идентификатор (ID) процесса аварийного завершения, а -е — значение дескриптора события, с помощью которого отладчик должен просигналить, когда его цикл отладки получит первое отладочное событие выхода из потока. Сигнал дескриптора события сообщает операционной системе, что отладчик присоединен успешно.



Содержание раздела