#工作空间 [Workspace]
工作空间被用来描述和存储一个调试项目的属性、参数以及调试器设置等信息,其功能类似于集成开发环境的项目文件。
具体包括:调试会话状态(断点、打开的源文件、用户定义的别名等)、调试器设置(符号文件路径、源文件路径、可执行映像文件路径等)和窗口布局状态。
0. 基础工作空间 [base workspace]
未载入任何的调试文件时使用默认的启动环境。存放位置:HKEY_CURRENT_USER\Software\Microsoft\Windbg\Workspaces下Default二进制值。
1. User、Kernel、Dump、Explicit工作空间
存放在HKEY_CURRENT_USER\Software\Microsoft\Windbg\Workspaces的各子键中(如下图)
子键User、Kernel、Dump用来保存用户态调试、内核态调试、调试转储文件时,使用【Save Workspace】方式时默认的保存路径。
子键Explicit则用来记录使用【Save Workspace as】方式保存的命名工作空间。
对于【Attach to a Process】的方式,会使用User下的Default二进制值作为其工作空间,之后对工作空间所作的修改也会写到该字段中。
注:有时会发现没有某个子键,这是因为windbg还没使用过该调试方式。
2. 其他说明
(1) windbg还提供另外一种保存方式【Save WorkSpace to File】,作用跟上面两种的方式差不多,
只不过把工作空间以文件的形式保存到磁盘里,那么用户就可以通过U盘或其它方式把工作空间的环境移植到其它机器上使用。
(2) 调试目标最后的工作空间为:基础工作空间与其对应工作空间的求并所得的环境。
(3) 用windbg打开目标可执行文件【Open Executable】,若有默认的工作空间文件(在子键User下),会自动打开其对应默认的的工作空间文件。
要使用子键Explicit下目标文件的工作空间,必须先提前【Open WorkSpace】,然后再打开目标可执行文件。
(4) 在载入调试目标后改变了工作空间并保存,并不改变windbg的基础工作空间,只改变调试目标对应的工作空间;
而只有在windbg还没载入任何调试目标时改变并保存才会影响基础工作空间。
#符号 [Symbol]
调试者接触的二进制数据以及汇编代码,如果没有符号表文件的帮助,就无法知道这些数据代表什么变量、函数。
符号(symbol) :函数和变量的别名,编译器通过符号修饰(Name Decoration)和 函数签名(Function Signature)来实现全局唯一性。
函数符号含有返回值,函数名,函数参数等函数所有信息;变量符号含有类型和名称信息。
通过不同的编译选项,编译器可以生成两种截然不同的信息集合:私有符号(private symbol)和公有符号(public symbol)。
私有符号(private symbol)包含完整的调试信息,编译选项:/Zi或/ZI。
公共函数和变量:在多个的编译单元(源代码文件)中可见的函数和变量。
私有函数和变量:用于描述除公共函数和变量以外的所有函数和变量,包括静态函数、静态和局部变量、函数参数)。
因此,为了能更方便读懂二进制数据和汇编语言,需要获取符号表文件,在windbg下使用的符号表文件一般是.PDB文件。
客户端软件存在于exe/dll中,这些二进制文件会调用操作系统的dll,这两部分二进制文件的符号表文件都需要获取。
客户端软件自己的符号表文件在构建程序的过程中产生;而操作系统dll的符号表文件可以通过设置windbg,根据需要自动从微软符号服务器上下载。
#pdb(program Database )
0. pdb中包含的内容
符号表 |
函数和变量符号到地址的映射表(map文件实际就是一个文本格式的符号表)。 |
源文件和代码行信息 |
二进制指令到代码源文件代码行的映射表。 |
类型信息 |
用于存储每一个函数和变量的类型信息。对于变量或函数参数,类型信息能够告诉调试器是整型还是字符串类型,或是用户自定义类型。 对于函数,类型信息记载了参数的个数、调用转换和返回值的类型。 |
FPO(frame pointer omission, 帧指针省略)
|
对于做了FPO优化的函数,FPO信息保存了一些数据来帮助调试器确定函数堆栈帧的大小, 帮助调试器查找函数的参数和本地变量,甚至在帧指针无效时也能工作。 如果没有FPO信息,调试器无法正确显示被优化的程序的调用堆栈。 |
编辑和继续执行信息 |
用于帮助Visual Studio在调试时实现编辑和继续执行的功能 |
在过去的十年中,微软使用了几种不同的格式(COFF、CodeView和应用的最广泛的PDB格式)来存放调试信息。
格式 |
是否有文档 |
存储 |
公共函数和变量 |
私有函数和变量 |
源文件和代码行信息 |
类型信息 |
FPO信息 |
编辑和继续执行信息 |
COFF |
有 |
可执行文件中 |
+ |
- |
+ |
- |
+ |
- |
CodeView |
部分 |
可执行文件中 或.DBG文件中 |
+ |
+ |
+ |
+ |
+ |
- |
Program Database |
微软没有提供程序数据库格式的文档,只提供特殊的编程接口DbgHelp 和DIA来访问它。 |
.PDB文件中 |
+ |
+ |
+ |
+ |
+ |
+ |
1. 在vc6下配置生成PDB文件
vc6的Debug配置,会在编译时生成私有符号pdb文件;Release可通过如下配置来达到这一目的:
(1) 选择Project->Setting菜单,打开工程设置对话框
(2) 在“Setting For:” 中选择 “Win32 Release”(只设置Release版本即可,因为Debug版本默认生成PDB文件)
(3) 选择标签页 “C/C++” ,在 “Debug info” 中选择 “Line Numbers Only”(如果选择Program Database则包含更多的符号信息)
(4) 选择标签页 “Link” ,勾选 “Generate debug info”
(5) 在标签页 “Link” 中,选择 “Category->Customize”,在这里填写PDB文件的生成路径
注:无论是Debug还是Release配置,VC6.0的Link选项需要将/pdbtype:sept改为/pdbtype:con, 否则生成的pdb文件中将不包含如自定义结构体,类等信息
更多关于vc6和vs的pdb编译和链接选项信息,请参考:Windows程序调试系列: 使用VC++生成调试信息
2.在windbg下使用PDB文件
启动windbg后,选择 【Symbol File Path】,弹出 “Symbol Search Path” 对话框,输入存放PDB文件的路径。可以输入多个路径,路径之间使用分号进行分隔。
如果此时正在调试程序,则对话框中的 “reload” 选择框是可以选择的,勾选后点击“确定”。
3.使用Windows符号表服务器
为了方便调试,需要获取Windows系统DLL的符号表文件。微软公司提供了可以通过Internet访问的符号表服务器。
经过设置,在调试过程中,windbg调试器会从符号表服务器自动下载调试所需的PDB文件,非常方便。
设置方法: 在 【Symbol Search Path】 对话框中加入下面的路径:srv*D:\SystemSymbols*http://msdl.microsoft.com/download/symbols
其中: D:\SystemSymbols为存放符号的下载目录
4.建立自己的符号表服务器
通过为产品建立自己的符号表服务器,就不需要关心复杂的版本问题。在调试过程中,windbg可以根据二进制文件(exe/dll)的哈希值,在符号表服务器上自动查找正确版本的PDB。
(1)建立一个共享文件夹,比如 “ \\server\symbols” ,然后将所有的PDB文件上传到此处
(2)使用下面的命令上传符号表文件
symstore add /r /f D:\MyOutput /s \\server\symbols /t "chess" /v "1.100.20"
symstore 是windbg附带的一个exe文件,存放于windbg安装目录下。
上述命令完成的功能是:在D:\MyOutput目录下面查找二进制文件(exe/dll)和PDB文件,
并把它们上传到\\server\symbols目录中。产品的名称是"chess",版本信息是"1.100.20"。
(3)在windbg的 【Symbol Search Path】对话框中添加新的路径(路径之间使用分号[;]分隔)\\server\symbols(关联一个本地缓存目录)
srv*D:\SystemSymbols*http://msdl.microsoft.com/download/symbols;srv*D:\ServerSymbols*\\server\symbols;f:\symbols
#使用windbg
0. Attach方式
启动windbg,使用菜单【Attach to a Process】,可以选择注入到现在正在运行的某个进程中。
注入到进程中后,进程被暂停执行,windbg处于可输入命令状态,但此时当前线程为windbg产生的线程。
如果要观察主线程堆栈,需要切换到主线程(0号线程)(在windbg命令窗口输入这个命令,然后回车):
~0s
当程序出现CPU占用100%的情况时,通常可以使用这种方式进行注入,然后切换到占用CPU的线程,
同时根据需要使用单步跟踪、设置断点等手段,来判断是何处的代码导致CPU100%问题。
1. Open Executable方式
使用【Open Executable】方式,可以通过windbg启动被调试程序。在这种方式下,被调试程序从启动时刻起就在调试器的监控之下。
对于一些在程序启动过程中产生的异常,可以使用这种方式进行调试。
2. Just in time Debugger 方式 (JIT)
当进程产生结构化异常时,如果进程不作处理,也没有调试器在监控这个进程,那么windows就会调用默认的调试器来调试发生异常的进程。
通过把windbg设置为Just in time Debugger,可以在任意进程发生异常时自动调用windbg来进行调试。
在注册表编辑器中修改下面的注册表项:
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\AeDebug\Debugger
修改为下面的取值 "D:\Tools\windbg.exe " -p %ld -e %ld
注:将例中windbg.exe的路径替换为本机windbg绝对路径即可。
3. Dump文件
为了分析程序发生异常的原因,还可以借助Dump文件。
当异常发生时,把进程当前的信息保存到一个Dump文件中,再把该Dump文件发送给分析者,由分析者通过windbg进行分析。
Dump分类:
内核dump:(蓝屏后,由系统生成)
1. 完全内存转储。这个文件比较大,和物理内存相当,包含了程序崩溃前系统及用户模式下的所有信息。
2. 核心内存转储。这个文件大小约物理内存的三分之一,主要包含崩溃前系统内核的所有信息。一般为了分析内核错误,就选用这种文件。
3. 小内存转储(MiniDump)。保存内存前64KB的基本空间数据,主要包含crash进程及crash线程内核上下文信息,crash线程内核模式堆栈,加载的驱动和模块等信息。
不同信息量的minidump:
(1)标准的minidump。包含了相对比较少的信息,适合在做在线分析:系统信息、加载的模块(DLL)信息、进程信息和线程信息。
.dump /m c:\stardardMini.dmp(注:不指定/m和/f,/m为缺省选项)
(2) full dump。在内核模式下,生成完全内存转储;用户模式下,最好不使用(/ma和/mf是更好的选择,生成出的dump信息也更丰富一些)
.dump /f c:\fullMini.dmp
(3)带有尽量多选项的minidump。包括完整的内存内容、句柄、未加载的模块,等等。文件很大(本机和局域网环境适用)~
.dump /ma c:\bigMini.dmp(注:/ma等价于/mfFhut;/m的子参数包括:a,A,f,F,h,u,t,i,p,w,d,c,r,R ).dump命令详细用法
下面列出六种生成Dump文件的方法:
(1)在产品代码中加入自动生成Dump文件功能
使用下面的函数,可以生成一个Dump文件:
BOOL MiniDumpWriteDump (
HANDLE hProcess,
DWORD ProcessId,
HANDLE hFile,
MINIDUMP_TYPE DumpType,
PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
PMINIDUMP_CALLBACK_INFORMATION CallbackParam
);
这个函数定义在dbghelp.dll中,windbg安装目录中带有这个dll,同时在sdk子目录中提供了.h和.lib文件。
在进程捕捉结构化异常的地方,使用这个API生成一个Dump文件。
(2) 使用AdPlus生成Dump文件
windbg附带了一个AdPlus的脚本,可以用于监控进程运行情况,并且可以在发生异常时把进程信息写入到Dump文件中。
如果产品没有自动生成Dump文件的功能,可以通过使用这个工具来弥补缺陷。
通过如下命令来使用AdPlus:
cscript D:\tools\windbg\adplus.vbs -crash -pn DebugTest.exe -o D:\CrashDumps
上述命令的意思是:监控当前运行的DebugTest.exe,如果发生了结构化异常,则生成一个Dump文件到D:\CrashDumps目录中。
关于AdPlus更详细的介绍请参阅:http://support.microsoft.com/kb/286350/zh-cn
(3) 使用windbg生成Dump文件
windbg可以通过命令生成mini-Dump文件:
.dump /mfh D:\CrashDumps\mydumpfile.dmp
(4) win7任务管理器 - 进程标签页 - 创建转储文件
(5) 使用vs2010以上版本生成 在调试状态下[菜单]:调试 - 将转储另存为
(6) 第三方系统工具 如:最新版本的Process Explorer、proccump.exe命令行工具等
注意:
win32程序在64位操作系统上,需要生成dump时,应该生成32位的dump,否则windbg在获取call stack时可能出错,一些sos、psscor2等扩展无法读取dump中的数据,一些命令在获取dump中的信息也会受限。
对于32位dump,应该用x86版的windbg分析(无论是在32位还是64位操作系统上);64位dump,应该用在64位操作系统上使用x64版的windbg进行分析。
分析Dump文件
使用菜单【Open Crash Dump】打开Dump文件,然后打开堆栈观察窗口,此时看到的堆栈可能不是异常发生时的堆栈。
通过如下命令切换到异常发生时的堆栈:
.ecxr
为了分析异常,可以通过下面的语句来获取分析信息,帮助定位问题:
!analyze -v
#参考
http://www.debuginfo.com/resources.html
DbgHelp Functions: http://msdn.microsoft.com/en-us/library/ms679291(VS.85).aspx