使用 !analyze 扩展

调试崩溃的目标计算机或应用程序的第一步是使用 !analyze 扩展命令。 此扩展执行大量的自动分析。 此分析的结果显示在“调试器命令”窗口中。

应使用 -v 选项完全详细地显示数据。 有关其他选项的详细信息,请参阅 !analyze 参考页。

User-Mode !analyze -v 示例

在此示例中,调试器附加到遇到异常的用户模式应用程序。

0:000> !analyze -v
*******************************************************************************
*                                                                             *
*                        Exception Analysis                                   *
*                                                                             *
*******************************************************************************

Debugger SolutionDb Connection::Open failed 80004005

如果连接到 Internet,调试器将尝试访问由 Microsoft 维护的故障解决方案数据库。 在这种情况下,将显示一条错误消息,指示计算机无法访问 Internet 或网站无法正常工作。

FAULTING_IP: 
ntdll!PropertyLengthAsVariant+73
77f97704 cc               int     3

FAULTING_IP字段显示故障时的指令指针。

EXCEPTION_RECORD:  ffffffff -- (.exr ffffffffffffffff)
ExceptionAddress: 77f97704 (ntdll!PropertyLengthAsVariant+0x00000073)
   ExceptionCode: 80000003 (Break instruction exception)
  ExceptionFlags: 00000000
NumberParameters: 3
   Parameter[0]: 00000000
   Parameter[1]: 00010101
   Parameter[2]: ffffffff

“EXCEPTION_RECORD”字段显示此故障的异常记录。 也可以使用 .exr (显示异常记录) 命令查看此信息。

BUGCHECK_STR:  80000003

“BUGCHECK_STR”字段显示异常代码。 该名称用词不当, 术语 bug 检查实际上表示内核模式崩溃。 在用户模式调试中,将显示异常代码,在本例中为0x80000003。

DEFAULT_BUCKET_ID:  APPLICATION_FAULT

“DEFAULT_BUCKET_ID”字段显示此故障所属的常规故障类别。

PROCESS_NAME:  MyApp.exe

PROCESS_NAME字段指定引发异常的进程的名称。

LAST_CONTROL_TRANSFER:  from 01050963 to 77f97704

“LAST_CONTROL_TRANSFER”字段显示堆栈上的最后一次调用。 在这种情况下,位于地址的代码0x01050963 0x77F97704调用函数。 可以将这些地址与 ln (List Nearest Symbols) 命令一起使用,以确定这些地址所在的模块和函数。

STACK_TEXT:  
0006b9dc 01050963 00000000 0006ba04 000603fd ntdll!PropertyLengthAsVariant+0x73
0006b9f0 010509af 00000002 0006ba04 77e1a449 MyApp!FatalErrorBox+0x55 [D:\source_files\MyApp\util.c @ 541]
0006da04 01029f4e 01069850 0000034f 01069828 MyApp!ShowAssert+0x47 [D:\source_files\MyApp\util.c @ 579]
0006db6c 010590c3 000e01ea 0006fee4 0006feec MyApp!SelectColor+0x103 [D:\source_files\MyApp\colors.c @ 849]
0006fe04 77e11d0a 000e01ea 00000111 0000413c MyApp!MainWndProc+0x1322 [D:\source_files\MyApp\MyApp.c @ 1031]
0006fe24 77e11bc8 01057da1 000e01ea 00000111 USER32!UserCallWinProc+0x18
0006feb0 77e172b4 0006fee4 00000001 010518bf USER32!DispatchMessageWorker+0x2d0
0006febc 010518bf 0006fee4 00000000 01057c5d USER32!DispatchMessageA+0xb
0006fec8 01057c5d 0006fee4 77f82b95 77f83920 MyApp!ProcessQCQPMessage+0x3b [D:\source_files\MyApp\util.c @ 2212]
0006ff70 01062cbf 00000001 00683ed8 00682b88 MyApp!main+0x1e6 [D:\source_files\MyApp\MyApp.c @ 263]
0006ffc0 77e9ca90 77f82b95 77f83920 7ffdf000 MyApp!mainCRTStartup+0xff [D:\source_files\MyApp\crtexe.c @ 338]
0006fff0 00000000 01062bc0 00000000 000000c8 KERNEL32!BaseProcessStart+0x3d

“STACK_TEXT”字段显示故障组件的堆栈跟踪。

FOLLOWUP_IP: 
MyApp!FatalErrorBox+55
01050963 5e               pop     esi

FOLLOWUP_NAME:  dbg

SYMBOL_NAME:  MyApp!FatalErrorBox+55

MODULE_NAME:  MyApp

IMAGE_NAME:  MyApp.exe

DEBUG_FLR_IMAGE_TIMESTAMP:  383490a9

!analyze 确定可能导致错误的指令时,它会将其显示在FOLLOWUP_IP字段中。 SYMBOL_NAME、MODULE_NAME、IMAGE_NAME和DEBUG_FLR_IMAGE_TIMESTAMP字段显示与此指令对应的符号、模块、映像名称和图像时间戳。

STACK_COMMAND:  .ecxr ; kb

“STACK_COMMAND”字段显示用于获取STACK_TEXT的命令。 可以使用此命令重复此堆栈跟踪显示,或更改它以获取相关的堆栈信息。

BUCKET_ID:  80000003_MyApp!FatalErrorBox+55

“BUCKET_ID”字段显示当前故障所属的特定故障类别。 此类别可帮助调试器确定在分析输出中要显示的其他信息。

Followup: dbg
---------

有关FOLLOWUP_NAME和后续字段的信息,请参阅 跟进字段和 triage.ini 文件

可能会出现各种其他字段:

  • 如果将控制权转移到无效地址,则FAULTING_IP字段将包含此无效地址。 FAILED_INSTRUCTION_ADDRESS字段将显示此地址中的反汇编代码,而不是FOLLOWUP_IP字段,尽管此反汇编可能毫无意义。 在这种情况下,SYMBOL_NAME、MODULE_NAME、IMAGE_NAME和DEBUG_FLR_IMAGE_TIMESTAMP字段将引用此指令的调用方。

  • 如果处理器发生误火,你可能会看到SINGLE_BIT_ERROR、TWO_BIT_ERROR或POSSIBLE_INVALID_CONTROL_TRANSFER字段。

  • 如果内存损坏似乎已发生,CHKIMG_EXTENSION字段将指定用于调查的 !chkimg 扩展命令。

Kernel-Mode !analyze -v 示例

在此示例中,调试器连接到刚刚崩溃的计算机。

kd> !analyze -v
*******************************************************************************
*                                                                             *
*                        Bugcheck Analysis                                    *
*                                                                             *
*******************************************************************************

DRIVER_IRQL_NOT_LESS_OR_EQUAL (d1)
An attempt was made to access a pagable (or completely invalid) address at an
interrupt request level (IRQL) that is too high.  This is usually
caused by drivers using improper addresses.
If kernel debugger is available get stack backtrace.

显示的第一个元素显示 bug 检查代码以及有关此类 bug 检查的信息。 显示的某些文本可能不适用于此特定实例。 有关每个 bug 检查的详细信息,请参阅 Bug 检查代码参考部分。

Arguments:
Arg1: 00000004, memory referenced
Arg2: 00000002, IRQL
Arg3: 00000001, value 0 = read operation, 1 = write operation
Arg4: f832035c, address which referenced memory

接下来会显示 bug 检查 参数。 它们分别后跟说明。 例如,第三个参数为 1,它后面的注释说明这表示写入操作失败。

## Debugging Details:


WRITE_ADDRESS:  00000004 Nonpaged pool

CURRENT_IRQL:  2

接下来的几个字段因崩溃的性质而异。 在本例中,我们看到WRITE_ADDRESS和CURRENT_IRQL字段。 这些只是重述 bug 检查 参数中显示的信息。 通过将语句“非分页池”与 bug 进行比较,检查文本显示“尝试访问可分页 (或) 地址完全无效”,我们可以看到地址无效。 在这种情况下,0x00000004无效地址。

FAULTING_IP: 
USBPORT!USBPORT_BadRequestFlush+7c
f832035c 894204           mov     [edx+0x4],eax

FAULTING_IP字段显示故障时的指令指针。

DEFAULT_BUCKET_ID:  DRIVER_FAULT

“DEFAULT_BUCKET_ID”字段显示此故障所属的常规故障类别。

BUGCHECK_STR:  0xD1

“BUGCHECK_STR”字段显示我们已经看到的 bug 检查代码。 在某些情况下,会追加其他会审信息。

TRAP_FRAME:  f8950dfc -- (.trap fffffffff8950dfc)
.trap fffffffff8950dfc
ErrCode = 00000002
eax=81cc86dc ebx=81cc80e0 ecx=81e55688 edx=00000000 esi=81cc8028 edi=8052cf3c
eip=f832035c esp=f8950e70 ebp=f8950e90 iopl=0         nv up ei pl nz ac po nc
cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00010216
USBPORT!USBPORT_BadRequestFlush+7c:
f832035c 894204           mov     [edx+0x4],eax     ds:0023:00000004=????????
.trap
Resetting default context

TRAP_FRAME字段显示此崩溃的陷阱帧。 也可以使用 .trap (显示陷阱帧) 命令查看此信息。

LAST_CONTROL_TRANSFER:  from f83206e0 to f832035c

“LAST_CONTROL_TRANSFER”字段显示堆栈上的最后一次调用。 在这种情况下,位于地址的代码0xF83206E0 0xF832035C调用函数。 可以使用 ln (List Nearest Symbols) 命令来确定这些地址驻留在哪个模块和函数中。

STACK_TEXT:  
f8950e90 f83206e0 024c7262 00000000 f8950edc USBPORT!USBPORT_BadRequestFlush+0x7c
f8950eb0 804f5561 81cc8644 81cc8028 6d9a2f30 USBPORT!USBPORT_DM_TimerDpc+0x10c
f8950fb4 804f5644 6e4be98e 00000000 ffdff000 nt!KiTimerListExpire+0xf3
f8950fe0 8052c47c 8053cf20 00000000 00002e42 nt!KiTimerExpiration+0xb0
f8950ff4 8052c16a efdefd44 00000000 00000000 nt!KiRetireDpcList+0x31

“STACK_TEXT”字段显示故障组件的堆栈跟踪。

FOLLOWUP_IP: 
USBPORT!USBPORT_BadRequestFlush+7c
f832035c 894204           mov     [edx+0x4],eax

“FOLLOWUP_IP”字段显示可能导致错误的指令的反汇编。

FOLLOWUP_NAME:  usbtri

SYMBOL_NAME:  USBPORT!USBPORT_BadRequestFlush+7c

MODULE_NAME:  USBPORT

IMAGE_NAME:  USBPORT.SYS

DEBUG_FLR_IMAGE_TIMESTAMP:  3b7d868b

SYMBOL_NAME、MODULE_NAME、IMAGE_NAME和DBG_FLR_IMAGE_TIMESTAMP字段显示与此指令 ((如果有效) )对应的符号、模块、图像和图像时间戳;如果指令 (未) ,则显示此指令的调用方。

STACK_COMMAND:  .trap fffffffff8950dfc ; kb

“STACK_COMMAND”字段显示用于获取STACK_TEXT的命令。 可以使用此命令重复此堆栈跟踪显示,或更改它以获取相关的堆栈信息。

BUCKET_ID:  0xD1_W_USBPORT!USBPORT_BadRequestFlush+7c

“BUCKET_ID”字段显示当前故障所属的特定故障类别。 此类别可帮助调试器确定在分析输出中要显示的其他信息。

有关FOLLOWUP_NAME和后续字段的信息,请参阅 跟进字段和 triage.ini 文件

可能会出现各种其他字段:

  • 如果将控制权转移到无效地址,则FAULTING_IP字段将包含此无效地址。 FAILED_INSTRUCTION_ADDRESS字段将显示此地址中的反汇编代码,而不是FOLLOWUP_IP字段,尽管此反汇编可能毫无意义。 在这种情况下,SYMBOL_NAME、MODULE_NAME、IMAGE_NAME和DBG_FLR_IMAGE_TIMESTAMP字段将引用此指令的调用方。

  • 如果处理器发生误火,你可能会看到SINGLE_BIT_ERROR、TWO_BIT_ERROR或POSSIBLE_INVALID_CONTROL_TRANSFER字段。

  • 如果内存损坏似乎已发生,CHKIMG_EXTENSION字段将指定用于调查的 !chkimg 扩展命令。

  • 如果设备驱动程序的代码中出现 bug 检查,则其名称可能会显示在BUGCHECKING_DRIVER字段中。

跟进字段和 triage.ini 文件

在用户模式和内核模式下,如果可以确定,则显示中的“跟进”字段将显示有关当前堆栈帧所有者的信息。 此信息按以下方式确定:

  1. 使用 !analyze 扩展时,调试器从堆栈中的顶部帧开始,并确定它是否对错误负责。 否则,将分析下一帧。 此过程将一直持续到找到可能存在故障的帧。

  2. 调试器尝试确定此帧中模块和函数的所有者。 如果可以确定所有者,则认为此帧有故障。

  3. 如果无法确定所有者,调试器将传递到下一个堆栈帧,依此推,直到确定所有者 (或) 完全检查堆栈。 在此搜索中找到其所有者的第一个帧被视为有错误。 如果堆栈耗尽而未找到任何信息,则不显示任何后续字段。

  4. 故障时帧的所有者显示在“跟进”字段中。 如果使用 !analyze -v ,则FOLLOWUP_IP、SYMBOL_NAME、MODULE_NAME、IMAGE_NAME和DBG_FLR_IMAGE_TIMESTAMP字段将引用此帧。

要使“跟进”字段显示有用信息,必须首先创建一个包含模块和函数所有者名称的 triage.ini 文件。

triage.ini 文件应标识可能存在错误的所有模块的所有者。 可以使用信息字符串而不是实际所有者,但此字符串不能包含空格。 如果确定某个模块不会出错,则可以省略此模块或指示应跳过该模块。 还可以指定单个函数的所有者,使会审过程更精细。

有关 triage.ini 文件的语法的详细信息,请参阅 指定模块和函数所有者

其他 !analyze 技术

如果未发生崩溃或异常, !analyze 将显示一条非常短的文本,其中提供了目标的当前状态。 在某些情况下,你可能希望强制执行分析,就像发生崩溃一样。 使用 !analyze -f 完成此任务。

在用户模式下,如果发生了异常,但认为根本问题是线程挂起,请将当前线程设置为正在调查的线程,然后使用 !analyze -hang。 此扩展将执行线程堆栈分析,以确定是否有线程正在阻止其他线程。

在内核模式下,如果发生了 bug 检查,但你认为根本问题是挂起的线程,请使用 !analyze -hang。 此扩展将调查系统持有的锁并扫描 DPC 队列链,并显示挂起线程的任何指示。 如果认为问题是内核模式资源死锁,请使用 !死锁 扩展以及驱动程序验证程序的 死锁检测 选项。

还可以自动忽略已知问题。 为此,必须先创建一个 XML 文件,其中包含已知问题的格式化列表。 使用 !analyze -c -loadKnownIssuesFile 扩展来加载此文件。 然后,当发生异常或中断时,请使用 !analyze -c 扩展。 如果异常与已知问题之一匹配,则目标将恢复执行。 如果目标未继续执行,则可以使用 !analyze -v 来确定问题的原因。

另请参阅

有关其他信息,请参阅这些主题。

!analyze

Bug 检查代码参考

使用 Windows 调试器 (WinDbg) 进行故障转储分析

使用 WinDbg 分析内核模式转储文件