windbg查询内存泄笔记
前段时间一直在做项目的压力测试,奈何天意弄人,测试一直在出问题,从数据库,到服务器cpu,再到内存,不停地在调试,检测,修改,再调试。
下面就将windbg的使用心得总结下。
1:先要下dump,为了分析内存泄露,至少要下好几个dump比较。
2:下完dump后,就可以使用windbg打开该dump了,使用!dumpheap -stat命令,分析其中的内存分布,比较各个dump的可疑之处。初步确认几个可疑的内存泄露。
3:确认可疑泄露支出后,使用!dumpheap -type 命令,以及!gcroot,查询泄露的出处。
4:检查上一步的几个源码,分析内存泄露。
1. 什么是windbg
WinDbg从字面意思就是Windows+Debug的组合,即Windows平台上的调试工具,可以调试用户模式、内核模式、dump文件等,总之知道它的调试功能非常强大就行了。WinDbg调试命令分为3种,分别是基本命令、元命令和扩展命令。基本命令和元命令是调试器自带的,元命令总以".“开头,而扩展命令总以”!"开头。
2. 开始设置windbg
.WinDbg默认的设置路径
File -> Settings -> Debugging settings,默认的源码路径、符号路径和缓存路径如下:
设置_NT_SYMBOL_PATH环境变量
设置_NT_SYMBOL_PATH环境变量的值为SRV*D:\mysymbol*https://msdl.microsoft.com/download/symbols:
https://learn.microsoft.com/zh-cn/windows-hardware/drivers/debuggercmds/commands
常见非托管命令
1.工具和系统级别类
- .restart // 重启并调试
- .kill // 强制结束当前调试
- q // 结束当前调试会话,回到基础工作空间,并结束调试进程
- qd // 结束当前调试会话,回到基础工作空间,但不结束调试进程
- vertarget // os信息
- !cpuid // cpu信息
- .time // 获取dump抓取时间
- ? // 打印出所有标准命令
- .help // 打印出所有元命令
- .hh // 打开windbg的chm帮助文件
- .cls // 清空控制台
- !analyze -v // 对内存转储文件进行分析,并显示详细的结果
2.进程和线程类
- | // 列出调试进程
- |* // 列出调试进程
- ~ // 列出线程
- ~* // 所有线程
- ~* k // 所有线程堆栈信息
- ~* r // 所有线程寄存器信息
- ~. // 查看当前线程
- ~0s // 查看主线程
- ~N // 查看序数为N的线程
- ~~[n] // 查看线程ID为n的线程
- ~N f // 冻结序数为N的线程
- ~N u // 解冻序数为N的线程
- ~N n // Suspend序数为N的线程
- ~N m // Resume序数为N的线程
- ~* e !gle // 显示所有线程最后的一个错误信息 e后可以为任意windbg命令
- !runaway //显示当前进程的所有线程时间信息
3. 断点类
- bl // 列出所有断点
- bc * // 清除所有断点
- bc 1 // 清除1号断点
- bc 1 2 5 // 清除1号、2号、5号断点
- be * // 启用所有断点
- be 1 // 启用1号断点
- bd * // 禁用所有断点
- bd 1 // 禁用1号断点
- bp bc701a00 // 在bc701a00地址处放置一个断点
- bp Test.cpp:36 // 在Test.cpp的36行处放置一个断点
- bp main // 在main函数的起始处放置一个断点
- bp Test.cpp:40 ".if (poi(pVar)>5) {}; {g}" // ".if (Condition) {Optional Commands}; {g}" // 条件断点 pVar指针指向的值>5,执行空语句(;),断住 否则继续执行
4. 调试控制类
- g // Go(F5)
- gN //【Go with Exception Not Handled】执行gN命令强制让调试器返回没有处理了这个异常,那么系统会进一步分发该异常,
- gu // 执行到当前函数完成时停下 【Go Up】
- Ctrl+Break // 暂停正在运行的程序
- p // 单步执行(F10) 【Step】
- p 2 // 2为步进数目
- pc // 执行到下一个函数调用处停下 【Step to Next Call】
- pa 7c801b0b // 执行到7c801b0b地址处停下 【Step to Adress】
- t // Step into(F11) 【Trace】
- tc // 执行到下一个进入点处停下 【Trace to Next Call】
- ta 7c801b12 // 执行到7c801b12地址处停下 【Trace to Adress】
5. 查看句柄类
- !handle // 查看所有句柄的ID
- !handle 000007f8 1 // 查看ID为000007f8的句柄的类型
- !handle 000007f8 4 // 查看ID为000007f8的句柄的名称
- !handle 0 5 // 查看所有句柄的类型和名称
6. 线程栈类
- kb 5 // 只显示最上的5层调用堆栈
- kv // 在kb的基础上增加了函数调用约定等信息
- kp // 显示每一层函数调用的完整参数,包括参数类型、名字、取值(必须是完整符号的情况下,private symbols);注意:若程序被优化,这些值不一定对
- kd // 打印堆栈的地址
- .frame // 显示当前栈帧
- .frame n // 设置编号n的栈帧为当前栈帧(n为16进制数)
- .frame /r n // 设置编号n的栈帧为当前栈帧(n为16进制数) 并显示寄存器变量
- !uniqstack // 显示所有线程的调用堆栈
7. 汇编和寄存器类
- u . // 反汇编当前ip寄存器地址的后8条指令
- u $ip // 反汇编当前ip寄存器地址的后8条指令
- ub . // 反汇编当前ip寄存器地址的前8条指令
- ub $ip // 反汇编当前ip寄存器地址的前8条指令
- u main+0x29 L30 // 反汇编main+0x29地址的后30条指令
- u // 反编译下8条指令
- uf CTest::add // 反汇编CTest类的add函数
- uf /c main // 反汇编main函数,通过/c可以查看main函数中的函数调用(call)都有哪些
- ub 000c135d L20 // 查看地址为000c135d指令前的20条指令内容
- r // 显示所有寄存器信息及发生core所在的指令
- r eax, edx // 显示eax,edx寄存器信息
- r eax=5, edx=6 // 对寄存器eax赋值为5,edx赋值为6
6. 地址和进制表示类
- dt // 显示数据结构信息,可以用来查看特定内存地址处的数据结构。
- hi(x) // 高16 bits
- low(x) // 低16 bits
- by(x) // 返回第一个byte
- wo(x) // 返回第一个word
- dwo(x) // 返回第一个dword
- qwo(x) // 返回第一个4 word(Quad-word)
- poi(x) // 返回第一个指针所指向的值
- 0n(十进制) 0x (十六进制)0t (8进制) 0y (2进制)
7. 符号模块
- .sympath // 查看当前符号查找路径
- .sympath SRVC:\mysymbolshttp://msdl.microsoft.com/download/symbols // 设置本地和远程的符号路径
- !sym noisy // 激活详细符号加载(noisy symbol loading)显示
- .reload // 为所有已加载模块载入符号信息
- ld * // 为所有模块加载符号.symfix
- ld kernel32 // 加载kernel32.dll的符号
- lm // 列出所有模块(加载和未加载)对应的符号信息
- lmv // 列出所有模块(加载和未加载)对应的符号信息
常见托管命令
1. 常用命令
- !threadpool // 查看线程池CPU使用量
- !threads // 查看所有托管线程情况
- !clrstack // 某个线程托管代码的调用栈情况
- ~*e!clrstack // 所有线程托管代码的调用栈情况
- !runaway // 查看线程占用CPU时间
- !dumpstackobjects(!dso) // 本线程调用栈所有对象实例
- !dumpdomain // 显示所有域里的程序集,或者根据参数获取指定域。
- !savemodule // 根据具体程序集地址,把当前程序集的代码生成到指定文件
- !PrintException(!pe) // 显示在当前线程上引发的最后一个异常错误信息
- !VerifyHeap // 检查垃圾回收器堆中是否有损坏迹象,并显示找到任何错误
- !syncblk // 显示同步块表信息
- !dumpheap -stat -type
- !gcroot