.sympath
不带参数,显示符号的路径,参数可以是符号的文件夹
.reload
枚举进程中的所有模块,并尝试找出与各个模块相关的符号文件
.symfix
自动将符号路径设置为Microsoft公有符号服务器
.sympath+
将另一个文件夹添加到现有符号路径列表中
按ctrl + c
来中断程序执行
g
不带任何参数,只是恢复调试目标的执行,直到下一次发生某个调试事件
如果不希望调试器在初始启动时停止程序的执行,在启动调试器时加 -g
如 ntsd -g a.exe
p
step
p 的变种 pt
会一直执行指令,直到遇见一个Ret指令。
pc
可以很快地执行到下一个Call指令
t
trace 执行单条指令,并显示所有寄存器的结果。命令t在执行call指令或者中断指令时的行为
t的变种
ta
执行到address指定的地址,并将包含被调用函数的单步执行显出来
tc
执行到下一个call指令,并将包含被调用函数的单步执行显示出来。
tt
执行到下一个ret 指令,并将包含被调用函数的单步执行显示出来。
q
退出调试会话并终止调试目标
qd
quit and detach 结束调试会话,但让调试目标继续运行。
非托管调试器中可以使用两种不同类型的命令
!htrace -enable
,在调试托管代码时,有两个DLL需要注意,它们是SOS 和SOSEX.load c:\abc.dll
SOS调试器扩展的DLL (sos.dll)与程序使用的CLR版本是相关的。因此需要加载与目标程序CLR版本一致的sos.dll
如.load c:\windows\Microsoft.NET\Framework\v2.0.50727\sos.dll
这个方法太难用,我们可以用另一个元命令 loadby 语法如下
.loadby DLLName ModuleName
元命令将会找出由ModuleName指定的路径,并且使用这个路径来加载指定的DLLName
例如正在查找的模块是 mscorwk,则只需执行以下命令
.loadby sos.dll mscorwks
如果目标程序的mscorwks模块还没有被加载,那么loadby 将提示错误信息。
如果需要在加载mscorwks模块时立即加载SOS调试器扩展,那么可以使用sxe
命令
sxe ld
可以使得在加载某个特定的模块后,立即中断进入到调试器,然后加载SOS调试器扩展
如 sxe ld mscorwks.dll
SOSEX可以用于调试托管代码。增强了SOS的功能,使某些特定的调试任务更高效。
加载SOSEX的命令
.load sosex.dll
或指定sosex.dll的完整路径。
虽然在没有加载mscorwkd.dll的情况下也能加载sosex扩展,但这些命令本身并不能工作。
在调试.Net程序时,调试器可以加载一个辅助DLL 称为mscordacwks.dll ,这个dll用于输出托管代码调试过程中的各种信息,这个dll的路径取决于mscorwks.dll的路径。在实时调试中不会有问题,但在事后调试时可能出现版本不匹配的情况。可以使用cordll来告诉调试器加载mscordacwks.dll的确切位置
.cordll -lp c:\x\y\z
从文件夹c:\x\y\z下加载mscordacwks.dll
如果要卸载mscordacwks.dll可以使用-u 开关。
非托管调试中使用bp命令来断点。
使用X 来显示包含 指定函数字符串的所有符号 ,bp的参数可以是 函数名,或地址。
先让目标程序运行一次函数,确保已经被JIT编译器编译,然后通过SOS命令 name2ee 来判断
!name2ee
如果在最后一行输出中 这个方法的状态为JITTED 表示已被 JIT编译器编译过了,并且会给出地址。
可以通过命令U 对这一段代码做一个简单的完整性检查。
!U
!ClrStack
来查看代码位置
bpmd
用来在还没有被JIT编译的代码上设置断点。它采取的做法是设置一个延时断点,在设置该断点时,断点的位置是未知的,只有将来某个事件发生时,才会真正地设置断点。
使用ngen.exe 预编译 !name2ee
可以直接断点指定的函数。
!bpmd aaa.exe XXX`1.method
dd
查看内存
du
把被转储的内存视作Unicode字符
da
把被转储的内存视作ASCII字符
dw
把被转储的内存视作字word
db
把被转储的内存视作字节值和ASCII字符
dq
把被转储的内存视作四字quad word 值
!dumpobj
可以转储更多信息
如何判断一个指针指向的是否是值类型?用DumpObj
命令试一下就知道了,如果给定的指针指向一个值类型,会报错。
!DumpObj
如果要显示插管调用栈及相关的局部变量,可以通过ClrStack命令来获取。
!ClrStack -a
如果ClrStack提示错误,指出当前线程上下文不是一个有效的托管线程,需要先切换线程上下文。使用 ~
将上下文切换到线程0 再执行ClrStack
~0s
通过命令r
将寄存器转储出来。
!DumpVC <方法表地址> <地址>
给出方法更为详细的信息,如值类型的名字和大小。
!DumpObj [-nofields]
DumpObj会转储出类型信息及相关的域
这个命令可以缩写为 do
!DumpArray -details
输出数组的详细信息 DumpArray会自动识别出正在处理的是值类型还是引用类型。
大多时候可以用ClrStack 命令来找出每个栈帧的参数和局部变量
DumpStackObjects
可以对栈进行遍历,并输出栈上的所有托管对象。语法:
!DumpStackObjects [-verify] [top stack [bottom stack]]
如果没有指定任何参数,那么DumpStackObjects 会输出当前线程的所有托管对象。
DumpStackObjects 太长,可以缩写成dso ??
对象的大小表示这个类型所占据的内存字节数量。
!DumpObj
可以获得对象大小
通常,对象会引用其它对象,如果要获得对象的总体大小(包括遍历每个类型域的大小)可以使用ObjSize命令
!ObjSize
如果没有指定地址,那么这个命令将列出进程中所有托管线程中所有对象大小。
Windows在实现异常模型时采用的方法之一就是结构化异常处理(SEH),CLR在每个异常内携带的额外信息被保存在托管堆上。异常是一种引用类型,所有CLR异常都以SHE异常形式出现,错误码为0xe0434f4d
用 kb
来输出调用栈。
SOS调试器扩展中包含一个命令 PrintException
参数是托管异常的地址,能以更容易理解的形式输出异常信息。
!PrintException
另一个有用的命令是 Threads
能显示出系统中各个托管线程的信息,包括该线程抛出的最后一个异常。
!Threads
StopOnException
这个命令的作用是在抛出特定异常时设置一个断点。语法
!StopOnException [-derived] [-create | -create2]
!StopOnException -create System.ArgumentException
SOS调试器扩展提供了一组线程命令
!ClrStack
可以输出线程ID和托管调用栈的所有栈帧
!ClrStack -l
用于显示局部变量信息(没有名字)
得到变量的地址后,可以用!DumpObj
来输出变量信息
!ClrStack -p
将显示调用栈上每个托管代码栈帧的所有参数。
!Threads
可以枚举进程中的所有托管代码线程。
Threads命令包含了一组开关,-live开关将Threads命令限制为只输出那些活跃状态的线程信息。 -special 开关表示输出进程的所有特殊的线程,如垃圾收集线程,调试器线程,线程池定时器线程等。
ClrStack只给出托管代码调用栈,k系列命令只给出非托管调用栈。要同时转储出托管代码调用栈和非托管代码调用栈,可以使用DumpStack命令。
DumpStack 使用-EE 开关表示只显示托管函数。
EEStack 会对进程中每个活跃的线程调用DumpStack
两个开关: -short 只输出感兴趣的线程调用栈。即 (这个线程持有一个锁。线程被劫持以执行一个垃圾收集操作。线程当前正在托管代码中执行) -EE 这个开关会直接传给DumpStack命令,表示只显示托管代码调用栈。
当与COM子系统一起使用时,重点是要知道COM提供的不同套间模型。COM提供了两种主要的套间模型。1,单线程套间 STA 2,多线程套间 MTA 。每当一个线程希望使用COM对象时,它必须告诉COM子系统它需要使用哪一种套间模型。在具体套间模型中对线程进行初始化的概念非常重要,在调用COM互用性问题时,找出线程的套间模型是一个更为重要的方面。
COMState 可以找出系统中每个线程的套间模型。
!COMState
命令u 把代码字节流反汇编为汇编指令,因而能很容易推断出代码所要执行或者曾经执行的功能。
u适用于非托管代码,对于托管代码可以使用 !U
U命令除了指定代码地址外,还可以指定一个方法描述符。
!IP2MD
将任意的托管代码地址转为一个方法描述符,然后就可以使用DumpMD命令获得进一步的信息。
!DumpIL
来查看函数的IL
该命令以方法描述符的地址作为参数,从IP2MD 可以得到方法描述符地址。
!EEVersion
如果有了某个方法的名字后,找出方法描述符最有用的方法之一就是Name2EE命令 ,其参数是模块的名字和方法的全名,这个命令将输出方法的一些信息,包括方法描述符。
每个CLR托管类型都有一个相应的同步块,用于实现同步行为。 SyncBlk 命令可以用来获得这个同步块的详细信息,这个命令在分析死锁问题时非常有用。
每个托管对象都有一个相应的方法表,其中包含了该对象的一些信息。DumpMT命令可以用来显示方法表的信息,命令参数是方法表的地址。
命令 | 描述 |
---|---|
DumpHeap | 遍历托管堆,收集并输出这个堆以及位于堆上所有对象的详细信息 |
GCRoot | 显示对某个对象的引用(根对象)信息。当要找出某个对象为什么还没有被收集时,这将是非常有用的信息 |
TraverseHeap | 遍历托管堆,并把遍历结果输出到一个文件中,由CLR分析器来进行分析 |
VerifyHeap | 与任何堆一样,托管堆可能被破坏,这个命令将验证托管堆的完整性 |
要找出指定的对象实例位于哪一个应用程序域中,可以使用 FindAppDomain
命令,知道了对象的应用程序域之后,可以使用 DumpDomain
来获取应用程序域的进一步信息。
用ProcInfo命令
!ProcInfo [-env] [-time] [-mem]
如果不加参数会显示所有这三类信息。
!mbl
显示所有的断点
mbc 将从列表中清除指定的断点,或清除所有断点
mbd 将禁用列表中指定的断点,或禁用所有的断点
mbe 将启用列表中指定的断点,或启用所有的断点
mbp 这个命令可以在任意给定的源代码位置上设置断点。
mbm 可以在特定类型的指定IL偏移处设置一个断点。
!mbm *!Advanced.NET.Debugging.Chapter3.Simple.Main 0
mbm 的选项
!mbm
其中Options 可以是 /1 只触发这个断点一次。 /p: 表示在第几次执行到断点时才停止执行程序。 /t: 只有由threadID指定线程才可以触发这个断点。
mbm 支持在strTypeAndMethodFilter中使用通配符。
如果想找出类型名和方法名,可以使用mx命令,语法为 !mx
其中参数 filter string 表示查找元数据的通配符。
另一个有用的命令是 mln 参数是一个地址,可以识别出与该地址上内容相关的代码,可以准确地识别出JIT已编译的代码,堆及栈对象。
有各种不同的方式来显示插管堆线程的调用栈。非托管调试器命令kb,ClrStack 能显示托管代码调用栈
当要同时显示托管代码的调用栈和非托管代码的调用栈时,还可以使用SOSEX的mk命令,它可以显示帧的编号
mdv命令输出指定栈帧的局部变量。
mframe 可以设置当前托管栈帧
显示局部变量,全局变量或某个数据类型的信息。
!mdt [typename|paramname| localname|MT] [Addr] [-r]
-r 表示mdt命令将递归地执行。
SOSEX中的 dlk可以把找出哪些线程可能发生死锁的过程自动化。
gcgen 参数是托管堆上的对象地址,它能显示该对象所属的代。当要找出一个对象的存活时间(或者它已经过了多少次垃圾收集过程)时,这个命令将非常方便
dumpgen能输出指定代的所有对象。
strings 能搜索托管堆上的任意字符串。在搜索过程中可以指定通配符,要搜索的代,最小长度和最大长度。
如 !strings -m:Debug
-g 指定代 -n 指定最小长度 -x指定最大长度
事后调试 postmortem debugging 工作原理是抓取指定进程的快照,并以离线方式对快照进行调试。
快照只是一个二进制文件,其中包含了抓取快照时进程所处的状态。