个人技术分享

1. 可以用everything软件工具搜索本机。如果没有windbg的话,可以去下载一个(根据需求选择x64或x86)。现在微软不提供单独的windbg下载,可以下载一个完整的windows sdk,在安装时只选择windbg即可。

2. 双击打开WinDgb.exe软件,File->Open Crash Dump。可能一打开dump,就找到了崩溃发生的位置,此时命令行前面的数字就表示了崩溃的线程编号"0:threadId>",比如0:128>就表示崩溃发生在128号线程。

3. 命令行输入".excr",定位到发生崩溃线程上下文位置。或者执行~128s,手动切换到128号线程上下文。

4.在输出信息中有“模块名!函数名+偏移地址offset1”, 指明了发生崩溃的位置。通过"?模块名!函数名+偏移地址offset1"计算崩溃的具体地址,记作pCrash,再通过"lm m 模块名"查看该模块此次被加载到进程中的基地址,记作pMbase。则该函数相对于模块的偏移地址offset0 = pCrash-offset1-pMbase,只要软件没更新,offset0和offset1每次运行都是不变的。如果找不到pdb符号文件,excr 输出信息中可能会没有函数名,即"模块名+offset",这个offset为offset0+offset1,如果想看这个崩溃位置所在的函数,可以用kn 指令查看当前线程的函数调用栈,最上面那一行就是目标函数信息,这样也可以推算出offset0和offset1。

6. 如果看不到模块信息,可能是pdb 符号文件没找到,可以手动增加一个搜索路径:".sympath+ pdb文件所在文件夹",然后执行reload 指令".reload",重新加载符号

7. u查看指定地址处的反汇编指令(u 模块名+(offset0+offset1)),默认是可以看到从指定地址起始的后面连续8条汇编指令。如果想看到更多,可以用"u 地址 L行数"来查看指定行数的汇编指令。如果想看从给定地址往前的汇编指令,可以用"up 地址 L行数",如果所给地址是某个函数的地址,也可以用"uf 地址"查看该函数的完整反汇编指令。

8. ~*kn 可查看所有线程的信息,包括线程编号和线程调用栈;~3s 可切换到3号线程,然后执行kp/kn/kv查看当前线程调用栈相关信息。

9. 如果还是找不到问题发生的原因,可以到开发环境进一步分析对应的模块反汇编代码(通过IDA逆向工具打开对应二进制模块,将IDA View-A中的内容导出到文本文件:Edit->Select all->Copy)。在模块的反汇编文件中,基地址是.textbss的Imagebase后面的数字,可以根据第4步算出来的offset0和offset1,叠加上这个基地址,就可以定位到出错的指令位置。如果因为软件版本不同,offset0和offset1已经不准确了,那可以用第5步看到的完整函数汇编代码段,尝试去匹配反汇编文件中的位置(因为虽然软件版本不同,但是某个函数内部逻辑大多数情况下不会发生太大变化)。

10. 有dump的情况下,通过事后静态分析dump定位问题还是相对容易的。如果没有dump,就需要采用动态调试的方法,将WinDbg附加到进程中,等待错误复现,触发WinDgb中断。由于很多时候问题并不是必现的,并且生产环境挂WinDbg会受到这样那样的限制(客户不一定允许这样做),所以在开发软件时,尽可能要做好dump抓取。一个常见的崩溃后不生成dump的原因,可能是在软件发生异常时调用了exit系统函数(abort也一样,它内部调用了exit),直接退出了进程,无法创建dump。我们在一些第三方开源库经常会看到开源库里面有exit调用,使用这些库时尤其要小心,要了解清楚它的异常发生条件,在软件异常退出时,要排查是不是这些异常条件被触发。此外,c++程序在申请动态内存时,少用malloc,尽量用new,因为如果因内存不足导致申请失败时,malloc内部会调用exit,而new则是抛出alloc_bad异常,后者是可以生成dump的。

11. 如果确认问题发生在某个函数内部,可以使用windbg动态调试:首先,将windbg附加到进程,使用"x 模块名!函数名"查看函数的地址,然后使用"bp 地址"在该函数上加断点(如:bp 00007ffc`1a72dd00),然后输入g指令,让软件运行起来,等待触发断点, 使用p或t指令单步调试,dv/dt查看局部变量,也可以在菜单栏,选择locals,显示当前函数的局部变量(整形变量为0nxx)。用x指令查看函数地址的时候,可以对符号进行模糊匹配,如" x TestModule!*CheckState* "就可以列出TestModule模块中所有带"CheckState"的函数,在查类的成员函数时也适用。查到的地址可能类似"00007ffc`1a72dd00", 就表示该函数的地址为0x7ffc1a72dd00,前面的7ffc表示整个模块的基地址为0x00007ffc00000000,后面的1a72dd00表示该函数在该模块中的偏移。

12. 有些复杂场景下windbg不能自动定位到异常发生位置,此时需要手动寻找异常点: windbg入门教程之复杂场景下crash分析_windbg crash-CSDN博客