[翻译+补充]WinDbg托管程序调试扩展与调试思路
在《.Net高级调试》中作者介绍了sos和sosex两种调试工具,最近在查如何实现结构类型的变量显示时,发现了些花里胡哨的WinDbg扩展。主要翻译自nuggets/README.md at main · gabrielweyer/nuggets (github.com),其中环境的配置部分在另一篇中已经记录就不再翻译了。看完以后排查问题就不要只知道sos和sosex扩展了。
推荐扩展简介
SOS
最有名的WinDbg扩展,介绍的比较多不再展开。
SOSEX
作者:Steve Johnson的杰作.(顺便吐槽一句老爷子会在StackOverflow亲自推销他写的这个,例如)
下载:
拷贝x64 / x86 DLLs 到指定路径:
C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\winextC:\Program Files (x86)\Windows Kits\10\Debuggers\x86\winext
如果没有,则查找 windbg.exe (where.exe /R C:\ windbg.exe).
加载
.load sosex
注意:
-
实际上可以不拷贝直接指定路径加载
.load c:\sosex.dll -
商店版本不需要拷贝路径。
MEX
官方使用的加强版sos,作用类似于sosex,参考。Windbg程序调试系列1-Mex扩展使用总结 - Eric zhou - 博客园 (cnblogs.com)
下载: MEX.
加载
.load mex
NetExt
可以方便的调试http request, wcf services等等。
参考官方Github文档 或 博客园。
下载: NetExt.
加载
.load netext
CMKD
看文档主要和非托管相关。
下载:
官方地址.
加载
.load cmkd
tracer
(这个没懂干啥的)
下载:
官方地址.
加载
.load tracer
常用命令列表
- 普通调试进程的命令 (e.g.:
k) - 点命令控制调试器 (e.g.:
.sympath) - 扩展命令(e.g.:
!handle)
WinDbg 命令
:star2: 获取最近异常:
!analyze -v
显示进程信息块 (PEB) - 包括环境变量和命令行
!peb
(F5) - 继续执行命令:
g
Objects per generation(这里没懂):
!FinalizeQueue
进制转换(看起来毫不需要)
0:000> ?12c0
Evaluate expression: 4800 = 00000000`000012c0
List of drivers:(这个命令也没懂,看起来是列出模块的意思)
lm t.lon
补充几个常用的
显示指定位置内存
dd <memory address>
dp <memory address> l1
// dp以指针显示
// dd以双字(4字节)显示
// l1表示只长度1
其他参考【调试技术】windbg帮助文档 (keenjin.github.io)
快捷键
- 中断当前操作:
Ctrl + Break key - 焦点到输入框:
ALT + 1
Session settings
在合适的地方增加超链接命令,不用手打了 (新版本默认了)::
.prefer_dml 1
SOS 命令
:star2: 列出所有SOS命令(或参考SOS.dll官方命令列表)
!sos.help
:star2: 列出类的所有方法
!DumpMT -md <MethodTable address>
SOSEX 命令
:star2: 列出所有SOSEX 命令
!sosex.help
:turtle: 创建堆的索引使得查找更快(build heap index):
!sosex.bhi
列出终结者队列的内容:
!sosex.finq
Freachable queue(遍历终结者队列?):
!sosex.frq
检测死锁:
!sosex.dlk
查看每一个托管线程正在等待什么 (如果有的话):
!sosex.mwaits
显示对象的字段, 可选递归:
!sosex.mdt address
设置托管代码断点
!sosex.mbp File.cs 13
MEX 命令
列出MEX 命令
!mex.help
:star2: ASP.NET: 列出请求的简要历史和正在执行的请求
!mex.aspxpagesext
托管内存相关
获取当前托管堆上的所有对象. 大对象在列表底部:
!dumpheap -stat
获取指定类型的对象:
!dumpheap -type Full.Namespace.Type
查看对象为什么存活:
!sosex.mroot <address>
查看托管堆:
!eeheap -gc
非托管内存相关
按类型统计内存:
!address -summary
异常相关
获取堆上的所有异常:
!dumpheap -type Exception -stat
查找指定异常:
!dumpheap -type System.Threading.ThreadAbortException
通过地址打印异常:
!pe 0x01c601ac
查看转储文件中所有异常:
!mex.dumpallexceptions
# 如果你想偷懒 则
!mex.dae
# 如果你想指定名称
!dumpheap -type Exception -stat
线程相关
:star2: 列出当前所有线程和他们进行的工作。
!mex.mthreads
或 !sos.threads
第一栏是线程ID,可以被我们用于后面的所有命令中 。
查看托管线程的堆栈:
~142e !sos.CLRStack
查看非托管线程的堆栈:
~142k
切换到线程:
~142s
:star2: :turtle: 根据堆栈分组线程
!mex.us
这个命令很好的区分了执行不同内容的线程 (执行相同的内容意味着他们能被相同的原因阻塞).
代码相关
反汇编:
!U \d <address>
交替显示反汇编和IL代码:
!sosex.mu <address>
锁相关
0:000> !syncblk
Index SyncBlock MonitorHeld Recursion Owning Thread Info SyncBlock Owner
52 20ee3118 229 2 20fc6ba0 9628 42 0a13ee8c System.Object
# Abbreviated
第三栏 (MonitorHeld)指示了多少线程正在尝试获取相同的锁. 在本例中是 (229 - 1) / 2 = 114. 你可以从 so answer了解更多.
概念
参考
- SO - WinDbg symbols resolution
- Pinpointing a Static GC Root with SOS
- Tess Ferrandez - New commands in SOS for .NET 4.0