Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Роббинс Д. - Отладка приложений для Microsoft .NET и Microsoft Windows - 2004

.pdf
Скачиваний:
353
Добавлен:
13.08.2013
Размер:
3.3 Mб
Скачать

ГЛАВА 19 Утилита Smooth Working Set

683

 

 

Если счетчик вызовов для этой записи не равен 0.

{

Если размер этой записи > размер наилучшей записи по числу вызовов.

{

Обозначить эту запись как наилучшее соответствие по числу вызовов.

}

}

}

}

}

Если наилучшее соответствие по числу вызовов не найдено.

{

Считать наилучшей записью по числу вызовов наилучшую запись в целом.

}

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

страницы.

}

}

Закрыть все временные файлы

}

Что после SWS?

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

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

Реализуйте упомянутые мной возможности исключения символов, чтобы их число в ваших SWS файлах было минимальным.

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

684 ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода

Резюме

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

Ч А С Т Ь V

ПРИЛОЖЕНИЯ

П Р И Л О Ж Е Н И Е

A

Чтение журналов Dr. Watson

Я надеюсь, что для облегчения отладки приложений вы будете включать в них возможность создания минидампов (см. главу 13) и вам не придется изучать жур налы Dr. Watson. Однако, если у вас есть готовое приложение или ваши клиенты не могут высылать вам двоичные минидампы по электронной почте, Dr. Watson всегда сможет указать вам на время и место возникновения проблемы.

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

На следующих страницах я рассмотрю полный журнал Dr. Watson, объясняя по ходу дела всю важную информацию (релевантные данные в конкретном раз деле выделены полужирным начертанием). Этот журнал был создан в результате одной из ошибок ранней версии WDBG.EXE — отладчика, написанного мной для главы 4.

После знакомства с этой книгой ничто в журнале Dr. Watson не должно быть для вас незнакомым. Что до различий между журналами Dr. Watson в Microsoft Windows 2000, Windows XP и Windows Server 2003, то их немного. Однако, как вы увидите ниже, версии Dr. Watson в двух последних ОС несколько лучше.

Для получения журналов запустите Dr. Watson (DRWTSN32.EXE). В списке Appli cation Errors (ошибки приложения) вы увидите недавние ошибки. Если этот спи сок пуст, возможно, Dr. Watson не задан в качестве отладчика по умолчанию. Что бы сделать Dr. Watson отладчиком по умолчанию, запустите его с ключом –i, т. е. введите выражение drwtsn32 –i. Для генерирования тестовой ошибки запустите

ПРИЛОЖЕНИE A Чтение журналов Dr. Watson

687

 

 

программу CrashTest.EXE из главы 13 и нажмите кнопку Crash Away (сгенериро вать ошибку). Пользовательский интерфейс Dr. Watson изображен на рис. A 1.

Рис. A 1. Пользовательский интерфейс Dr. Watson

Выберите в списке Application Errors интересующую вас ошибку и нажмите кнопку View (показать), после чего появится диалоговое окно Log File Viewer (про смотр журнала) (рис. A 2). В Windows 2000 в окне Application Errors вы увидите только номера ошибок приложений и их адреса. В Windows XP/Server 2003 вы увидите еще и имя процесса. Скопируйте из окна Log File Viewer описание кон кретной ошибки. Если вам хочется получить минидамп последней ошибки, пол ный путь к нему указан в поле Crash Dump (аварийная копия памяти) окна Dr. Watson.

Рис. A 2. Диалоговое окно Log File View утилиты Dr. Watson для Windows XP

688 ЧАСТЬ V Приложения

Журналы Dr. Watson

Первый раздел журнала Dr. Watson имеет вид:

Исключение в приложении: Прил.: (pid=1796)

Время: 1/2/2003 @ 13:42:56.208

Номер: c0000005 (нарушение прав доступа)

Заголовок содержит информацию о причине ошибки: в моем примере это исклю чение в приложении. В случае некоторых ошибок номера исключений не всегда преобразуются в понятную людям форму, такую как «нарушение прав доступа» для исключения 0xC0000005. Все номера исключений вы можете узнать, отыскав в файле WINNT.H строки STATUS_. Коды ошибок указаны в документации как значе ния EXCEPTION_, возвращаемые функцией GetExceptionCode, однако реальные значе ния определяются в директивах #define STATUS_. После преобразования кода ошибки в значение EXCEPTION_ вы сможете просмотреть ее описание в документации к

GetExceptionCode.

Раздел System Information (сведения о системе) в объяснении не нуждается:

*——> Сведения о системе <——* Имя компьютера: HUME Имя пользователя: john

Число процессоров: 2

Тип процессора: x86 Family 15 Model 0 Stepping 10

Версия Windows 2000: 5.0

Текущая сборка: 2195

Пакет обновления: 3

Текущий тип: Multiprocessor Free

Зарегистрированная организация: Wintellect Зарегистрированный пользователь: John Robbins

Раздел Task List (список задач) выглядит так:

*——>

Список задач <——*

0

Idle.exe

8

System.exe

132

smss.exe

160

csrss.exe

156

winlogon.exe

208

services.exe

220

lsass.exe

364

svchost.exe

424

svchost.exe

472

spoolsv.exe

504

MWMDMSVC.exe

528

MWSSW32.exe

576

regsvc.exe

592

MSTask.exe

836

Explorer.exe

904

tp4mon.exe

912

tphkmgr.exe

ПРИЛОЖЕНИE A Чтение журналов Dr. Watson

689

 

 

920

4nt.exe

940

taskmgr.exe

956

tponscr.exe

268

msdev.exe

252

WDBG.exe

828

NOTEPAD.exe

416

drwtsn32.exe

0_Total.exe

Вразделе Task List выводится список процессов, выполнявшихся в момент ошиб ки. К сожалению, в нем нет информации об их версиях, поэтому вам придется узнать у пользователя версии файлов всех процессов. В левом столбце указаны десятичные идентификаторы процессов (PID), выполнявшихся в момент ошиб ки. После ошибки они совершенно бесполезны.

Вразделе Module List (список модулей) указываются все модули, загруженные в момент ошибки в адресное пространство. Информация обо всех модулях име ет формат (адрес загрузки – максимальный адрес). В этом месте впервые появля ются различия между журналами Dr. Watson в Windows 2000, Windows XP и Windows Server 2003. В журналах Windows 2000 вы увидите только диапазоны адресов мо дулей и больше ничего. Если ваше приложение откомпилировано в Microsoft Visual Studio 6 и для него доступны символы, то после диапазона адресов будут указаны загруженные символы. Так как DBGHELP.DLL, поставляемая с Windows 2000, ни чего не знает о символах Microsoft Visual Studio .NET, вы никогда не увидите загру женные символы для двоичных файлов, откомпилированных в этой среде. Если в Windows 2000 список модулей имеет некоторые недостатки, то Dr. Watson в Win dows XP/Server 2003 достаточно умен, чтобы указывать после каждого диапазона адресов имя соответствующей DLL.

Список модулей в Windows 2000 (00400000 6 00460000) (77F80000 6 77FFB000) (63000000 6 6301B000) (77E10000 6 77E6F000) (77E80000 6 77F31000)

Список модулей в Windows XP

(0000000000400000

6

0000000000460000: d:\Dev\BookTwo\Disk\Output\WDBG.exe

(0000000071c20000

6

0000000071c6e000: E:\WINDOWS\System32\NETAPI32.dll

(0000000075a70000

6

0000000075b15000: E:\WINDOWS\system32\USERENV.dll

(0000000075f40000

6

0000000075f5f000: E:\WINDOWS\system32\appHelp.dll

(00000000763b0000

6

00000000763f5000: E:\WINDOWS\system32\comdlg32.dll

Если вы желаете узнать, какие модули были загружены в Windows 2000, вам остается только гадать. Однако, как я несколько раз говорил, чрезвычайно важно знать, в какие области адресного пространства процесса загружаются ваши DLL. Скорее всего вы сможете узнать свои DLL по адресам загрузки. Чтобы получить сведения о других DLL на компьютере пользователя, можно написать утилиту, которая просматривала бы их и сообщала их имена, адреса загрузки и размер.

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

690 ЧАСТЬ V Приложения

операций, расположенные после адресов дизассемблированных команд, и пере нести строки информации о регистрах).

*——> State Dump for Thread Id 0xe14 (Копия памяти для потока 0xe14) <——*

eax=00000000 ebx=00000000 ecx=011305d8 edx=00000a30 esi=00154b40 edi=0012fae4

eip=00410144 esp=0012faa8 ebp=0012faf0 iopl=0

 

nv up ei pl nz na pe nc

 

 

 

 

cs=001b

ss=0023

ds=0023

es=0023

fs=0038

gs=0000

efl=00000202

функция: WDBG!CWDBGProjDoc__HandleBreakpoint

 

 

0041012b

push

esi

 

 

 

 

0041012c

push

edi

 

 

 

 

0041012d

push

ecx

 

 

 

 

0041012e

lea

edi,[ebp60x40]

 

 

 

00410131

mov

ecx,0xd

 

 

 

 

00410136

mov

eax,0xcccccccc

 

 

 

0041013b

rep

stosd

 

 

 

 

0041013d

pop

ecx

 

 

 

 

0041013e

mov

[ebp60x10],ecx

 

 

 

00410141

mov

eax,[ebp+0xc]

 

 

СБОЙ > 00410144

mov

ecx,[eax+0x4]

ds:0023:00000004=????????

 

00410147

cmp

dword ptr [ecx],0x80000003

 

 

0041014d

jz WDBG!CWDBGProjDoc__HandleBreakpoint+0x90 (004101a0)

 

0041014f

mov

[ebp60x14],esp

 

 

 

00410152

mov

[ebp60x18],ebp

 

 

 

00410155

mov

esi,esp

 

 

 

 

00410157

push

0x456070

 

 

 

 

0041015c

push

0x45606c

 

 

 

 

00410161

mov

edx,[ebp60x18]

 

 

 

00410164

xor

eax,eax

 

 

 

 

00410166

push

eax

 

 

 

Dr. Watson отображает информацию о состоянии каждого потока, выполнявше гося в процессе в момент ошибки. Состояния потока содержат всю информацию, необходимую для обнаружения механизма и причин краха.

В разделе регистров указываются значения всех регистров в момент ошибки. Особое внимание следует уделить регистру EIP, указателю команд. Для моего при мера из Windows XP у меня имелись символы, поэтому вы можете видеть, какую функцию выполнял этот поток в момент ошибки, но в большинстве журналов Dr. Watson информации о символах не будет. Конечно, если Dr. Watson не сообщает вам имя функции, это не проблема. Просто загрузите в программу CrashFinder из главы 12 проект CrashFinder вашего приложения, введите адрес в поле Hexadecimal Address(es) (шестнадцатеричные адреса) и нажмите кнопку Find (найти).

Поток из нашего фрагмента оказался потоком, вызвавшим ошибку. Об этом свидетельствует только указатель FAULT > (СБОЙ >) в середине дизассемблиро ванного листинга. Пару раз я видел журналы Dr. Watson, в которых не было ука зателя FAULT >. Если вы не можете найти в журнале этот указатель, изучите со

ПРИЛОЖЕНИE A Чтение журналов Dr. Watson

691

 

 

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

Если вы читали главу 7, дизассемблированный листинг должен быть вам по нятен. Новыми элементами будут только значения, показанные после команд. Чтобы вы могли узнать, какие значения использовались командой, дизассемблер Dr. Watson пытается просмотреть эффективный адрес ссылки на память. Адреса, начинаю щиеся с букв ss, свидетельствуют о том, что происходил доступ к сегменту стека; ds — к сегменту данных. В Windows XP/Server 2003 эффективный адрес будет указан только после строки, на которую указывал регистр EIP в момент ошибки.

Вжурналах Dr. Watson из Windows 2000 эффективные адреса будут указаны после каждой ассемблерной команды. Однако при этом гарантируется правиль ность только того адреса, что указан в строке, на которой находился указатель команд. Другие адреса могут быть неверны, так как значения, используемые коман дой, могли измениться. Допустим, первая дизассемблированная команда в состо янии потока ссылалась на память при помощи регистра EBX. Если ошибка случи лась после выполнения еще 10 команд, то одна из промежуточных команд легко могла изменить EBX. Однако, когда Dr. Watson в Windows 2000 дизассемблирует программу, для преобразования эффективного адреса он использует текущее зна чение EBX — то, которое имело место в момент ошибки. Поэтому эффективный адрес, показанный в дизассемблированном коде, может быть неверным. Итак, прежде чем поверить в значения эффективных адресов, убедитесь, что нужные регистры не были изменены никакой командой.

Благодаря недавно приобретенным навыкам работы с ассемблером, вы долж ны легко узнать, почему этот поток потерпел крах. Читая ассемблерный листинг Dr. Watson (или отладчика), большинство программистов допускает серьезную ошибку: они изучают его сверху вниз. Настоящая хитрость в том, чтобы начать исследование с места ошибки и постепенно подниматься вверх в поисках коман ды, присвоившей значения регистрам, использованным в команде, вызвавшей ошибку.

Внашем случае поток потерпел крах на команде 00410144 MOV ECX, [EAX+0x4], при которой регистр EAX имел значение 0. В Microsoft Windows все адреса, располо женные ниже 64 кб, отмечены как не имеющие доступа, поэтому попытка чтения памяти по адресу 0x00000004 — не лучшая идея. Итак, мы должны найти коман ду, заносящую 0 в EAX. Поднявшись на одну строку, вы увидите команду MOV EAX, [EBP+0xC]. Помните, что второй операнд, источник, помещается в первый операнд, приемник (иначе говоря, помните про правило «от источника к приемнику»). Это значит, что в EAX было скопировано значение, находившееся по адресу [EBP+0xC]. Следовательно, по адресу [EBP+0xC] располагался 0.

Вэтот момент вы должны вспомнить еще одну хитрость, которую я описал в главе 7: «параметры располагаются по положительным смещениям»! Параметры располагаются по положительным смещениям от регистра EBP, причем первый находится по адресу [EBP+0x8], а каждый следующий отстоит от предыдущего на 4 байта. Так как 0xC на 4 байта больше, чем 0x8, я могу предположить, что ошиб ка была вызвана тем, что второй параметр этой функции был равен NULL (наде юсь, прочитав эти два абзаца, вы поняли, как важно знать ассемблер в достаточ ном объеме для чтения журналов Dr. Watson!).

692 ЧАСТЬ V Приложения

Ниже вы можете увидеть вторую часть состояния потока: раздел Stack Back Trace (обратная трассировка стека) Заметьте: я вывожу имена функций на двух строках, чтобы они помещались на странице. При помощи двух символов подчеркивания (__) Dr. Watson отображает операцию разрешения области видимости (::).

*——> Stack Back Trace (Обратная трассировка стека) <——*

ChildEBP RetAddr

Args to Child

 

0012faf0

004100cd

00000a30

00000000

80000003

 

 

 

WDBG!CWDBGProjDoc__HandleBreakpoint +0x34

0012fb0c

004075f1

00000a30

0164f8fc

01130b68

 

 

 

WDBG!CWDBGProjDoc__HandleExceptionEvent+0x6d

0012fb20

7c3422b2

00000a30

0164f8fc

0000000d

 

 

 

WDBG!CDocNotifyWnd__HandleExceptionEvent+0x21

0012fc28

7c341b2e

00000502

00000a30

0164f8fc

 

 

 

MFC71UD!CWnd__OnWndMsg+0x752

0012fc48

7c33f2f0

00000502

00000a30

0164f8fc

 

 

 

MFC71UD!CWnd__WindowProc+0x2e

0012fcc0 7c33f7ce

01130b68

002502ca 00000502

 

 

 

MFC71UD!AfxCallWndProc+0xe0

0012fce0

7c3b072a 002502ca

00000502

00000a30

 

 

 

MFC71UD!AfxWndProc+0x9e

0012fd10

77d67ad7 002502ca

00000502

00000a30

 

 

 

MFC71UD!AfxWndProcBase+0x4a

0012fd3c 77d6ccd4 7c3b06e0 002502ca 00000502

 

 

 

USER32!SetWindowPlacement+0x57

0012fda4

77d445bd

00000000

7c3b06e0

002502ca

 

 

 

USER32!DefRawInputProc+0x284

0012fdf8

77d447d4

00593330

00000502

00000a30

 

 

 

USER32!TranslateMessageEx+0x78d

0012fe20 77fb4da6 0012fe30 00000018 00593330

 

 

 

USER32!DefWindowProcA+0x209

0012fe64

7c34e8e1 00154af8

00000000

00000000

 

 

 

ntdll!KiUserCallbackDispatcher+0x13

0012fe90 7c34fb4c

00455e30

0012feb8 7c34f407

 

 

 

MFC71UD!AfxInternalPumpMessage+0x21

0012fe9c

7c34f407

00000001

00455e30

00154ac8

 

 

 

MFC71UD!CWinThread__PumpMessage+0xc

0012feb8

7c34fe87

00455e30

00434ffa

0040a66b

 

 

 

MFC71UD!CWinThread__Run+0x87

0012fecc 7c34865a

1020c034

102682d0

ffffffff

 

 

 

MFC71UD!CWinApp__Run+0x57

0012fef0

00430008

00400000

00000000

00020c22

 

 

 

MFC71UD!AfxWinMain+0xda

0012ff08

004284b8

00400000

00000000

00020c22

 

 

 

WDBG!wWinMain+0x18

0012ffc0 77e814c7

00140000

01f88550

7ffdf000

 

 

 

WDBG!wWinMainCRTStartup+0x1f8

0012fff0

00000000

004282c0

00000000

78746341

 

 

 

kernel32!GetCurrentDirectoryW+0x44

Соседние файлы в предмете Программирование на C++