Windbg
+ -

Windbg 自动分析!analyze

2021-07-01 285 0

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

若要数据的全冗长模式显示,你应该使用 -v 选项。

例如: !analyze -v

用户模式 !analyze -v 示例

本例中,调试器被附加到一个已遭遇异常的用户模式应用程序。

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

Debugger SolutionDb Connection::Open failed 80004005

如果计算机有连接英特网,调试器尝试访问一个由微软维护的当机解决方案数据库。这里它显示了一个错误信息,指出或是你的机器不能够访问英特网或是网站关闭。

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 check这个术语实际上表示是一个内核模式当机。在用户模式调试中将显示异常代码 - 这里是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和DBG_FLR_IMAGE_TIMESTAMP字段表示这个指令相应的符号、模块、映像名字和映像时间戳。

STACK_COMMAND:  .ecxr ; kb

STACK_COMMAND字段表示用来获取STACK_TEXT的命令。你可以使用这个指令重复显示这个栈跟踪,或者改变它以获得有关的栈信息。

BUCKET_ID: 80000003_MyApp!FatalErrorBox+55

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

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

关于FOLLOWUP_NAME和Followup字段的信息,请看”Followup字段和triage.ini文件” The Followup Field and the triage.ini File。

还可能出现其他一些字段:

  1. 如果控制被转移给一个无效地址,那么FAULTING_IP字段将会包含有这个无效地址。不是FOLLOWUP_IP字段,而是FAILED_INSTRUCTION_ADDRESS字段将显示该地址上的反汇编码,虽然反汇编码可能是无意义的。在这种情形下,SYMBOL_NAME、MODULE_NAME、IMAGE_NAME和DBG_FLR_IMAGE_TIMESTAMP字段将指出这条指令的调用者。
  2. 如果处理器失败,你可能会看到SINGLE_BIT_ERROR、TWO_BIT_ERROR或POSSIBLE_INVALID_CONTROL_TRANSFER字段。
  3. 如果内存崩溃看起来已经发生,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 Check Code Reference部分。

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

其次显示隐错检查参数。在每个参数之后都有描述。例如,第三个参数是1,它后面的注释解释这是表示一个写操作失败。

Debugging Details:
------------------

WRITE_ADDRESS:  00000004 Nonpaged pool

CURRENT_IRQL:  2

下面几个字段因当机类型而不同。这里,我们看到WRITE_ADDRESS和CURRENT_IRQL字段。这些只是再次叙述在隐错检查参数中显示的信息。通过比较这句“非页池(Nonpaged pool)”和隐错检查文本“尝试存取一个可分页的(pagable) (或完全无效的)地址”,我们能够看出该地址是无效的。在这里,该无效地址是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字段表示隐错检查代码,我们已经见到过。有些情况还附带额外的优选(triage)信息。

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字段表示当前故障所属的特定故障类别。这个类别帮助调试器确定在分析输出中显示的其他信息。

INTERNAL_SOLUTION_TEXT: http://oca.microsoft.com/resredir.asp?sid=62&State=1

如果计算机有连接英特网,调试器尝试访问一个由微软维护的当机解决方案数据库。这个数据库包含大量有关已知隐错信息网页的链接。如果找到一个与你的问题相匹配的,INTERNAL_SOLUTION_TEXT字段将显示一个让你可以获取更多信息的网址。

Followup: usbtri
---------

      This problem has a known fix.
      Please connect to the following URL for details:
      ------------------------------------------------
      http://oca.microsoft.com/resredir.asp?sid=62&State=1

关于FOLLOWUP_NAME和Followup字段的信息,请看”Followup字段和triage.ini文件” The Followup Field and the triage.ini File:

还可能出现其他一些字段:

  1. 如果控制被转移给一个无效地址,那么FAULTING_IP字段将会包含有这个无效地址。不是FOLLOWUP_IP字段,而是FAILED_INSTRUCTION_ADDRESS字段将显示该地址上的反汇编码,虽然反汇编码可能是无意义的。在这种情形下,SYMBOL_NAME、MODULE_NAME、IMAGE_NAME和DBG_FLR_IMAGE_TIMESTAMP字段将指出这条指令的调用者。
  2. 如果处理器失败,你可能会看到SINGLE_BIT_ERROR、TWO_BIT_ERROR或POSSIBLE_INVALID_CONTROL_TRANSFER字段。
  3. 如果内存崩溃看起来已经发生,CHKIMG_EXTENSION字段将说明应该使用 !chkimg 扩展命令来调查。
  4. 如果在一个驱动程序的代码里面发生隐错检查,它的名字可能会在BUGCHECKING_DRIVER字段中显示。

Followup字段和triage.ini文件

在用户模式和内核模式中,如果能够确定,显示的Followup字段都将表示当前栈框架属主的有关信息。该信息是以下面方式确定:

  1. 当!analyze 扩展被使用的时候,调试器从栈中最顶的框架开始,确定它是否对错误负责。如果不是,就分析下一个框架。这个过程继续直到找出一个可能出错的框架。
  2. 调试器尝试确定这个框架里模块及函数的属主。如果属主能够被确定,就认为这个框架有错。
  3. 如果属主不能够被确定,调试器转到下一个栈框架,等等,直到属主被确定 (或者栈全部检查完)。搜索中第一个找到属主的框架被认为有错。如果栈完了还没有找到的任何信息,则不显示Followup字段。
  4. 故障框架的属主在Followup字段中显示。如果使用 !analyze -v,那么FOLLOWUP_IP、SYMBOL_NAME、MODULE_NAME、IMAGE_NAME和DBG_FLR_IMAGE_TIMESTAMP字段将会指出这个框架。

若要让Followup字段显示有用的信息,你必须先创建一个包含模块和函数属主名字的triage.ini文件。
triage.ini文件应该标识出所有可能出错模块的属主。你可以使用一个信息字符串而不是使用实际的属主,但是这个字符串不能够包含空格。如果你确信一个模块不会有故障,那么可以省略这个模块或指示应该略过它。给出一个较好的优选处理尺度,指定个别函数的属主也是可能的。


辅助的 !analyze 技术

如果认为BUCKET_ID是不正确的,你可以使用带 -D 参数 !analyze 重写存储桶选择(bucket choice)。

如果没有发生当机或异常,!analyze 将会显示一个很简短的文本给出目标的当前状态。在某种情况下,你可能想强迫它象在发生当机的情况下一样进行分析。使用 !analyze - f 完成这个任务。

在用户模式中,如果一个异常已经发生,而你认为问题潜在于一个挂起的线程,那么把当前线程设置为你要调查的线程,然后使用 !analyze -hang。这个扩展将会执行一个线程栈分析以确定是不是有哪些线程正在阻塞其他线程。

在内核模式中,如果一个隐错检查已经发生,而你认为问题潜在于一个挂起的线程,那么使用 !analyze -hang。这个扩展将会调查系统所持有的锁,并且扫描 DPC 队列链,然后将显示任何挂起线程的指示。如果你认为问题在于一个内核模式的资源死锁,那么与驱动程序验证器(Driver Verifier)的死锁检测Deadlock Detection选项一起使用 !deadlock 扩展。

0 篇笔记 写笔记

作者信息
我爱内核
Windows驱动开发,网站开发
好好学习,天天向上。
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!