通往WinDbg的捷径(二)

windbg 调试崩溃 提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一、pandas是什么? 二、使用步骤 1.引入库 2.读入数据 总结 前言 windbg 是非常强大的调试工具,但是在使用windbg 进修调试时候,很多的命令不知道如何使用。文章简单介绍如何使用windbg进行调试 https://docs.microsoft.com/zh-cn/windows-hardware/drivers/debugger/debugger-download.. 阅读详情

原文:http://www.debuginfo.com/articles/easywindbg2.html
译者:arhat
时间:2006年4月14日
关键词:CDB WinDbg 

保存 dumps 
在我们调试不容易重现的问题时,可能想把应用程序状态的快照(内存内容,打开名柄的列表,等等)保存起来,以便日后分析。例如,当我怀疑当前的状态可能包含我试图解决的问题的关键点,而想继续运行应用程序来查看情形怎样发展时,它就很有用了。有时候,我会做一系列的快照,一个接一个,以便稍后我能比较它们,查看在应用程序运行时有些数据结构怎样变化。当我最终能重现这个问题时,我总是创建一个快照来确保我没有因为某些错误(错误关闭了调试会话)而丢失有价值的信息。或许,大家不难猜到当我说“快照”时,我真正的意思是“minidump”,因为minidump为随时保存应用程序的状态提供了便利。
下面是创建minidump的命令行示例:
  cdb -pv -pn myapp.exe -c ".dump /m c:/myapp.dmp;q"
让我们仔细看一下.dump命令。在上面的例子里,我们只用到这条命令的一个选项(/m),后面跟着minidump的文件名。用/m来指定minidump里应当包括哪种信息。最重要的(依我之见)/m选项的变量列在下表中:
---------------------------------------------------------------------------------------------------------------------------
选项      描述                                                                                              例子
---------------------------------------------------------------------------------------------------------------------------
/m        默认就是这个选项。它创建标准的minidump,等同于MiniDumpNormal minidump类型。由此生成的minidump
          一般很小,因此,如果你想通过慢速的网络传输minidump,那么这个选项非常有用。但不幸地是,小体积的
          minidump也意味着在大多数情况下,它包含的信息不足以进行完整的分析(你可以在这篇文章里找到更多有
          关minidump内容的信息)。                                                                     dump /m c:/myapp.dmp
---------------------------------------------------------------------------------------------------------------------------
/ma       带所有可选项的Minidump(完整的内存内容,名柄,已卸载的模块,等等),由此生成的minidump将非常
          大。如果可以随意使用磁盘空间,这个选项将非常适合本地调试。                               .dump /ma c:/myapp.dmp
---------------------------------------------------------------------------------------------------------------------------
/mFhutwd  这个选项将生成带数据段,非共享读/写内存页和其它有用信息的minidump。如果你想尽可能的收集信息,
          但仍想使minidump保持小体积(并压缩),就可以用这个选项。                                .dump /mFhutwd c:/myapp.dmp
---------------------------------------------------------------------------------------------------------------------------
下面的命令生成包含所有信息的minidump:
  cdb -pv -pn myapp.exe -c ".dump /ma c:/myapp.dmp;q"
如果我们想生成一个新minidump,并覆盖已有的,该怎么办呢?在默认情况下,.dump命令不允许这样做??它会抱怨文件已经存在。为了改变默认行为,覆盖已存在的.dump文件,我们可以用/o选项:
  cdb -pv -pn myapp.exe -c ".dump /ma /o c:/myapp.dmp;q"
如果我们想生成一系列的minidump,一个接一个,那么它能很方便的为minidump命名,并使文件名反映生成minidump时的时间吗。嗯,如果我们指定了/u选项,.dump命令就可以自动为我们这样做,这真是一个好消息,不是吗?例如,下面的命令可以生成名为myapp_02CC_2006-01-28_04-11-18-171_0158.dmp的minidump(0158是进程ID):
  cdb -pv -pn myapp.exe -c ".dump /m /u c:/myapp.dmp;q"
.dump命令也支持其它有趣的选项(你可以在文档里发现它们)。
如果你想生成运行在Visual Studio调试器下的进程的minidump,我建议在生成dump前,先在Visual Studio里临时禁用所有的断点。如果没有禁用断点,生成的minidump将包含Visual Studio调试器插入目标进程代码里的断点指令(int 3)。

分析故障转储
CDB也可以用于自动分析故障转储。当我们分析故障转储时,通常会执行同样的操作,所以可以把这些操作自动化。什么样的操作呢?这要看故障转储的类型。我把所有的故障转储分成两大类:
•  带异常信息的故障转储
•  不带异常信息的故障转储
当应用程序引发未经处理的异常并调用just-in-time调试器(Dr. Watson,NTSD  , 或其它的调试器),或者用为未经处理的异常定制的过滤器 生成minidump时,通常会生成带异常信息的故障转储。通过写入故障转储里的异常信息,我们可以确定异常的类型和发生时它在代码里的位置。当我们想为以后的分析生成进程的快照时(例如,这方面的描述参见本文的前一部分“保存dumps”),通常手动生成不带异常信息的故障转储。
当我们调试带异常信息的故障转储时,通常想知道下面这些信息:
•  异常在代码中出现的位置(地址,源文件和行号)
•  异常发生时的调用栈
•  调用栈上一些或所有函数的参数值和局部变量
WinDbg和CDB为调试故障转储提供了非常有用的命令??!analyze。这条命令分析故障转储里的异常信息,确定异常发生的位置,调用栈,并显示详细的报告。下面是这条命令的示例:
  cdb -z c:/myapp.dmp -logo out.txt -lines -c "!analyze -v;q"
(-v选项要求!analyze输出详细的内容)
CrashDemo.cpp 例子演示了怎样用定制的过滤器捕获未经处理的异常并生成minidumps。如果你编译并运行它,然后用上述的CDB命令分析生成的minidump,你将可以得到和下面类似的输出内容:
0:001> !analyze -v
*******************************************************************************
*                                                                             *
*                        Exception Analysis                                   *
*                                                                             *
*******************************************************************************

FAULTING_IP: 
CrashDemo!TestFunc+2e [c:/tests/crashdemo/crashdemo.cpp @ 124]
004309de c70000000000     mov     dword ptr [eax],0x0

EXCEPTION_RECORD:  ffffffff -- (.exr ffffffffffffffff)
.exr ffffffffffffffff
ExceptionAddress: 004309de (CrashDemo!TestFunc+0x0000002e)
ExceptionCode: c0000005 (Access violation)
ExceptionFlags: 00000000
NumberParameters: 2
Parameter[0]: 00000001
Parameter[1]: 00000000
Attempt to write to address 00000000

DEFAULT_BUCKET_ID:  APPLICATION_FAULT

PROCESS_NAME:  CrashDemo.exe

ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at "0x%08lx" referenced memory 
  at "0x%08lx". The memory could not be "%s".

WRITE_ADDRESS:  00000000 

BUGCHECK_STR:  ACCESS_VIOLATION

LAST_CONTROL_TRANSFER:  from 0043096e to 004309de

STACK_TEXT:
006afe88 0043096e 00000000 00354130 00350001 CrashDemo!TestFunc+0x2e 
  [c:/tests/crashdemo/crashdemo.cpp @ 124]
006aff6c 00430f31 00000000 52319518 00354130 CrashDemo!WorkerThread+0x5e 
  [c:/tests/crashdemo/crashdemo.cpp @ 115]
006affa8 00430ea2 00000000 006affec 7c80b50b CrashDemo!_callthreadstartex+0x51 
  [f:/rtm/vctools/crt_bld/self_x86/crt/src/threadex.c @ 348]
006affb4 7c80b50b 00355188 00354130 00350001 CrashDemo!_threadstartex+0xa2 
  [f:/rtm/vctools/crt_bld/self_x86/crt/src/threadex.c @ 331]
006affec 00000000 00430e00 00355188 00000000 kernel32!BaseThreadStart+0x37


FOLLOWUP_IP: 
CrashDemo!TestFunc+2e [c:/tests/crashdemo/crashdemo.cpp @ 124]
004309de c70000000000     mov     dword ptr [eax],0x0

SYMBOL_STACK_INDEX:  0

FOLLOWUP_NAME:  MachineOwner

SYMBOL_NAME:  CrashDemo!TestFunc+2e

MODULE_NAME:  CrashDemo

IMAGE_NAME:  CrashDemo.exe

DEBUG_FLR_IMAGE_TIMESTAMP:  43dc6ee7

STACK_COMMAND:  .ecxr ; kb

FAILURE_BUCKET_ID:  ACCESS_VIOLATION_CrashDemo!TestFunc+2e

BUCKET_ID:  ACCESS_VIOLATION_CrashDemo!TestFunc+2e

Followup: MachineOwner
---------
注意用粗体表示的内容。第一处报告了异常的地址和类型。第二外报告调用栈。第三处为我们提供了怎样访问保存在故障转储里的异常信息的额外信息。
现在,我们知道异常发生的位置,甚至可以查看调用栈。那么,是得到函数的参数值及局部变量的时候了。在开始之前,让我们注意!analyze报告中的第三处信息。这里再重复一下第三处所包含的内容:
STACK_COMMAND:  .ecxr ; kb
对'kb'命令我们已经不陌生了(它显示调用栈)。但.ecxr是什么?这条命令要求调试器把当前的内容切换到保存在故障转储里的异常信息。我们执行这条命令后,将能访问异常抛出时调用栈和局部变量的值。
在我们要求调试使用异常的上下文后,我们可以用'dv'命令显示函数的参数值以及局部变量。因为我们通常想查看调用栈上每一个函数的信息,因此,我们可以用'!for_each_frame dv /t'命令(/t选项要求'dv'显示有用的类型信息)。(当然,我们必须记住,使用优化编译时,在函数的整个生存期中,局部变量有可能会被取消,重注册或被重用来保存其它的数据,因此,可能会导致'dv'命令输出错误的值)。
下面是分析带异常信息的故障转储的命令行示例:
  cdb -z c:/myapp.dmp -logo out.txt -lines -c "!analyze -v;.ecxr;!for_each_frame dv /t;q"
下面是'!for_each_frame dv /t'命令输出的例子:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
00 006afe88 0043096e CrashDemo!TestFunc+0x2e [c:/tests/crashdemo/crashdemo.cpp @ 124]
int * pParam = 0x00000000
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
01 006aff6c 00430f31 CrashDemo!WorkerThread+0x5e [c:/tests/crashdemo/crashdemo.cpp @ 115]
void * lpParam = 0x00000000
int * TempPtr = 0x00000000
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
02 006affa8 00430ea2 CrashDemo!_callthreadstartex+0x51 
  [f:/rtm/vctools/crt_bld/self_x86/crt/src/threadex.c @ 348]
struct _tiddata * ptd = 0x00355188
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
03 006affb4 7c80b50b CrashDemo!_threadstartex+0xa2 
  [f:/rtm/vctools/crt_bld/self_x86/crt/src/threadex.c @ 331]
void * ptd = 0x00355188
struct _tiddata * _ptd = 0x00000000
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
04 006affec 00000000 kernel32!BaseThreadStart+0x37
Unable to enumerate locals, HRESULT 0x80004005
Private symbols (symbols.pri) are required for locals.
Type ".hh dbgerr005" for details.
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
00 006afe88 0043096e CrashDemo!TestFunc+0x2e [c:/tests/crashdemo/crashdemo.cpp @ 124]
如果minidump没有包括目标进程内存的完整内容,那么只有当调试器能正确发现被目标进程加载的、相同版本的可执行模块时,才能分析dump。在某些情形下,你必须帮助调试器定位这些模块??通过指定模块搜索路径。关于模块搜索路径的详细信息和相关内容可以在这篇文章 中找到。

现在,我们来处理不带异常信息的故障转储。当我们分析这样的dump时,通常想知道所有线程的调用栈。下面是怎样得到这些信息:
  cdb -z c:/myapp.dmp -logo out.txt -lines -c "~*kb;q"
如果我们不知道故障转储是否包含异常信息,该怎么做呢?对于minidumps来说,我们可以用MiniDumpView 打印dump的内容,查看它里面是否包含异常信息。对于过时的'full user dumps',或许唯一的选择是,照现在的样子启动包含异常信息的dump,并查看!analyze是否报告了有意义的内容。
有一个有趣的特例??因为未经处理的异常生成故障转储,但因为某些原因没有包含异常信息是有可能的。在这种情形下,在下面过程的帮助下,仍可能找出异常发生的位置:
1.  打印所有线程的调用栈(用前面提过的CDB命令)。
2.  找出包含kernel32!UnhandledExceptionFilter函数调用栈的线程。
3.  使用UnhandledExceptionFilter 函数的第一个参数(包含一个指向EXCEPTION_POINTERS 结构的指针)的实际值。
下面是EXCEPTION_POINTERS 结构的声明:
typedef struct _EXCEPTION_POINTERS 
{  
    PEXCEPTION_RECORD ExceptionRecord;  
    PCONTEXT ContextRecord;
} EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;
如果我们知道这个结构的地址,就能得到指向异常上下文的指针(保存在ContextRecord字段里),把它传递给.cxr命令,从而把调试器上下文切换到异常发生的位置。在.cxr命令执行后,我们可以用'kb'命令得到异常发生时的调用栈。下面是一个例子:
1.  打印所有线程的调用栈。
  cdb -z c:/myapp.dmp -logo out.txt -c "~*kb;q"
0:000> ~*kb

.  0  Id: 6c4.73c Suspend: 1 Teb: 7ffdf000 Unfrozen
ChildEBP RetAddr  Args to Child              
0012fdf8 7c90d85c 7c8023ed 00000000 0012fe2c ntdll!KiFastSystemCallRet
0012fdfc 7c8023ed 00000000 0012fe2c 0012ff54 ntdll!NtDelayExecution+0xc
0012fe54 7c802451 0036ee80 00000000 0012ff54 kernel32!SleepEx+0x61
0012fe64 00430856 0036ee80 00330033 00300037 kernel32!Sleep+0xf
0012ff54 00431702 00000001 00352ed0 00352fb0 CrashDemo!wmain+0x96
0012ffb8 004314bd 0012fff0 7c816d4f 00330033 CrashDemo!__tmainCRTStartup+0x232
0012ffc0 7c816d4f 00330033 00300037 7ffd9000 CrashDemo!wmainCRTStartup+0xd
0012fff0 00000000 0042e5a5 00000000 00000000 kernel32!BaseProcessStart+0x23

   1  Id: 6c4.5cc Suspend: 1 Teb: 7ffde000 Unfrozen
ChildEBP RetAddr  Args to Child              
006af6e4 7c90e273 7c863130 d0000144 00000004 ntdll!KiFastSystemCallRet
006af6e8 7c863130 d0000144 00000004 00000000 ntdll!NtRaiseHardError+0xc
006af96c 00438951 006af9e0 5d343834 00000000 kernel32!UnhandledExceptionFilter+0x59c
006af990 00430f2a c0000005 006af9e0 0044ad30 CrashDemo!_XcptFilter+0x61
006af99c 0044ad30 00000000 00000000 00000000 CrashDemo!_callthreadstartex+0x7a
006af9b0 00438c67 00430f13 0049a230 00000000 CrashDemo!_EH4_CallFilterFunc+0x12
006af9e8 7c9037bf 006afad4 006aff98 006afaf0 CrashDemo!_except_handler4+0xb7
006afa0c 7c90378b 006afad4 006aff98 006afaf0 ntdll!ExecuteHandler2+0x26
006afabc 7c90eafa 00000000 006afaf0 006afad4 ntdll!ExecuteHandler+0x24
006afabc 004309be 00000000 006afaf0 006afad4 ntdll!KiUserExceptionDispatcher+0xe
006afe88 0043094e 00000000 00354130 00350001 CrashDemo!TestFunc+0x2e
006aff6c 00430f01 00000000 647bff58 00354130 CrashDemo!WorkerThread+0x5e
006affa8 00430e72 00000000 006affec 7c80b50b CrashDemo!_callthreadstartex+0x51
006affb4 7c80b50b 00355188 00354130 00350001 CrashDemo!_threadstartex+0xa2
006affec 00000000 00430dd0 00355188 00000000 kernel32!BaseThreadStart+0x37
2.  改变调试器的上下文,得到异常的调用栈。
  cdb -z c:/myapp.dmp -logo out.txt -lines -c ".cxr dwo(0x006af9e0+4);kb;q"
('dwo'操作符返回保存在指定地址里的double word,并把它传递给.cxr命令)
本篇文章后面提供的批处理文件(实际上是DumpStackCtx.bat)将简化这个任务。
还有另外的方法可以解决这个问题??你可以在这里 找到更多的信息。

分析虚拟内存
当我们想审查被调试进程的虚拟内存布局时,CDB可以协助Visual Studio调试器。下面的命令显示进程完整的虚拟内存映射:
  cdb -pv -pn myapp.exe -logo out.txt -c "!vadump -v;q"
(!vadump命令负责打印虚拟内存映射,照常,用-v选项要求它显示详细的内容)
下面是!vadump输出的例子:
BaseAddress:       00040000
AllocationBase:    00040000
AllocationProtect: 00000004  PAGE_READWRITE
RegionSize:        0002e000
State:             00002000  MEM_RESERVE
Type:              00020000  MEM_PRIVATE

BaseAddress:       0006e000
AllocationBase:    00040000
AllocationProtect: 00000004  PAGE_READWRITE
RegionSize:        00001000
State:             00001000  MEM_COMMIT
Protect:           00000104  PAGE_READWRITE + PAGE_GUARD
Type:              00020000  MEM_PRIVATE

BaseAddress:       0006f000
AllocationBase:    00040000
AllocationProtect: 00000004  PAGE_READWRITE
RegionSize:        00011000
State:             00001000  MEM_COMMIT
Protect:           00000004  PAGE_READWRITE
Type:              00020000  MEM_PRIVATE
在Windows XP和Windows Server 2003上,CDB为审查虚拟内存布局提供了一条更好的命令??!address。这条命令可以完成下面的任务:
•  显示进程的虚拟内存映射(依我之见,比!vadump的输出内容更易阅读)
•  显示有用的、虚拟内存使用的统计数据
•  确定指定的地址属于哪种虚拟内存区域(例如,它是属于栈,堆,还是可执行映象?)
下面是怎样用!address报告虚拟内存映射的示例:
  cdb -pv -pn myapp.exe -logo out.txt -c "!address;q"
下面显示被线程的栈占用的内存区域:
    00040000 : 00040000 - 0002e000
                    Type     00020000 MEM_PRIVATE
                    Protect  00000000 
                    State    00002000 MEM_RESERVE
                    Usage    RegionUsageStack
                    Pid.Tid  658.644
               0006e000 - 00001000
                    Type     00020000 MEM_PRIVATE
                    Protect  00000104 PAGE_READWRITE | PAGE_GUARD
                    State    00001000 MEM_COMMIT
                    Usage    RegionUsageStack
                    Pid.Tid  658.644
               0006f000 - 00011000
                    Type     00020000 MEM_PRIVATE
                    Protect  00000004 PAGE_READWRITE
                    State    00001000 MEM_COMMIT
                    Usage    RegionUsageStack
                    Pid.Tid  658.644
注意,!address非常智能,可以报告属于栈的线程的线程ID。
在!address报告虚拟内存区域之后,它也能报告有趣的、虚拟内存使用的统计数据:
-------------------- Usage SUMMARY --------------------------
    TotSize   Pct(Tots) Pct(Busy)   Usage
   00838000 : 0.40%       27.96%      : RegionUsageIsVAD
   7e28c000 : 98.56%      0.00%       : RegionUsageFree
   01348000 : 0.94%       65.60%      : RegionUsageImage
   00040000 : 0.01%       0.85%       : RegionUsageStack
   00001000 : 0.00%       0.01%       : RegionUsageTeb
   001a0000 : 0.08%       5.53%       : RegionUsageHeap
   00000000 : 0.00%       0.00%       : RegionUsagePageHeap
   00001000 : 0.00%       0.01%       : RegionUsagePeb
   00001000 : 0.00%       0.01%       : RegionUsageProcessParametrs
   00001000 : 0.00%       0.01%       : RegionUsageEnvironmentBlock
       Tot: 7fff0000 Busy: 01d64000

-------------------- Type SUMMARY --------------------------
    TotSize   Pct(Tots) Usage
   7e28c000 : 98.56%     : <free>
   01348000 : 0.94%      : MEM_IMAGE
   007b6000 : 0.38%      : MEM_MAPPED
   00266000 : 0.12%      : MEM_PRIVATE

-------------------- State SUMMARY --------------------------
    TotSize   Pct(Tots) Usage
   01647000 : 1.09%      : MEM_COMMIT
   7e28c000 : 98.56%     : MEM_FREE
   0071d000 : 0.35%      : MEM_RESERVE

Largest free region: Base 01014000 - Size 59d5c000
当我们正在调试内存泄露,以及想确定内存泄露的类型(堆,栈,原始的虚拟内存,等等)时,这些统计数据非常有用。通过最后一行,我们可以确定虚拟内存中最大的空闲区域的大小,这在我们必须设计请求大量内存的应用程序时非常有帮助。
如果你只想查看统计数据,而不想看虚拟内存映射,可以用-summary参数:
  cdb -pv -pn myapp.exe -logo out.txt -c "!address -summary;q"
如果我们想确定给定地址的虚拟内存属于哪种类型,可以把这个地址作为参数传递给!address命令。下面是一个例子:
0:000> !address 0x000a2480;q
    000a0000 : 000a0000 - 000d7000
                    Type     00020000 MEM_PRIVATE
                    Protect  00000004 PAGE_READWRITE
                    State    00001000 MEM_COMMIT
                    Usage    RegionUsageHeap
                    Handle   000a0000

搜索符号
有时候,我们可能需要确定符号(函数或变量)的地址。如果我们知道准确的符号名,可以把它输入Visual Studio调试器的反汇编窗口,从中找出它对应的地址。但是,假设我们忘了准确的符号名呢?或者想找出名字上有相同规律的、一组符号的地址(例如,类的所有成员函数)?CDB很容易解决这个问题??它提供'x'命令,可以列出匹配指定掩码的所有符号名:
  x Module!Symbol
下面的命令设法定位位于kernel32.dll 中的UnhandledExceptionFilter函数的地址:
  cdb -pv -pn notepad.exe -logo out.txt -c "x kernel32!UnhandledExceptionFilter;q"
下面是输出内容:
0:000> x kernel32!UnhandledExceptionFilter;q
7c862b8a kernel32!UnhandledExceptionFilter = <no type information>
'x'命令可以接受多个通配符,提供一些有用的选项,可用于排序输出内容以及输出符号的额外信息??你可以在WinDbg文档里找到更多的信息。例如,下面的命令可以把我们在应用程序的主可执行模块中定义的CmainFrame类所有的成员函数和统计数据列出来:
0:000> x myapp!*CMainFrame*
004542f8 MyApp!CMainFrame::classCMainFrame = struct CRuntimeClass
00401100 MyApp!CMainFrame::`scalar deleting destructor' (void)
004011a0 MyApp!CMainFrame::OnCreate (struct tagCREATESTRUCTW *)
00401000 MyApp!CMainFrame::CreateObject (void)
00401280 MyApp!CMainFrame::PreCreateWindow (struct tagCREATESTRUCTW *)
00401070 MyApp!CMainFrame::GetRuntimeClass (void)
00401120 MyApp!CMainFrame::~CMainFrame (void)
00401090 MyApp!CMainFrame::CMainFrame (void)
00401080 MyApp!CMainFrame::GetMessageMap (void)
004578ec MyApp!CMainFrame::`RTTI Base Class Array' = <no type information>
004578dc MyApp!CMainFrame::`RTTI Class Hierarchy Descriptor' = <no type information>
004578c8 MyApp!CMainFrame::`RTTI Complete Object Locator' = <no type information>
004579ec MyApp!CMainFrame::`RTTI Base Class Descriptor at (0,-1,0,64)' = <no type information>
00461e94 MyApp!CMainFrame `RTTI Type Descriptor' = <no type information>
00454354 MyApp!CMainFrame::`vftable' = <no type information>
CDB也可以反着做??通过地址找符号,使用'ln'命令:
  ln Address
下面是它的用法:
  cdb -pv -pn notepad.exe -logo out.txt -c "ln 0x77d491c8;q"
下面是它的输出内容:
0:000> ln 0x77d491c8;q
(77d491c6)   USER32!GetMessageW+0x2   |  (77d49216)   USER32!CharUpperBuffW
注意,我们不必指定符号的起始地址(在这个例子里,是函数),而可以用符号占用的地址范围内的任何地址。'ln'将找出符号,报告它的地址,另外还报告跟在指定内容后面的地址和符号名。

显示数据结构
如果我们想研究数据结构的内容,通常会用Visual Studio的Watch,QuickWatch或其它类似的窗口。这些窗口允许我们查看结构成员变量的类型和值。但是假设我们也需要知道结构的精确布局,包括它的成员的偏移量?Visual Studio不提供易用的解决方法,但幸运的是,CDB可以。在'dt'命令的帮助下,我们可以显示数据结构或类的精确布局。
如果我们只想了解数据类型的布局,可以用下面这条命令:
  dt -b TypeName
(-b选项启用递归显示,显示结构或类的成员类型的嵌入式数据结构)。
下面是命令的示例:
  cdb -pv -pn myapp.exe -logo out.txt -c "dt -b CSymbolInfoPackage;q"
下面是输出内容(在运行SymFromAddr 应用程序时得到的):
0:000> dt /b CSymbolInfoPackage;q
   +0x000 si               : _SYMBOL_INFO
      +0x000 SizeOfStruct     : Uint4B
      +0x004 TypeIndex        : Uint4B
      +0x008 Reserved         : Uint8B
      +0x018 Index            : Uint4B
      +0x01c Size             : Uint4B
      +0x020 ModBase          : Uint8B
      +0x028 Flags            : Uint4B
      +0x030 Value            : Uint8B
      +0x038 Address          : Uint8B
      +0x040 Register         : Uint4B
      +0x044 Scope            : Uint4B
      +0x048 Tag              : Uint4B
      +0x04c NameLen          : Uint4B
      +0x050 MaxNameLen       : Uint4B
      +0x054 Name             : Char
   +0x058 name             : Char
如果你想显示特殊变量的布局,可以把它的地址传递给'dt'命令:
  dt -b TypeName Address
下面是例子:
  cdb -pv -pn myapp.exe -logo out.txt -c "dt -b CSymbolInfoPackage 0x0012f6d0;q"
0:000> dt /b CSymbolInfoPackage 0x0012f6d0;q
   +0x000 si               : _SYMBOL_INFO
      +0x000 SizeOfStruct     : 0x58
      +0x004 TypeIndex        : 2
      +0x008 Reserved         : 
       [00] 0
       [01] 0
      +0x018 Index            : 1
      +0x01c Size             : 0x428
      +0x020 ModBase          : 0x400000
      +0x028 Flags            : 0
      +0x030 Value            : 0
      +0x038 Address          : 0x411d30
      +0x040 Register         : 0
      +0x044 Scope            : 0
      +0x048 Tag              : 5
      +0x04c NameLen          : 0xe
      +0x050 MaxNameLen       : 0x7d1
      +0x054 Name             :  "S"
       [00] 83 'S'
   +0x058 name             :  "SymbolInfo"
    [00] 83 'S'
    [01] 121 'y'
    [02] 109 'm'
    [03] 98 'b'
    [04] 111 'o'
    [05] 108 'l'
    [06] 73 'I'
    [07] 110 'n'
    [08] 102 'f'
    [09] 111 'o'
    [10] 0 ''
    [11] 0 ''
    [12] 0 ''
    [13] 0 ''
    [14] 0 ''
    [15] 0 ''
    [16] 0 ''
    [17] 0 ''
    ... 省略部分输出内容
    [1990] 0 ''
    [1991] 0 ''
    [1992] 0 ''
    [1993] 0 ''
    [1994] 0 ''
    [1995] 0 ''
    [1996] 0 ''
    [1997] -52 ''
    [1998] -52 ''
    [1999] -52 ''
    [2000] -52 ''
注意,'dt'也显示结构成员变量的值。

批处理文件
我们已经知道怎样用CDB解决一些有趣的调试问题了。现在可以用它解决更多的问题了??用易用的批处理文件代替难记的CDB命令行。考虑我们在本文开始部分使用的命令行示例:
  cdb -pv -pn myapp.exe -logo out.txt -c "lm;q"
这条命令中的大部分是固定的,用不着改变。唯一可变的部分是目标信息(-pn myapp.exe),我们可能会用另外的可执行文件名,或另外的附着方式(例如,通过进程ID)替换它。
下面介绍了怎样用批处理文件代替这条命令:
  ; lm.bat
  cdb -pv %1 %2 -logo out.txt -c "lm;q"
如果我们运行这个批处理文件,通过进程来得到已加载模块的列表,可以用下面的方法:
通过可执行文件名附上进程:
  lm -pn myapp.exe
通过进程ID附上进程:
  lm -p 1234
通过服务名附上进程:
  lm -psn MyService
打开故障转储文件:
  lm -z c:/myapp.dmp
这条命令与具体的目标无关,只做同样的事情??打印已加载模块的列表。
如果我们想为CDB命令指定另外的参数,我们可以用同样的方法。考虑下面用于显示数据结构布局的命令:
  cdb -pv -pn myapp.exe -logo out.txt -c "dt /b MyStruct;q"
当然,我们可能想使用任何数据类型运行这条命令,而不仅仅是MyStruct。下面是怎样做的示例:
  ; dt.bat
  cdb -pv %1 %2 -logo out.txt -c "dt /b %3;q"
现在,我们可以象下面这样运行这条命令:
  dt -pn myapp.exe CTestClass
或者象这样:
  dt -p 1234 SYMBOL_INFO
或者,也可以象这样:
  dt -z c:/myapp.dmp EXCEPTION_POINTERS
我们可以用同样的方法处理其它的命令。你可以从这里 找到本文曾讨论过的批处理文件。在以后,我准备用另外有用的命令扩充它。

将理论与实践相结合--译者语

【排查过程】计算机蓝屏重启问题 问题: - 每次蓝屏的时间段范围在[15,30] 分钟;即平均每隔半个小时蓝屏一次; - 蓝屏提示代码:DRIVER_IRQL_NOT_LESS_OR_EQUAL(或其他代码) 我的电脑具体情况: - 电脑是原装系统 - 去年的第四季度,系统蓝屏过3次左右,每次相隔一个月左右; - 今年,每隔一周,蓝屏一次,时间都固定在每周三,15:24,会蓝屏重启; - 在某天,系统蓝屏5次左右;继日开始,每隔半小时蓝屏一次; 总结该电脑的问题,先写在前面,在后文就不提了: 我的电脑为何每隔一... 阅读详情

相关推荐

ThingsBoard实战指南:RPC命令下发与设备交互详解

本文深入解析了ThingsBoard平台中RPC命令下发的核心机制与实战应用。通过对比客户端与服务端RPC模式,详细阐述了从仪表盘控件配置到设备端Python代码响应的完整双向交互流程,并提供了常见问题排查与进阶优化技巧,帮助开发者构建可靠、实时的物联网设备控制系统。

weixin_29168153的博客 213

翻译:通往WinDbg的捷径(二)

通往WinDbg的捷径(二)原文:http://www.debuginfo.com/articles/easywindbg2.html译者:arhat时间:2006年4月14日关键词:CDBWinDbg保存dumps在我们调试不容易重现的问题时,可能想把应用程序状态的快照(内存内容,打开名柄的列表,等等)保存起来,以便日后分析。例如,当我怀疑当前的状态可能包含我试图解决的问题的关键点,而...

weixin_30896763的博客 345

xilinx zynq-7000 linux下rs422串口通信测试步骤

这几天使用xilinx的zynq-7000系列调试rs422串口,期间遇到不少问题,好在最终调试完成,在这里记录一下调试步骤。 我用的soc型号是xc7z100,板载了2个rs422外设,PS端有2个uart控制器,uart0用于连接rs232并作为console控制台输出,uart1连接rs422。 rs422和主板连线原理图如下: 其中422_TXD1_3V3 和422_RXD1_3V3 为数据发送和接收引脚,422_TXD_DE1_3V3为发送使能引脚,422_RXD_REn1_...

dongyunke的博客 5907

Windbg命令学习14(dv)

以下以自己的控件库skinhgy为例,windbg启动 1.dv(display Local variable) dv 命令显示当前作用域的所有局部变量的名字和值。 0:000> x Simple1Demo!CSimple1DemoApp::InitInstance 00640080 Simple1Demo!CSimple1DemoApp::InitInsta...

weixin_30802273的博客 701

STM32CubeMX的正交编码器encoder

由于最近干活遇到需要获取正交编码的功能,所以返回来研究一下STM32CubeMX的编码器功能,之前也更新过STM32的定时器功能,但是因为之前搞平衡车的正交编码器没有搞清楚原理,就直接用了脉冲输入功能草草了事,现在也算是为之前的年轻买单了,不过看到现有的Encoder说明都比较简单,所以就当是为开发者做贡献了,我研究清楚之后来一篇博客。

ASWaterbenben的博客 1万+

瑞芯微RK3588/RK3576平台采集BT1120(fpga)驱动调试记录

本文介绍了在RK3576/RK3588平台上采集FPGA传输的BT1120数据的方法。主要内容包括:1)硬件连接需确认管脚复用和图像格式(通常为uyvy_2x8);2)设备树配置要点,如I2C总线设置、BT1120模式指定等;3)驱动适配注意事项,包括时钟极性、分辨率格式等关键参数;4)测试方法,通过media-ctl和v4l2-ctl工具验证数据采集。文中特别指出RK3576和RK3588配置的通用性,并提供了常见问题的排查思路。

bingdund的博客 916

DUMP文件分析4:栈溢出

前面说到过,栈溢出类型的异常通过编程的方式获取DUMP可能不成功,因为栈溢出会破坏SEH(结构化异常处理)框架。实际上,通过DUMP文件来调试栈溢出同样是困难的,因为栈溢出本身一般不会造成异常,异常往往发生在栈溢出破坏栈上的数据之后,同时,由于栈溢出破坏了栈上的数据,使得我们无法对函数调用进行回溯,从而难以定位问题的发生位置。 本节的示例是经dump1简单修改而来,在Crash Me!按钮的消息处

hustd10的博客 6605

通往WinDbg的捷径(一)(二)

通往WinDbg的捷径(一) 原文:http://www.debuginfo.com/articles/easywindbg.html译者:arhat时间:2006年4月13日关键词:CDB WinDbg 导言你钟情什么样的调试器?如果你问我这个问题,我会回答是“Visual Studio + WinDbg”。我比较喜欢Visual Studio那朴实无华且易操作的接口,更喜欢它能迅速把我需要

shellching的专栏 3548

工业机器人抓取目标检测与抓取姿态估计系统:基于YOLOv12与Cornell Grasping Dataset

工业机器人抓取是自动化生产中的核心环节,而精确的目标检测和抓取姿态估计是实现高效抓取的关键。本文将详细介绍如何构建一个完整的工业机器人抓取系统,该系统结合了最新的YOLOv12目标检测算法和Cornell Grasping Dataset的抓取姿态估计,并提供了用户友好的UI界面。本文详细介绍了一个完整的工业机器人抓取系统,该系统结合了YOLOv12目标检测和基于Cornell Grasping Dataset的抓取姿态估计。我们将使用PyQt5构建一个用户友好的界面,集成目标检测和抓取姿态估计功能。

YOLO 574

Android 14 (AOSP) 源码架构深度解析与开发者指南

Android14源码的仓库指南信息

保持专注,保持热爱 1186

通往WinDbg的捷径(二)

windbg 调试

zzw315的专栏 899

LeRobot 宇树人形机器人 G1 部署 GR00T 运动策略

本文详细介绍了Unitree G1人形机器人的完整设置流程。首先通过以太网连接机器人并进行SSH访问,然后配置WiFi网络实现无线连接。在机器人端安装LeRobot软件并运行服务器程序,最后在控制端配置IP地址并运行GR00T移动控制系统。指南涵盖了29自由度和23自由度版本G1机器人的配置方法,支持通过WiFi进行远程策略部署,提供双足行走与平衡控制功能,并详细说明了遥控器操作方式。

2506_90492529的博客 2172

【转帖】通往WinDbg的捷径(一)(二)

 通往WinDbg的捷径(一)原文:http://www.debuginfo.com/articles/easywindbg.html译者:arhat时间:2006年4月13日关键词:CDB WinDbg 导言你钟情什么样的调试器?如果你问我这个问题,我会回答是“Visual Studio + WinDbg”。我比较喜欢Visual Studio那朴实无华且易操作的接口,更喜欢它能迅速把我需要的信

floweronwarmbed的专栏 1579

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

通往WinDbg的捷径(一)

通往WinDbg的捷径(一) 原文:http://www.debuginfo.com/articles/easywindbg.html 译者:arhat 时间:2006年4月13日 关键词:CDB WinDbg  导言 你钟情什么样的调试器?如果你问我这个问题,我会回答是“Visual Studio + WinDbg”。我比较喜欢Visual Studio那朴实无华且易操作的接

xiaoshahai的专栏 2200

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

通往WinDbg的捷径

原文:http://www.debuginfo.com/articles/easywindbg.html译者:arhat时间:2006年4月13日关键词:CDB WinDbg 导言你钟情什么样的调试器?如果你问我这个问题,我会回答是“Visual Studio + WinDbg”。我比较喜欢Visual Studio那朴实无华且易操作的接口,更喜欢它能迅速把我需要的信息以可视的形式展示出来。但遗憾

狼窝 4254

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

通往WinDbg的捷径【转】

 原贴:通往WinDbg的捷径(一)http://bbs.pediy.com/showthread.php?s=&threadid=24077通往WinDbg的捷径(二)http://bbs.pediy.com/showthread.php?s=&threadid=24119

做好我自己 625

翻译:通往WinDbg的捷径(一)

标 题: 翻译:通往WinDbg的捷径(一)作 者: arhat时 间: 2006-04-13,21:11链 接: http://bbs.pediy.com/showthread.php?t=24077通往WinDbg的捷径(一)原文:http://www.debuginfo.com/articles/easywindbg.html译者:arhat时间:2006年4月13日关键词:CDB WinD

tanliyoung的专栏 1279

通往 WinDbg 的捷径

导言你钟情什么样的调试器?如果你问我这个问题,我会回答是“Visual Studio + WinDbg”。我比较喜欢Visual Studio那朴实无华且易操作的接口,更喜欢它能迅速把我需要的信息以可视的形式展示出来。但遗憾的是,Visual Studio调试器无法获取某些信息。例如,假设我想知道哪个线程正在占用特殊的临界区?或者是哪个函数占用了大部分的栈空间?不用担心,有WinDbg呢。它的

我的程序世界 1146
上一篇: 通往WinDbg的捷径(一)
下一篇: 也谈Release版本排错
mergerly
博客等级 码龄22年 953粉丝 80原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值