Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Установка и настройка отладчика
Некоторые действия средства проверки приложений могут привести к возникновению исключения. Отладчик должен быть настроен на обработку этих исключений на втором этапе, так как непосредственно Application Verifier будет обрабатывать исключения первого шанса.
Исключения, возникающие, относятся к трем типам:
Исключение нарушения доступа (0xC0000005) создается, если параметр кучи обнаруживает переполнение буфера кучи. В некоторых случаях параметр "Проверить системный путь" также может привести к нарушению доступа.
Исключение недопустимого дескриптора (0xC0000008) создается, когда функция Обнаружение недопустимого использования дескриптора обнаруживает недопустимую операцию дескриптора.
Исключение переполнения стека (0xC00000FD) создается, когда параметр проверки достаточного стека обнаруживает, что начальный стек был слишком коротким.
Одним из способов подготовки к этим событиям является запуск отладчика в командной строке следующим образом:
windbg -xd av -xd ch -xd sov ApplicationCommandLine
или
cdb -xd av -xd ch -xd sov ApplicationCommandLine
Если вы уже запустили отладчик, можно использовать команду sxd (Set Exceptions) для перехвата всех нарушений доступа, недопустимых дескрипторов и переполнения стека в качестве исключений второго шанса:
0:000> sxd av
0:000> sxd ch
0:000> sxd sov 1
Теоретически можно управлять проверкой приложений с помощью отладчика ядра. Однако это не рекомендуется. Для этого требуется частое использование команд .process и .pagein, но оно не дает больше возможностей, чем использование отладчика в пользовательском режиме.
Установка средств отладки
Чтобы скачать последнюю версию инструментов, см. статью "Скачать средства отладки" для Windows.
Настройка оборудования для отладки User-Mode
Отладка в пользовательском режиме обычно выполняется на одном компьютере: отладчик выполняется на том же компьютере, что и приложение, которое завершилось сбоем.
В этом случае не требуется настройка оборудования. В этом разделе термины главный компьютер и целевой компьютер взаимозаменяемы в данном случае.
Настройка программного обеспечения для отладки User-Mode
Базовая User-Mode конфигурация. Прежде чем начать отладку в пользовательском режиме, необходимо скачать необходимые файлы символов и задать определенные переменные среды.
Файлы символов
Необходимо скачать файлы символов для отлаживаемого процесса пользовательского режима. Если это приложение, которое вы написали, оно должно быть создано со всеми символьными файлами. Если это коммерческое приложение, файлы символов могут быть доступны на веб-сервере или для скачивания, обратитесь к производителю.
Если выполняется удаленная отладка, расположение файла символов зависит от используемого метода:
При удаленной отладке с помощью отладчика файлы символов должны находиться на компьютере с сервером отладки.
Если выполняется удаленная отладка через remote.exe, файлы символов должны находиться на компьютере с отладчиком.
Если выполняется удаленная отладка с помощью сервера обработки или сервера подключения KD, файлы символов должны находиться на компьютере с смарт-клиентом.
Если вы управляете отладчиком пользовательского режима из отладчика ядра, файлы символов должны находиться на обоих компьютерах.
Настройка переменных среды
Отладчик использует различные переменные среды для указания ряда важных параметров.
Дополнительные сведения об отладчиках см. в статье "Начало работы с отладкой Windows"
Настройка средства проверки приложений с помощью отладчика с помощью командной строки
Чтобы настроить средство проверки приложений, можно использовать командную строку CDB или NTSD.
Используйте следующую командную строку:
cdb OtherOptions -vf:Flags Target
Где target — это имя целевого приложения, а флаги — нужные параметры проверки приложений, которые должны применяться к этому целевому объекту.
Флаги должны быть суммой битов, представляющих нужные параметры. Ниже приведены отдельные битовые значения:
| Значение флага | Значение |
|---|---|
| 00000001 | ПРОВЕРКИ КУЧИ |
| 00000004 | УПРАВЛЕНИЕ ПРОВЕРКАМИ |
| 00000008 | ПРОВЕРКИ SIM-КАРТ ПРИ НЕДОСТАТКЕ РЕСУРСОВ |
| 00000020 | ПРОВЕРКИ TLS |
| 00000040 | ГРЯЗНЫЕ СТЕКИ |
| 00000200 | ОПАСНЫЕ интерфейсы программирования приложений (API) |
| 00001000 | ПРОВЕРКА ИСКЛЮЧЕНИЙ |
| 00002000 | ПРОВЕРКИ ПАМЯТИ |
| 00020000 | ДРУГИЕ ПРОВЕРКИ |
| 00040000 | ПРОВЕРКА ЗАМКОВ |
Отладка с помощью !avrf
Расширение !avrf управляет параметрами средства проверки приложений и отображает различные выходные данные, созданные средством проверки приложений. Дополнительные сведения о расширении !arvrf см. в документации по отладчику !avrf .
Синтаксис
!avrf
Команда !avrf без параметров показывает параметры проверки приложений и сведения о текущем и предыдущем прерывании проверки приложений, если таковые есть.
!avrf –vs { Length | -aAddress }
Отображает журнал операций виртуального пространства. Длина указывает количество записей, отображаемых начиная с последней. Адрес указывает виртуальный адрес. Будут отображаться записи виртуальных операций, содержащих этот виртуальный адрес.
!avrf -hp { Length | -a Address }
Отображает журнал операций кучи. Адрес указывает на адрес кучи. Будут отображаться записи операций кучи, содержащих этот адрес кучи.
!avrf -cs { Length | -a Address }
Отображает журнал удаления критического раздела. Длина указывает количество записей, отображаемых начиная с последней. Адрес указывает адрес критического раздела. Записи для определенного критического раздела отображаются при указании адреса.
!avrf -dlls [ Length ]
Отображает журнал загрузки и выгрузки библиотеки DLL. Длина указывает количество записей, отображаемых начиная с последней.
!avrf -trm
Отображает журнал всех завершенных и приостановленных потоков.
!avrf -ex [ Length ]
Отображает журнал исключений. Средство проверки приложений отслеживает все исключения, происходящие в приложении.
!avrf -threads [ ThreadID ]
Отображает сведения о потоках в целевом процессе. Для дочерних потоков также отображаются размеры стека и флаги CreateThread, указанные родительским потоком. При предоставлении идентификатора потока будут отображаться сведения только для этого конкретного потока.
!avrf -tp [ ThreadID ]
Отображает журнал пула потоков. Этот журнал может содержать трассировки стека для различных операций, таких как изменение маски привязки потоков, изменение приоритета потока, отправка сообщений в потоки, инициализация COM и деинициализация COM из обратного вызова пула потоков. При предоставлении идентификатора потока будут отображаться сведения только для этого конкретного потока.
!avrf -srw [ Address | Address Length ] [ -stats ]
Отображает журнал средства чтения и записи Slim Reader/Writer (SRW). Указание адреса блокировки SRW будет отображать записи, относящиеся к этому адресу. Если длина указана вместе с адресом, отображаются все блокировки SRW в этом диапазоне адресов. Параметр -stats сбрасывает статистику блокировки SRW.
!avrf -leak [ -m ModuleName ] [ -r ResourceType ] [ -a Address ] [ -t ]
Отображает журнал невыполненных ресурсов. Эти ресурсы в любой момент могут быть или не быть утечками. Указание имени модуля (включая расширение) отображает все выдающиеся ресурсы в указанном модуле. При указании ResourceType отображаются выдающиеся ресурсы данного типа ресурса. С указанием адреса создаются дампы записей о невыполненных ресурсах с этим адресом. ResourceType может быть одним из следующих вариантов:
- Куча: отображение выделения кучи с помощью API-интерфейсов Кучи Win32
- Local: отображает локальные и глобальные распределения
- CRT: отображает распределения с помощью API CRT
- Virtual: Отображает виртуальные резервирования
- BSTR: отображает выделения BSTR
- Реестр: отображает открытие ключа реестра
- Power: отображает объекты уведомлений о питании
- Дескриптор: отображает выделения потоков, файлов и событий
!avrf –trace TraceIndex
Отображает трассировку стека для указанного индекса трассировки. Некоторые структуры используют этот 16-разрядный номер индекса для идентификации трассировки стека. Этот индекс указывает на расположение в базе данных трассировки стека. Если вы анализируете такую структуру, вы найдете этот синтаксис полезным.
!avrf -cnt
Отображает список глобальных счетчиков.
!avrf -brk [ BreakEventType ]
Указывает, что это команда события останова. При использовании !avrf -brk без дополнительных параметров отображаются настройки события останова. BreakEventType указывает номер типа события прерывания. Для списка возможных типов используйте !avrf -brk.
!avrf -flt [ EventTypeProbability ]
Указывает, что это команда внедрения ошибок. При использовании !avrf -flt без дополнительных параметров отображаются текущие параметры внедрения ошибок. EventType указывает номер типа события. Вероятность указывает частоту сбоя события. Это может быть любое целое число от 0 до 1000 000 (0xF4240).
!avrf -flt break EventType
Приводит к тому, что проверяющее приложение прерывается в отладчик каждый раз, когда вызывается эта ошибка.
!avrf -flt stacks Length
Отображает указанное количество трассировок стека для наиболее недавних операций, подвергнутых внедрению сбоев.
!avrf -trg [ StartEnd | dll Module | all ]
Указывает, что это команда для целевого диапазона. Если -trg используется без дополнительных параметров, отображаются текущие целевые диапазоны. Start задает начальный адрес целевого диапазона или диапазона исключений. End задает конечный адрес целевого диапазона или диапазона исключений. Модуль указывает имя модуля, который должен быть нацелен или исключен. Модуль должен содержать полное имя модуля, включая расширение .exe или .dll. Сведения о пути не должны быть включены. Указание всех причин сброса всех целевых диапазонов или диапазонов исключений.
!avrf -skp [ StartEnd | dll Module | all | Time ]
Указывает, что это команда для задания диапазона исключений. Start задает начальный адрес целевого диапазона или диапазона исключений. End задает конечный адрес целевого диапазона или диапазона исключений. Модуль указывает имя модуля, который должен быть выбран или исключен. Модуль должен содержать полное имя модуля, включая расширение .exe или .dll. Сведения о пути не должны быть включены. Указание всех диапазонов приводит к сбросу всех целевых диапазонов или диапазонов исключений. Указание времени приводит к подавлению всех ошибок на указанное количество миллисекунд после возобновления выполнения.
Ниже приведены выходные данные, предоставляемые командой !avrf в отладчике.
0:000> !avrf
Application verifier settings (816431A7):
- full page heap
- COM
- RPC
- Handles
- Locks
- Memory
- TLS
- Exceptions
- Threadpool
- Leak
- SRWLock
No verifier stop active.
Note: Sometimes bugs found by verifier manifest themselves as raised
exceptions (access violations, stack overflows, invalid handles),
and it is not always necessary to have a verifier stop.
Комментарии расширения !avrf
Если расширение !avrf используется без параметров, оно отображает текущие параметры проверки приложений.
Расширение !avrf использует Exts.dll в отладчике.
Если произошла остановка проверяющего приложения, расширение !avrf без параметров покажет характер остановки и причину.
Если отсутствуют символы ntdll.dll и verifier.dll, расширение !avrf создаст сообщение об ошибке.
Континуируемые и неконтинуируемые остановки
Отладка непрерывной остановки
Ниже приведен пример исключения из-за недопустимого дескриптора, вызванного опцией обнаружения использования недопустимого дескриптора.
Во-первых, появится следующее сообщение:
Invalid handle - code c0000008 (first chance)
===================================================
VERIFIER STOP 00000300: pid 0x558: invalid handle exception for current stack trace
C0000008 : Exception code.
0012FBF8 : Exception record. Use .exr to display it.
0012FC0C : Context record. Use .cxr to display it.
00000000 :
===================================================
This verifier stop is continuable.
After debugging it use 'go' to continue.
===================================================
Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=6a27c280 ecx=6a226447 edx=0012fa4c esi=00942528 edi=6a27c260
eip=6a22629c esp=0012facc ebp=0012faf0 iopl=0 nv up ei pl zr na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!DbgBreakPoint:
6a22629c cc int 3
Обратите внимание, что сообщение сообщает о том, что эта остановка средства проверки приложений может быть продолжена. После понимания того, что произошло, можно продолжить работу целевого приложения.
Сначала следует использовать расширение !avrf. Это дает сведения о текущем сбое:
0:000> !avrf
Global flags: 00000100
Application verifier global flag is set.
Application verifier settings (00000004):
- no heap checking enabled!
- handle checks
Page heap is not active for this process.
Current stop 00000300 : c0000008 0012fbf8 0012fc0c 00000000 .
Using an invalid handle (either closed or simply bad).
Последняя строка этого отображения суммирует проблему.
Возможно, вы хотите посмотреть на некоторые логи на данном этапе. После завершения используйте команду g (Go), чтобы снова запустить приложение:
0:000> g
## Debugging a Non-Continuable Stop
Here is an example of an access violation that has been raised by the page heap option.
First, the following message appears:
Access violation - code c0000005 (first chance)
===================================================
VERIFIER STOP 00000008: pid 0x504: exception raised while verifying block header
00EC1000 : Heap handle
00F10FF8 : Heap block
00000000 : Block size
00000000 :
===================================================
This verifier stop is not continuable. Process will be terminated when you use the 'go' debugger command.
===================================================
Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=00000000 ecx=6a226447 edx=0012fab7 esi=00f10ff8 edi=00000008
eip=6a22629c esp=0012fb5c ebp=0012fb80 iopl=0 nv up ei pl zr na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!DbgBreakPoint:
6a22629c cc int 3
В этом случае сообщение сообщает о том, что эта остановка средства проверки приложений не может быть продолжена. Ошибка слишком серьезна для продолжения выполнения процесса, и для проверки приложений нет способа спасти процесс.
Расширение !avrf можно использовать для предоставления сведений о текущем сбое:
0:000> !avrf
Global flags: 02000100
Application verifier global flag is set.
Page heap global flag is set.
Application verifier settings (00000001):
- full page heap
Page heaps active in the process (format: pageheap, lightheap, flags):
00941000 , 00a40000 , 3 (pageheap traces )
00b41000 , 00c40000 , 3 (pageheap traces )
00cb1000 , 00db0000 , 3 (pageheap traces )
00ec1000 , 00fc0000 , 3 (pageheap traces )
Current stop 00000008 : 00ec1000 00f10ff8 00000000 00000000 .
Corrupted heap block.
Последняя строка этого отображения суммирует проблему.
Вы также можете просмотреть некоторые логи на этом этапе. Вы можете использовать команду перезапуска (перезапуск целевого приложения) на этом этапе. Или, возможно, вы можете завершить сеанс проверки приложений и начать исправление ошибок в коде.
Отладка ошибок критических секций
Расширение отладчика !cs
!cs можно использовать как в отладчике пользовательского режима, так и в отладчике ядра для отображения сведений о критических разделах текущего процесса. Для получения дополнительных сведений о расширении !cs см. !cs в документации по отладчику.
Требуется сопоставление символов с сведениями о типе, особенно для ntdll.dll.
Синтаксис этого расширения:
!cs [-s] — дампа всех активных критических разделов в текущем процессе.
Адрес !cs [-s] — критически важный раздел дампа по этому адресу.
!cs [-s] -d address - вывести критическую секцию, соответствующую DebugInfo по этому адресу.
-s выведет трассировку стека инициализации критической секции, если она доступна.
Примеры.
Использовать адрес для извлечения информации о критическом разделе.
0:001> ! cs 0x7803B0F8
Critical section = 0x7803B0F8 (MSVCRT!__app_type+0x4)
DebugInfo = 0x6A262080
NOT LOCKED
LockSemaphore = 0x0
SpinCount = 0x0
Выгрузить сведения о критическом разделе, используя его адрес, включая трассировку стека инициализации.
0:001> !cs -s 0x7803B0F8
Critical section = 0x7803B0F8 (MSVCRT!__app_type+0x4)
DebugInfo = 0x6A262080
NOT LOCKED
LockSemaphore = 0x0
SpinCount = 0x0
Stack trace for DebugInfo = 0x6A262080:
0x6A2137BD: ntdll!RtlInitializeCriticalSectionAndSpinCount+0x9B
0x6A207A4C: ntdll!LdrpCallInitRoutine+0x14
0x6A205569: ntdll!LdrpRunInitializeRoutines+0x1D9
0x6A20DCE1: ntdll!LdrpInitializeProcess+0xAE5
Вывести сведения о критическом разделе, используя адрес отладочной информации
0:001> !cs -d 0x6A262080
DebugInfo = 0x6A262080
Critical section = 0x7803B0F8 (MSVCRT!__app_type+0x4)
NOT LOCKED
LockSemaphore = 0x0
SpinCount = 0x0
Вывести информацию о критическом разделе, используя адрес отладочной информации, включая трассировку стека инициализации.
0:001> !cs -s -d 0x6A262080
DebugInfo = 0x6A262080
Critical section = 0x7803B0F8 (MSVCRT!__app_type+0x4)
NOT LOCKED
LockSemaphore = 0x0
SpinCount = 0x0
Stack trace for DebugInfo = 0x6A262080:
0x6A2137BD: ntdll!RtlInitializeCriticalSectionAndSpinCount+0x9B
0x6A207A4C: ntdll!LdrpCallInitRoutine+0x14
0x6A205569: ntdll!LdrpRunInitializeRoutines+0x1D9
0x6A20DCE1: ntdll!LdrpInitializeProcess+0xAE
Выгрузить информацию обо всех активных критических секциях в текущем процессе
0:001> !cs
-----------------------------------------
DebugInfo = 0x6A261D60
Critical section = 0x6A262820 (ntdll!RtlCriticalSectionLock+0x0)
LOCKED
LockCount = 0x0
OwningThread = 0x460
RecursionCount = 0x1
LockSemaphore = 0x0
SpinCount = 0x0
-----------------------------------------
DebugInfo = 0x6A261D80
Critical section = 0x6A262580 (ntdll!DeferedCriticalSection+0x0)
NOT LOCKED
LockSemaphore = 0x7FC
SpinCount = 0x0
-----------------------------------------
DebugInfo = 0x6A262600
Critical section = 0x6A26074C (ntdll!LoaderLock+0x0)
NOT LOCKED
LockSemaphore = 0x0
SpinCount = 0x0
.....
Дамп информации о всех активных критических секциях текущего процесса, включая трассировку стека инициализации
0:001> !cs -s
...
-----------------------------------------
DebugInfo = 0x6A261EA0
Critical section = 0xA8001C (+0xA8001C)
NOT LOCKED
LockSemaphore = 0x0
SpinCount = 0x0
No stack trace saved
-----------------------------------------
DebugInfo = 0x6A261EC0
Critical section = 0x6A263560 (ntdll!RtlpDphTargetDllsLock+0x0)
NOT LOCKED
LockSemaphore = 0x0
SpinCount = 0x0
No stack trace saved
-----------------------------------------
DebugInfo = 0x6A261EE0
Critical section = 0xA90608 (+0xA90608)
NOT LOCKED
LockSemaphore = 0x7EC
SpinCount = 0x0
Stack trace for DebugInfo = 0x6A261EE0:
0x6A2137BD: ntdll!RtlInitializeCriticalSectionAndSpinCount+0x9B
0x6A20B0DC: ntdll!CsrpConnectToServer+0x1BE
0x6A20B2AA: ntdll!CsrClientConnectToServer+0x148
0x77DBE83F: KERNEL32!BaseDllInitialize+0x11F
0x6A207A4C: ntdll!LdrpCallInitRoutine+0x14
0x6A205569: ntdll!LdrpRunInitializeRoutines+0x1D9
0x6A20DCE1: ntdll!LdrpInitializeProcess+0xAE5
-----------------------------------------
DebugInfo = 0x6A261F00
Critical section = 0x77E1AEB8 (KERNEL32!BaseDllRegistryCache+0x18)
NOT LOCKED
LockSemaphore = 0x0
SpinCount = 0x0
Stack trace for DebugInfo = 0x6A261F00:
0x6A2137BD: ntdll!RtlInitializeCriticalSectionAndSpinCount+0x9B
0x6A207A4C: ntdll!LdrpCallInitRoutine+0x14
0x6A205569: ntdll!LdrpRunInitializeRoutines+0x1D9
0x6A20DCE1: ntdll!LdrpInitializeProcess+0xAE5
Ошибки отладки исключений
Журнал исключений записывает все исключения, произошедшие в целевом процессе.
Для отображения последних нескольких исключений можно использовать команду расширения !avrf -ex Length; Длина указывает количество исключений. Если длина опущена, отображаются все исключения.
Ниже приведен пример:
0:000> !avrf -ex 4
=================================
Thread ID: 0000052c
Exception code: c0000008
Exception address: 6a226663
Exception record: 0012fb50
Context record: 0012fb64
Displayed 1 exception log entries.
Отладка обрабатывает ошибки
!htrace можно использовать как в отладчике пользовательского режима, так и в отладчике ядра для отображения сведений о стеке вызовов для одного или всех дескрипторов процесса. Эта информация доступна, если трассировка дескриптора включена для процесса, автоматически включена, если проверка дескриптора включена в средство проверки приложений. Трассировки стека сохраняются каждый раз, когда процесс открывает или закрывает дескриптор или когда он ссылается на недопустимый дескриптор. Дополнительные сведения о расширении !htrace см. в документации по отладчику ! htrace .
Синтаксис отладчика ядра для этого расширения:
!htrace [ handle [process] ]
Если дескриптор не указан или равен 0, будут отображаться сведения обо всех дескрипторах процесса. Если процесс не указан, будет использоваться текущий процесс.
Синтаксис отладчика в пользовательском режиме:
!htrace [handle]
Расширение отладчика пользовательского режима всегда отображает сведения о текущем процессе отладки.
Примеры.
Сведения об дескрипторе 7CC в процессе 815328b0
kd> !htrace 7CC 815328b0
Loaded \\...\kdexts extension DLL
Process 0x815328B0
ObjectTable 0xE15ECBB8
--------------------------------------
Handle 0x7CC - CLOSE:
0x8018FCB9: ntoskrnl!ExDestroyHandle+0x103
0x801E1D12: ntoskrnl!ObpCloseHandleTableEntry+0xE4
0x801E1DD9: ntoskrnl!ObpCloseHandle+0x85
0x801E1EDD: ntoskrnl!NtClose+0x19
0x77DBFCD6: KERNEL32!GetLocaleFileInfo+0x3D
0x77DBF942: KERNEL32!NlsProcessInitialize+0x11D
0x77E0C6DF: KERNEL32!NlsDllInitialize+0x35
0x6A20785C: ntdll!LdrpCallInitRoutine+0x14
0x6A205393: ntdll!LdrpRunInitializeRoutines+0x1D9
0x6A20DD80: ntdll!LdrpInitializeProcess+0xAF6
--------------------------------------
Handle 0x7CC - OPEN:
0x8018F44A: ntoskrnl!ExCreateHandle+0x94
0x801E3180: ntoskrnl!ObpCreateHandle+0x304
0x801E1563: ntoskrnl!ObOpenObjectByName+0x1E9
0x77DBFCD6: KERNEL32!GetLocaleFileInfo+0x3D
0x77DBF942: KERNEL32!NlsProcessInitialize+0x11D
0x77E0C6DF: KERNEL32!NlsDllInitialize+0x35
0x6A20785C: ntdll!LdrpCallInitRoutine+0x14
0x6A205393: ntdll!LdrpRunInitializeRoutines+0x1D9
0x6A20DD80: ntdll!LdrpInitializeProcess+0xAF6
--------------------------------------
Parsed 0x1CA stack traces.
Dumped 0x2 stack traces.
Вывод информации обо всех дескрипторах в процессе 815328b0
kd> !htrace 0 81400300
Process 0x81400300
ObjectTable 0xE10CCF60
--------------------------------------
Handle 0x7CC - CLOSE:
0x8018FCB9: ntoskrnl!ExDestroyHandle+0x103
0x801E1D12: ntoskrnl!ObpCloseHandleTableEntry+0xE4
0x801E1DD9: ntoskrnl!ObpCloseHandle+0x85
0x801E1EDD: ntoskrnl!NtClose+0x19
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D
--------------------------------------
Handle 0x7CC - OPEN:
0x8018F44A: ntoskrnl!ExCreateHandle+0x94
0x801E3390: ntoskrnl!ObpCreateUnnamedHandle+0x10C
0x801E7317: ntoskrnl!ObInsertObject+0xC3
0x77DE23B2: KERNEL32!CreateSemaphoreA+0x66
0x010011C5: badhandle!main+0x45
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D
--------------------------------------
Handle 0x7DC - BAD REFERENCE:
0x8018F709: ntoskrnl!ExMapHandleToPointerEx+0xEA
0x801E10F2: ntoskrnl!ObReferenceObjectByHandle+0x12C
0x801902BE: ntoskrnl!NtSetEvent+0x6C
0x80154965: ntoskrnl!_KiSystemService+0xC4
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D
--------------------------------------
Handle 0x7DC - CLOSE:
0x8018FCB9: ntoskrnl!ExDestroyHandle+0x103
0x801E1D12: ntoskrnl!ObpCloseHandleTableEntry+0xE4
0x801E1DD9: ntoskrnl!ObpCloseHandle+0x85
0x801E1EDD: ntoskrnl!NtClose+0x19
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D
--------------------------------------
Handle 0x7DC - OPEN:
0x8018F44A: ntoskrnl!ExCreateHandle+0x94
0x801E3390: ntoskrnl!ObpCreateUnnamedHandle+0x10C
0x801E7317: ntoskrnl!ObInsertObject+0xC3
0x77DE265C: KERNEL32!CreateEventA+0x66
0x010011A0: badhandle!main+0x20
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D
--------------------------------------
Parsed 0x6 stack traces.
Dumped 0x5 stack traces.
Вывод сведений о дескрипторе 7DC в текущем процессе
kd> !htrace 7DC
Process 0x81400300
ObjectTable 0xE10CCF60
--------------------------------------
Handle 0x7DC - BAD REFERENCE:
0x8018F709: ntoskrnl!ExMapHandleToPointerEx+0xEA
0x801E10F2: ntoskrnl!ObReferenceObjectByHandle+0x12C
0x801902BE: ntoskrnl!NtSetEvent+0x6C
0x80154965: ntoskrnl!_KiSystemService+0xC4
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D
--------------------------------------
Handle 0x7DC - CLOSE:
0x8018FCB9: ntoskrnl!ExDestroyHandle+0x103
0x801E1D12: ntoskrnl!ObpCloseHandleTableEntry+0xE4
0x801E1DD9: ntoskrnl!ObpCloseHandle+0x85
0x801E1EDD: ntoskrnl!NtClose+0x19
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D
--------------------------------------
Handle 0x7DC - OPEN:
0x8018F44A: ntoskrnl!ExCreateHandle+0x94
0x801E3390: ntoskrnl!ObpCreateUnnamedHandle+0x10C
0x801E7317: ntoskrnl!ObInsertObject+0xC3
0x77DE265C: KERNEL32!CreateEventA+0x66
0x010011A0: badhandle!main+0x20
0x010012C1: badhandle!mainCRTStartup+0xE3
0x77DE0B2F: KERNEL32!BaseProcessStart+0x3D
--------------------------------------
Parsed 0x6 stack traces.
Dumped 0x3 stack traces.
Отладка ошибок кучи
Расширение отладчика проверки кучи
Расширение проверки кучи отладчика является частью расширения !heap (расширение отладчика кучи NT). Простую справку можно получить с !heap -? или более обширный с !кучей -p -? . Текущее расширение не определяет самостоятельно, включена ли куча страниц для процесса, и не действует соответствующим образом. Теперь пользователю расширения необходимо знать, что куча страниц включена, и он должен использовать команды, префиксированные !heap -p. Дополнительные сведения о расширении !htrace см. в документации отладчика !heap .
!heap -p
Выводит адреса всех полных куч страниц, созданных в процессе.
!heap -p -h ADDRESS-OF-HEAP
Полный дамп полной кучи страниц по адресу ADDRESS-OF-HEAP.
!heap -p -a ADDRESS
Пытается выяснить, есть ли блок кучи в ADDRESS. Это значение не должно быть адресом начала блока. Команда полезна, если нет никаких подсказок о характере области памяти.
Журнал операций кучи
Журнал операций кучи отслеживает все подпрограммы кучи. К ним относятся HeapAlloc, HeapReAlloc и HeapFree.
Команду расширения можно использовать !avrf -hp Length для отображения последних нескольких записей; Длина определяет количество записей.
Вы можете использовать !avrf -hp -a Address для отображения всех операций кучи памяти, которые повлияли на указанный адрес. Для операции выделения достаточно, чтобы адрес содержался в выделенном блоке кучи. Для свободной операции необходимо указать точный адрес начала блока.
Для каждой записи в журнале отображаются следующие сведения:
- Вызывается функция кучи.
- Идентификатор потока, вызывающего подпрограмму.
- Адрес, участвующий в вызове, — это адрес, который был возвращен подпрограммой выделения или передан в бесплатную подпрограмму.
- Размер региона, связанного с вызовом.
- Трассировка стека вызова.
Сначала отображаются последние записи.
В этом примере отображаются две последние записи:
0:001> !avrf -hp 2
alloc (tid: 0xFF4):
address: 00ea2fd0
size: 00001030
00403062: Prymes!_heap_alloc_dbg+0x1A2
00402e69: Prymes!_nh_malloc_dbg+0x19
00402e1e: Prymes!_malloc_dbg+0x1E
00404ff3: Prymes!_stbuf+0xC3
00401c23: Prymes!printf+0x43
00401109: Prymes!main+0xC9
00402039: Prymes!mainCRTStartup+0xE9
77e7a278: kernel32!BaseProcessStart+0x23
alloc (tid: 0xFF4):
address: 00ea07d0
size: 00000830
00403062: Prymes!_heap_alloc_dbg+0x1A2
00402e69: Prymes!_nh_malloc_dbg+0x19
00402e1e: Prymes!_malloc_dbg+0x1E
00403225: Prymes!_calloc_dbg+0x25
00401ad5: Prymes!__initstdio+0x45
00401f38: Prymes!_initterm+0x18
00401da1: Prymes!_cinit+0x21
00402014: Prymes!mainCRTStartup+0xC4
77e7a278: kernel32!BaseProcessStart+0x23
Типичные сценарии отладки
Существует несколько сценариев сбоя, которые могут возникнуть. Некоторые из них требуют значительной детективной работы, чтобы получить полную картину.
Нарушение доступа на недоступной странице
Это происходит, когда включен полный объём кучи страниц, если тестируемое приложение обращается за пределы буфера. Это также может произойти, если он касается освобожденного блока. Чтобы понять характер адреса, в котором произошло исключение, необходимо использовать следующее:
!heap –p –a ADDRESS-OF-AV
Поврежденное сообщение блока
Во несколько этапов во время существования выделения (выделение, освобождение пользователем, фактическое освобождение) менеджер стека страниц проверяет, имеет ли блок все паттерны заполнения нетронутыми, а заголовок блока содержит корректные данные. Если это не так, вы получите остановку верификатора.
Если блок представляет собой полноблочный блок кучи страниц (например, если вы точно знаете, что полная страничная куча включена для всех выделений), то вы можете использовать команду "!heap –p –a АДРЕС", чтобы узнать характеристики этого блока.
Если блок является легким блоком кучи страниц, необходимо определить начальный адрес заголовка блока. Начальный адрес можно найти, выполнив дамп 30–40 байт ниже указанного адреса и найдя специальные сигнатуры начала/конца заголовка блока (ABCDAAAA, ABCDBBBB, ABCDAAA9, ABCDBBBA).
Заголовок предоставит все сведения, необходимые для понимания сбоя. В частности, магические шаблоны будут указывать, выделен блок или свободен, если это упрощённая страничная куча или полная страничная куча. Сведения здесь должны быть тщательно сопоставлены с проблемным вызовом.
Например, если вызов к HeapFree выполняется с адресом блока плюс четыре байта, вы получите поврежденное сообщение. Заголовок блока будет выглядеть хорошо, но вам придется заметить, что первый байт после конца заголовка (первый байт после магического значения 0xDCBAXXXXXX) имеет другой адрес, чем адрес в вызове.
Специальные указатели заполнения
Диспетчер куч страницы заполняет выделение памяти пользователем значениями, которые будут выглядеть как указатели ядра. Это происходит, когда блок освобождается (значение памяти равно F0), и когда блок выделяется, но запрос на обнуление блока не выполняется (значение памяти — E0 для легкой загрузки страницы и C0 для полной загрузки страницы). Вынесенные выделения памяти с ненулевыми значениями являются типичными для пользователей malloc/new. Если возникает сбой (нарушение доступа), когда попытка чтения и записи выполняется по таким адресам, как F0F0F0F0, E0E0E0E0, C0C0C0C0, скорее всего, вы попали в один из этих случаев.
Операция чтения/записи на F0F0F0F0 означает, что блок был использован после его освобождения. К сожалению, вам потребуется провести некоторую детективную работу, чтобы узнать, какой блок вызвал это. Необходимо получить трассировку стека сбоя, а затем проверить код для функций в стеке. Один из них может ошибочно предположить, что выделение все еще активно.
Чтение и запись в E0E0E0E0/C0C0C0C0 означает, что приложение не правильно инициализировало выделение памяти. Для этого также требуется проверка кода функций в текущей трассировке стека. Ниже приведен пример такого рода сбоя. В ходе тестового процесса было замечено нарушение доступа при выполнении HeapFree на адресе E0E0E0E0. Оказалось, что тест выделил структуру, не инициализировал её правильно, а затем вызвал деструктор объекта. Поскольку определенное поле не было null (оно содержало E0E0E0E0), оно вызывало удаление.
Технические сведения о Page Heap (куча страниц)
Чтобы обнаружить повреждения кучи (переполнения или недостатки), AppVerifier изменит способ выделения памяти, добавляя к запрошенной памяти полные незаписываемые страницы или специальные теги перед и после выделенной памяти. AppVerifier делает это путем загрузки Verifier.dll в проверяемый процесс и перенаправления некоторых Win32 Heap API, вызываемых приложением, к соответствующим API Verifier.dll.
При заполнении запрошенной памяти незаписываемыми страницами в полном объеме (параметр FULL активирован в разделе свойств страницы кучи и является параметром по умолчанию), AppVerifier будет использовать большой объем виртуальной памяти, но имеет преимущество, что события повреждения кучи кэшируются в режиме реального времени при переполнении или недозаполнении. Помните, что память в этом режиме будет выглядеть следующим образом: [AppVerifier Read-Only страница кучи (4k)] [Объем памяти, запрошенный приложением в ходе тестирования], или так: [Объем памяти, запрошенный приложением в ходе тестирования] [AppVerifier Read-Only страница кучи (4k)].
Проверка кучи будет размещать страницу защиты в начале или конце выделения в зависимости от свойства "Назад". Если параметр Backward имеет значение False, что является значением по умолчанию, то будет размещена страница защиты в конце выделенной памяти для обнаружения переполнения буфера. Если установлено значение True, страница охранника размещается в начале выделения для обнаружения ошибок недогрузки буфера.
При добавлении специальных тегов в запрашиваемую память (активируется снятием отметки с флажка "Full" в свойствах кучи), AppVerifier будет проверять и оповещать вас при освобождении этой памяти. Основная проблема в использовании этого метода заключается в том, что в некоторых случаях повреждение памяти будет обнаружено только при освобождении памяти (минимальный объем блока памяти составляет 8 байт), поэтому при возникновении 3-байтовой переменной или 5-байтового переполнения он не будет немедленно обнаружен.
При возникновении переполнения будет предпринята попытка записать данные на страницу Read-Only. Это приведет к возникновению исключения. Обратите внимание, что это исключение может быть поймано только в том случае, если целевое приложение выполняется под отладчиком. Обратите внимание, что полный режим кучи страниц также обнаружит эти ошибки, так как использует страницы padding+guard. Причина, по которой вы будете использовать лёгкую кучу страниц, заключается в том, если вашему компьютеру сложно справиться с высокими требованиями к памяти полной кучи страниц.
Для приложений с интенсивным объемом памяти или при необходимости использовать AppVerifier в течение длительных периодов времени (например, стресс-тестирование), лучше выполнять обычные (легкие) кучи тесты вместо полного режима из-за снижения производительности. Однако при возникновении проблемы включите полную кучу страниц для дальнейшего изучения.
Приложения, использующие настраиваемые кучи (кучи, которые обходят стандартную реализацию кучи операционной системы), могут не получить полного преимущества от использования режима страницы кучи или даже выйти из строя при его включении.
Отладка ошибок памяти
Расширение отладчика проверки памяти
Журнал операций виртуального пространства отслеживает все подпрограммы, которые изменяют виртуальное пространство процесса каким-либо образом. К ним относятся VirtualAlloc, VirtualFree, MapViewOfFile и UnmapViewOfFile.
Команду расширения можно использовать !avrf -vs Length для отображения последних нескольких записей; Длина определяет количество записей.
Вы можете использовать !avrf -vs -a Address для отображения всех операций виртуального пространства, затронутых указанным адресом. Для выделения достаточно, чтобы Адрес содержался в выделенном блоке. Для бесплатной версии необходимо укажите точный адрес начала региона.
Для каждой записи в журнале отображаются следующие сведения:
- Вызываемая функция
- Идентификатор потока, вызывающего подпрограмму
- Адрес, участвующий в вызове, — это адрес, возвращенный подпрограммой выделения или переданный в бесплатную подпрограмму.
- Размер региона, связанного с вызовом
- Тип операции памяти (параметр AllocationType)
- Тип запрошенной защиты
- Трассировка стека вызова
Примеры
Сначала отображаются последние записи.
В следующем примере отображаются две последние записи:
0:001> !avrf -vs 2
VirtualFree (tid: 0xB4): addr:04bb0000 sz:00400000 op:8000 prot:0
00aa1ac2: verifier!VsLogCall+0x42
00aa19c1: verifier!AVrfpNtFreeVirtualMemory+0x30
68925d17: kernel32!VirtualFreeEx+0x35
6892611c: kernel32!VirtualFree+0x13
75ef6525: mshtml+0x116525
75ef68af: mshtml+0x1168AF
6a20787c: ntdll!LdrpCallInitRoutine+0x14
6a211c6f: ntdll!LdrUnloadDll+0x39A
689275c1: kernel32!FreeLibrary+0x3B
77b22d69: ole32!CoQueryReleaseObject+0x1E6
77b02bd2: ole32!SetErrorInfo+0x1ED
VirtualFree (tid: 0xB4): addr:04bb0000 sz:00001000 op:4000 prot:0
00aa1ac2: verifier!VsLogCall+0x42
00aa19c1: verifier!AVrfpNtFreeVirtualMemory+0x30
68925d17: kernel32!VirtualFreeEx+0x35
6892611c: kernel32!VirtualFree+0x13
75ef65ae: mshtml+0x1165AE
75ef68af: mshtml+0x1168AF
6a20787c: ntdll!LdrpCallInitRoutine+0x14
6a211c6f: ntdll!LdrUnloadDll+0x39A
689275c1: kernel32!FreeLibrary+0x3B
77b22d69: ole32!CoQueryReleaseObject+0x1E6
77b02bd2: ole32!SetErrorInfo+0x1ED
Из выходных данных видно, что поток 0xB4 сначала отменил выделение страницы, а затем освободил весь виртуальный регион.
Ниже приведено отображение всех операций, влияющих на адрес 0x4BB1000:
0:001> !avrf -vs -a 4bb1000
Searching in vspace log for address 04bb1000 ...
VirtualFree (tid: 0xB4): addr:04bb0000 sz:00400000 op:8000 prot:0
00aa1ac2: verifier!VsLogCall+0x42
00aa19c1: verifier!AVrfpNtFreeVirtualMemory+0x30
68925d17: kernel32!VirtualFreeEx+0x35
6892611c: kernel32!VirtualFree+0x13
75ef6525: mshtml+0x116525
75ef68af: mshtml+0x1168AF
6a20787c: ntdll!LdrpCallInitRoutine+0x14
6a211c6f: ntdll!LdrUnloadDll+0x39A
689275c1: kernel32!FreeLibrary+0x3B
77b22d69: ole32!CoQueryReleaseObject+0x1E6
77b02bd2: ole32!SetErrorInfo+0x1ED
VirtualFree (tid: 0xB4): addr:04bb1000 sz:00001000 op:4000 prot:0
00aa1ac2: verifier!VsLogCall+0x42
00aa19c1: verifier!AVrfpNtFreeVirtualMemory+0x30
68925d17: kernel32!VirtualFreeEx+0x35
6892611c: kernel32!VirtualFree+0x13
75ef65ae: mshtml+0x1165AE
75ef68af: mshtml+0x1168AF
6a20787c: ntdll!LdrpCallInitRoutine+0x14
6a211c6f: ntdll!LdrUnloadDll+0x39A
689275c1: kernel32!FreeLibrary+0x3B
77b22d69: ole32!CoQueryReleaseObject+0x1E6
77b02bd2: ole32!SetErrorInfo+0x1ED
VirtualAlloc (tid: 0xB4): addr:04bb0000 sz:00010000 op:1000 prot:4
00aa1ac2: verifier!VsLogCall+0x42
00aa1988: verifier!AVrfpNtAllocateVirtualMemory+0x37
68925ca3: kernel32!VirtualAllocEx+0x61
68926105: kernel32!VirtualAlloc+0x16
75ef63f3: mshtml+0x1163F3
VirtualAlloc (tid: 0xB4): addr:04bb0000 sz:00400000 op:2000 prot:4
00aa1ac2: verifier!VsLogCall+0x42
00aa1988: verifier!AVrfpNtAllocateVirtualMemory+0x37
68925ca3: kernel32!VirtualAllocEx+0x61
68926105: kernel32!VirtualAlloc+0x16
75ef63d9: mshtml+0x1163D9
Чтобы прочитать эти выходные данные, помните, что записи сбрасываются начиная с последнего. В журнале показано, что поток 0xB4 выделил большую область, в которой он зафиксировал страницу. Позже он отозвал страницу, а затем выпустил весь виртуальный регион.
См. также
средство проверки приложений — обзор
средство проверки приложений — тестирование приложений
средство проверки приложений — тесты в проверяющего приложения
средство проверки приложений — остановки кодов и определений
средство проверки приложений — часто задаваемые вопросы