Application verifier - простая бесплатная мощь
Есть множество тулзов, помогающих в отладке программ под Windows. Какие-то лучше, какие-то хуже. Какие-то проще, какие-то сложнее. А можно ли выделить самый простой и полезный способ тестирования программ? Все полезны по-своему и про все стоит рассказать. Так что напишу-ка я серию статей про тулзы, помогающие в отладке программ под Windows.
Сегодня в первой статье я расскажу про GFlags и фичу “Enable Application Verifier” из Debugging tools for Windows. Выбрал именно это для первой статьи за полезность, простоту и удобство. По соотношению “простота использования\полезность” - это явный лидер.
Итак, если вы незнакомы с Application Verifier-ом, то эта статья для вас.
Итак, допустим у вас есть программа. Она работает и не падает, ну разве что очень-очень редко. Уверены ли вы, что программа корректна? Хотели бы вы иметь тулзу, которая сможет автоматически проверить основные ошибки и указать вам на них? При этом, естественно, хотелось бы сделать всё это с минимальными усилиями.
Тогда вам надо обратить внимание на Debugging tools for Windows и GFlags.
Итак, я написал небольшую тестовую программу для демонстрации принципа:
struct Test
{
char t[100];
char* p;
CRITICAL_SECTION cs;
};
void func_useDeletedMemory()
{
printf("func_useDeletedMemory");
Test* t = new Test;
t->p = new char[100];
delete t;
delete []t->p;
}
void func_deleteMemoryWithCriticalSection()
{
printf("func_deleteMemoryWithCriticalSection");
Test* t = new Test;
InitializeCriticalSection(&t->cs);
delete t;
}
int _tmain(int argc, _TCHAR* argv[])
{
if (argc < 2)
func_useDeletedMemory();
else
func_deleteMemoryWithCriticalSection();
return 0;
}
Если программа запускается без аргументов, то вызывается функция func_useDeletedMemory. Если с аргументами в командной строке, то func_deleteMemoryWithCriticalSection.
Программа компилируется и отрабатывает без ошибок, как ее не запускай. Но ведь ошибки очевидны?
В функции func_useDeletedMemory мы удалили структуру, а потом пытаемся удалить что-то изнутри структуры.
А в функции func_deleteMemoryWithCriticalSection мы удаляем структуру, в которой содержится неудаленная критическая секция - тут происходит утечка хэндла.
Обе эти функции при тестах падать не будут, но при реальном использовании утечки хэндлов могут привести со временем к чему угодно, а func_useDeletedMemory может легко начать очень редко падать в мультипоточных приложениях.
Попробуем отловить эти ошибки. Запустим программу GFlags.exe из комплекта Debugging tools for Windows и включим Application Veriifier для нашего тестового приложения. Для этого нужно всего лишь набрать имя приложения, нажать tab, включить одну галочку и нажать OK:

После этого запустим программу без параметров опять и сразу получаем падение!
Аттачим WinDBG или сначала снимаем дамп, а потом открываем его в WinDBG. Там набираем “!analize -v” и получаем:
0:001> !analyze -v
*******************************************************************************
* *
* Exception Analysis *
* *
*******************************************************************************
APPLICATION_VERIFIER_CORRUPTED_HEAP_BLOCK (8)
Corrupted heap block.
This is a generic error issued if the corruption in the heap block
cannot be placed in a more specific category.
Arguments:
Arg1: 01681000, Heap handle used in the call.
Arg2: f0f0f0f0, Heap block involved in the operation.
Arg3: 00000000, Size of the heap block.
Arg4: 00000000, Reserved
............
PROCESS_NAME: TestApplicationVerifier.exe
INVALID_HEAP_ADDRESS: f0f0f0f0 (!heap -p -a f0f0f0f0)
ERROR_CODE: (NTSTATUS) 0x80000003 - {EXCEPTION} Breakpoint A breakpoint has been reached.
NTGLOBALFLAG: 2000100
APPLICATION_VERIFIER_FLAGS: 6
FAULTING_THREAD: 00000448
STACK_TEXT:
0012f53c 7c90d9ca 7c8645fd d0000144 00000004 ntdll!KiFastSystemCallRet
0012f540 7c8645fd d0000144 00000004 00000000 ntdll!ZwRaiseHardError+0xc
0012f7c4 7c8438fa 0012f7ec 7c839b39 0012f7f4 kernel32!UnhandledExceptionFilter+0x628
0012f7cc 7c839b39 0012f7f4 00000000 0012f7f4 kernel32!BaseProcessStart+0x39
0012f7f4 7c9032a8 0012f8e0 0012ffe0 0012f900 kernel32!_except_handler3+0x61
0012f818 7c90327a 0012f8e0 0012ffe0 0012f900 ntdll!ExecuteHandler2+0x26
0012f8c8 7c90e48a 00000000 0012f900 0012f8e0 ntdll!ExecuteHandler+0x24
0012f8c8 7c90120e 00000000 0012f900 0012f8e0 ntdll!KiUserExceptionDispatcher+0xe
0012fbc8 7c956845 00000008 f0f0f0f0 00000000 ntdll!DbgBreakPoint
0012fbe0 7c96b28c 00000008 7c96b4cc 01681000 ntdll!RtlApplicationVerifierStop+0x160
0012fc5c 7c96c44f 01681000 00000004 f0f0f0f0 ntdll!RtlpDphReportCorruptedBlock+0x92
0012fc80 7c96c652 01681000 01001002 00000020 ntdll!RtlpDphNormalHeapFree+0x2e
0012fcd0 7c96f6f3 01680000 01001002 f0f0f0f0 ntdll!RtlpDebugPageHeapFree+0x79
0012fd44 7c94bc4c 01680000 01001002 f0f0f0f0 ntdll!RtlDebugFreeHeap+0x2c
0012fe2c 7c927573 01680000 01001002 f0f0f0f0 ntdll!RtlFreeHeapSlowly+0x37
0012fefc 5ad127d1 01680000 00000000 f0f0f0f0 ntdll!RtlFreeHeap+0xf9
0012ff14 78134c39 01680000 00000000 f0f0f0f0 verifier!AVrfpRtlFreeHeap+0x15
0012ff60 00401038 f0f0f0f0 01785588 00000064 msvcr80!free+0xcd
0012ff7c 004011e5 00000001 017844c8 01783060 TestApplicationVerifier!main+0x38 [c:\111\testapplicationverifier\testapplicationverifier\testapplicationverifier.cpp @ 35]
0012ffc0 7c817077 d309ee22 01c9c4fa 7ffdd000 TestApplicationVerifier!__tmainCRTStartup+0x10f [f:\sp\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 597]
0012fff0 00000000 0040132e 00000000 78746341 kernel32!BaseProcessStart+0x23
PRIMARY_PROBLEM_CLASS: STATUS_BREAKPOINT
BUGCHECK_STR: APPLICATION_FAULT_STATUS_BREAKPOINT
FOLLOWUP_IP:
msvcr80!free+cd
78134c39 85c0 test eax,eax
SYMBOL_STACK_INDEX: 11
SYMBOL_NAME: msvcr80!free+cd
FOLLOWUP_NAME: MachineOwner
MODULE_NAME: msvcr80
IMAGE_NAME: msvcr80.dll
DEBUG_FLR_IMAGE_TIMESTAMP: 4889d619
STACK_COMMAND: ~0s ; kb
FAILURE_BUCKET_ID: msvcr80.dll!free_80000003_STATUS_BREAKPOINT
BUCKET_ID: APPLICATION_FAULT_STATUS_BREAKPOINT_msvcr80!free+cd
Followup: MachineOwner
---------
Тут нам сразу готовый callstack с проблемой и примерное описание проблемы - что-то не так с кучей. После этого достаточно посмотреть на код, чтобы понять, что не так.
Обычно подсказки от Application Verifier гораздо более информативны. Например, в случае вызова функции func_deleteMemoryWithCriticalSection. Запускаем тестовое приложение с параметрами и получаем опять падение.
Открываем дамп в WInDBG, набираем “!analize -v” и получаем:
0:001> !analyze -v
*******************************************************************************
* *
* Exception Analysis *
* *
*******************************************************************************
APPLICATION_VERIFIER_LOCK_IN_FREED_HEAP (202)
Freeing heap block containing an active critical section.
This stop is generated if a heap allocation contains a critical section,
the allocation is freed and the critical section has not been deleted.
To debug this stop use the following debugger commands:
$ !cs -s parameter1 - dump information about this critical section.
$ ln parameter1 - to show symbols near the address of the critical section.
This should help identify the leaked critical section.
$ dps parameter2 - to dump the stack trace for this critical section initialization.
$ parameter3 and parameter4 might help understand where was this heap block
allocated (the size of the allocation is probably significant).
Arguments:
Arg1: 017855f8, Critical section address.
Arg2: 004115bc, Critical section initialization stack trace.
Arg3: 01785590, Heap block address.
Arg4: 00000080, Heap block size.
FAULTING_IP:
ntdll!DbgBreakPoint+0
7c90120e cc int 3
EXCEPTION_RECORD: 0012f90c -- (.exr 0x12f90c)
ExceptionAddress: 7c90120e (ntdll!DbgBreakPoint)
ExceptionCode: 80000003 (Break instruction exception)
ExceptionFlags: 00000000
NumberParameters: 3
Parameter[0]: 00000000
Parameter[1]: 7c91ead5
Parameter[2]: 0000003d
DEFAULT_BUCKET_ID: STATUS_BREAKPOINT
PROCESS_NAME: TestApplicationVerifier.exe
CRITICAL_SECTION: 017855f8 -- (!cs -s 017855f8)
ERROR_CODE: (NTSTATUS) 0x80000003 - {EXCEPTION} Breakpoint A breakpoint has been reached.
NTGLOBALFLAG: 2000100
APPLICATION_VERIFIER_FLAGS: 6
FAULTING_THREAD: 00001160
CONTEXT: 0012f92c -- (.cxr 0x12f92c)
eax=00000001 ebx=004115bc ecx=7c91ead5 edx=0000003d esi=00000202 edi=017855f8
eip=7c90120e esp=0012fbf8 ebp=0012fc0c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!DbgBreakPoint:
7c90120e cc int 3
Resetting default scope
LAST_CONTROL_TRANSFER: from 7c956845 to 7c90120e
STACK_TEXT:
0012f568 7c90d9ca 7c8645fd d0000144 00000004 ntdll!KiFastSystemCallRet
0012f56c 7c8645fd d0000144 00000004 00000000 ntdll!ZwRaiseHardError+0xc
0012f7f0 7c8438fa 0012f818 7c839b39 0012f820 kernel32!UnhandledExceptionFilter+0x628
0012f7f8 7c839b39 0012f820 00000000 0012f820 kernel32!BaseProcessStart+0x39
0012f820 7c9032a8 0012f90c 0012ffe0 0012f92c kernel32!_except_handler3+0x61
0012f844 7c90327a 0012f90c 0012ffe0 0012f92c ntdll!ExecuteHandler2+0x26
0012f8f4 7c90e48a 00000000 0012f92c 0012f90c ntdll!ExecuteHandler+0x24
0012f8f4 7c90120e 00000000 0012f92c 0012f90c ntdll!KiUserExceptionDispatcher+0xe
0012fbf4 7c956845 00256488 017855f8 01785590 ntdll!DbgBreakPoint
0012fc0c 7c944172 00000202 7c944214 017855f8 ntdll!RtlApplicationVerifierStop+0x160
0012fc70 7c96c461 01785590 00000080 00000000 ntdll!RtlpCheckForCriticalSectionsInMemoryRange+0xe5
0012fc90 7c96c652 01681000 01001002 00000000 ntdll!RtlpDphNormalHeapFree+0x40
0012fce0 7c96f6f3 01680000 01001002 01785590 ntdll!RtlpDebugPageHeapFree+0x79
0012fd54 7c94bc4c 01680000 01001002 01785590 ntdll!RtlDebugFreeHeap+0x2c
0012fe3c 7c927573 01680000 01001002 01785590 ntdll!RtlFreeHeapSlowly+0x37
0012ff0c 5ad127d1 01680000 00000000 01785590 ntdll!RtlFreeHeap+0xf9
0012ff24 78134c39 01680000 00000000 01785590 verifier!AVrfpRtlFreeHeap+0x15
0012ff70 00401069 01785590 00000001 004011e5 msvcr80!free+0xcd
0012ff7c 004011e5 00000002 017844c8 01783060 TestApplicationVerifier!main+0x69 [c:\111\testapplicationverifier\testapplicationverifier\testapplicationverifier.cpp @ 37]
0012ffc0 7c817077 d309ee22 01c9c4fa 7ffda000 TestApplicationVerifier!__tmainCRTStartup+0x10f [f:\sp\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 597]
0012fff0 00000000 0040132e 00000000 78746341 kernel32!BaseProcessStart+0x23
PRIMARY_PROBLEM_CLASS: STATUS_BREAKPOINT
BUGCHECK_STR: APPLICATION_FAULT_STATUS_BREAKPOINT
FOLLOWUP_IP:
msvcr80!free+cd
78134c39 85c0 test eax,eax
SYMBOL_STACK_INDEX: 11
SYMBOL_NAME: msvcr80!free+cd
FOLLOWUP_NAME: MachineOwner
MODULE_NAME: msvcr80
IMAGE_NAME: msvcr80.dll
DEBUG_FLR_IMAGE_TIMESTAMP: 4889d619
STACK_COMMAND: ~0s ; kb
FAILURE_BUCKET_ID: msvcr80.dll!free_80000003_STATUS_BREAKPOINT
BUCKET_ID: APPLICATION_FAULT_STATUS_BREAKPOINT_msvcr80!free+cd
Followup: MachineOwner
---------
0:000> !cs -s 017855f8
-----------------------------------------
Critical section = 0x017855f8 (+0x17855F8)
DebugInfo = 0x00256480
NOT LOCKED
LockSemaphore = 0x0
SpinCount = 0x00000000
Stack trace for DebugInfo = 0x00256480:
0x7c911583: ntdll!RtlInitializeCriticalSectionAndSpinCount+0xC9
0x7c91162c: ntdll!RtlInitializeCriticalSection+0xF
0x7c809f9f: kernel32!InitializeCriticalSection+0xE
0x00401063: TestApplicationVerifier!main+0x63
0x7c817077: kernel32!BaseProcessStart+0x23
Обратите внимание на подсказку: Freeing heap block containing an active critical section. и дальнейшие подсказки. Всё настолько просто и очевидно, что даже думать не надо! Один из советов - набрать “!cs -s 017855f8″, что я и сделал в конце. Эта команда позволяет с включенным Application Verifier увидеть кто и когда создавал и удалял нужную критическую секцию.
Если вы думаете, что это помогает только в таких тестовых случаях, то вы сильно неправы. Глючит всё. Глупы те, кто не использует такие вот простые способы для проверки своих программ.
Для примера я запустил GFlags и включил Application Verifier для установленного у меня лицензионного Lingvo12. Уж он-то не должен глючить.
Запустил Lingvo.exe, нажал выход - получил сразу падение. Открыл дамп в WinDBG, написал “!analize -v” и получил сообщение:
0:001> !analyze -v
APPLICATION_VERIFIER_INVALID_HANDLE (300)
Invalid handle exception for current stack trace.
This stop is generated if the function on the top of the stack passed an
invalid handle to system routines. Usually a simple kb command will reveal
what is the value of the handle passed (must be one of the parameters -
usually the first one). If the value is null then this is clearly wrong.
If the value looks ok you need to use !htrace debugger extension to get a
history of operations pertaining to this handle value. In most cases it
must be that the handle value is used after being closed.
Arguments:
Arg1: ffffffff, Exception code.
Arg2: 0000abba, Exception record. Use .exr to display it.
Arg3: 00000000, Context record. Use .cxr to display it.
Arg4: 00000000, Not used.
Там еще был call stack, но без правильных pdb файлов он бесполезен. Однако ошибка очевидна - используется ошибочный хэндл в одной из оконных функций, скорее всего этот хэндл просто уже удален к этому времени.
Другие программы тоже имеют такие ошибки - можете легко проверить сами.
Мой совет прост: всегда запускайте свои программы на тестировании с включенным флагом “Enable application verifier”. Это позволит вам сразу находить множество возможных ошибок, которые в обычной ситуации приходят от клиентов в виде случайных падений и других неприятностей.
Понравилась статья? Подпишись на RSS!
Похожие записи:
Шаблоны проектирования: структурные паттерны ч.1
Шаблоны проектирования: практические примеры. Часть 1.
Потоки и память
Книги + программисты = деньги
Не будите спящего программиста
2 признака кода с душком: убей его и лови всё молча
Рекомендую книги по этой теме:
|
|
|
|
|
|
|
А ты не пробовал натравить эту тулзу на саму себя?
Было бы показательно натравливать тулзы на самих себя и собратьев по функционалу.
Не пробовал. Почему-то я уверен, что они сами себя проверяют.
Вообще, этот комплект Debugging tools for Windows очень уж качественный. Гораздо качественнее самой Windows и большинства программ для нее
Тулзы там качественные, но GUI кошмарный
При этом дампы смотреть мне все равно удобней в WinDBG, чем в студии почему-то…
Кстати, про одну тулзу из этого комплекта писал недавно.
Ага, про UMDH я уже писал тут. И еще напишу
Гуи там страшненькие, но они обычно и не нужны. Я теперь в студии вообще дебажить не могу - только в WinDBG.
Кстати, в WinDBG можно форсировать загрузку pdb-файла с “неправильными” chechsum и timestamp. В студии я не нашел как это сделать. Может удавалось решить такую задачу в Visual Studio?
Мне не удавалось - там настроек для этого вообще нет.
а .net-приложения этой штукой можно проверять? ну, хотя бы после прогона через генератор нативного кода?
.net - нельзя. А перегнанные в нативный код - не знаю, надо проверять.
Здрасте, я натравил глобал флаг на миранду, просто так, попробовать решил. Теперь проблема миранда жутко тормозит помогите, что делать? Настройки глобал флаг вернул как было, но непомогло.
Найди в реестре в ключе “HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\” подключ с именем таким же, как у exe-файла миранды и потри его
Кто-нибудь в этой штуке запустит F-Secure компоненты?
ModLoad: 17330000 17347000 c:\program files\f-secure\scanner-interface\fsgkiapi.dll
=======================================
VERIFIER STOP 00000303: pid 0x13BC: NULL handle passed as parameter. A valid handle must be used.
00000000 : Not used.
00000000 : Not used.
00000000 : Not used.
00000000 : Not used.
А то приходится антивирус при отладке выключать…
Я сам мучаюсь, но fsgkiapi.dll - это не мой компонент. Баги сабмичу, понемногу правят, но конца и края для fsgkiapi.dll не видно.
На самом деле тебе не антивирус надо отключать, а только Web traffic scanning, ибо он уже грузит fsgkiapi.dll.
А Web traffic scanning грузится везде, где используется winsock