使用 !analyze 扩展

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

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

用户模式 !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(列表最近的符号)命令一起使用,以确定这些地址所在的模块和函数。

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 扩展命令。

内核模式 !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 检查参数中显示的信息。 通过将语句“Nonpaged pool”与错误检查文本进行比较,该文本读取了“尝试访问可分页地址(或完全无效)地址”,可以看到地址无效。 本例中无效的地址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(列表最近的符号) 命令来确定这些地址所在的模块和函数。

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 -load KnownIssuesFile 扩展加载此文件。 然后,当发生异常或中断时,请使用 !analyze -c 扩展。 如果异常与某个已知问题匹配,则目标将恢复执行。 如果目标未恢复执行,则可以使用 !analyze -v 来确定问题的原因。

另请参阅

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

!analyze

错误检查代码参考

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

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