.Net高级调试-基本调试任务-一些命令

符号

.sympath 不带参数,显示符号的路径,参数可以是符号的文件夹
.reload 枚举进程中的所有模块,并尝试找出与各个模块相关的符号文件
.symfix 自动将符号路径设置为Microsoft公有符号服务器
.sympath+ 将另一个文件夹添加到现有符号路径列表中

控制调试目标

中断执行

ctrl + c 来中断程序执行

恢复执行

g 不带任何参数,只是恢复调试目标的执行,直到下一次发生某个调试事件

如果不希望调试器在初始启动时停止程序的执行,在启动调试器时加 -gntsd -g a.exe

单步调试代码

p step
p 的变种 pt 会一直执行指令,直到遇见一个Ret指令。
pc可以很快地执行到下一个Call指令

t trace 执行单条指令,并显示所有寄存器的结果。命令t在执行call指令或者中断指令时的行为
t的变种
ta <address> 执行到address指定的地址,并将包含被调用函数的单步执行显出来
tc 执行到下一个call指令,并将包含被调用函数的单步执行显示出来。
tt 执行到下一个ret 指令,并将包含被调用函数的单步执行显示出来。

退出调试会话

q 退出调试会话并终止调试目标
qd quit and detach 结束调试会话,但让调试目标继续运行。

加载托管代码调试的扩展命令

非托管调试器中可以使用两种不同类型的命令

  • 元命令 指在调试引擎中内置的命令。如help sympath cls 等,必须在命令前面加上前缀"."
  • 扩展命令 是在调试器引擎之外的独立DLL中实现的,这些DLL也被称为调试器扩展。在执行扩展命令时,要在命令前面加上前缀 “!” ,如!htrace -enable ,在调试托管代码时,有两个DLL需要注意,它们是SOS 和SOSEX
    在使用这些扩展DLL之前,必须通过元命令load 来通知调试器。 如.load c:\abc.dll

加载SOS调试器扩展

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调试器扩展

SOSEX可以用于调试托管代码。增强了SOS的功能,使某些特定的调试任务更高效。
加载SOSEX的命令
.load sosex.dll
或指定sosex.dll的完整路径。
虽然在没有加载mscorwkd.dll的情况下也能加载sosex扩展,但这些命令本身并不能工作。

控制CLR的调试

在调试.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编译生成的函数上设置断点

先让目标程序运行一次函数,确保已经被JIT编译器编译,然后通过SOS命令 name2ee 来判断
!name2ee <module name> <type or method name>
如果在最后一行输出中 这个方法的状态为JITTED 表示已被 JIT编译器编译过了,并且会给出地址。
可以通过命令U 对这一段代码做一个简单的完整性检查。
!U <address>

!ClrStack 来查看代码位置

在还没有被JIT编译的函数上设置断点

bpmd 用来在还没有被JIT编译的代码上设置断点。它采取的做法是设置一个延时断点,在设置该断点时,断点的位置是未知的,只有将来某个事件发生时,才会真正地设置断点。

在预编译的程序集中设置断点

使用ngen.exe 预编译 !name2ee 可以直接断点指定的函数。

在泛型方法上设置断点

!bpmd  aaa.exe XXX`1.method

对象检查

内存转储

dd <address> 查看内存
du <address> 把被转储的内存视作Unicode字符
da <address> 把被转储的内存视作ASCII字符
dw <address> 把被转储的内存视作字word
db <address> 把被转储的内存视作字节值和ASCII字符
dq <address> 把被转储的内存视作四字quad word 值

!dumpobj <addr> 可以转储更多信息

值类型的转储

如何判断一个指针指向的是否是值类型?用DumpObj命令试一下就知道了,如果给定的指针指向一个值类型,会报错。
!DumpObj <addr>
如果要显示插管调用栈及相关的局部变量,可以通过ClrStack命令来获取。
!ClrStack -a

如果ClrStack提示错误,指出当前线程上下文不是一个有效的托管线程,需要先切换线程上下文。使用 ~ 将上下文切换到线程0 再执行ClrStack
~0s

通过命令r将寄存器转储出来。

!DumpVC <方法表地址> <地址> 给出方法更为详细的信息,如值类型的名字和大小。

转储基本的引用类型

!DumpObj [-nofields] <obj addr> DumpObj会转储出类型信息及相关的域
这个命令可以缩写为 do

数组的转储

!DumpArray -details <addr> 输出数组的详细信息 DumpArray会自动识别出正在处理的是值类型还是引用类型。

栈上对象的转储

大多时候可以用ClrStack 命令来找出每个栈帧的参数和局部变量
DumpStackObjects 可以对栈进行遍历,并输出栈上的所有托管对象。语法:
!DumpStackObjects [-verify] [top stack [bottom stack]]

如果没有指定任何参数,那么DumpStackObjects 会输出当前线程的所有托管对象。
DumpStackObjects 太长,可以缩写成dso ??

找出对象的大小

对象的大小表示这个类型所占据的内存字节数量。
!DumpObj 可以获得对象大小
通常,对象会引用其它对象,如果要获得对象的总体大小(包括遍历每个类型域的大小)可以使用ObjSize命令
!ObjSize <addr> 如果没有指定地址,那么这个命令将列出进程中所有托管线程中所有对象大小。

异常的转储

Windows在实现异常模型时采用的方法之一就是结构化异常处理(SEH),CLR在每个异常内携带的额外信息被保存在托管堆上。异常是一种引用类型,所有CLR异常都以SHE异常形式出现,错误码为0xe0434f4d

kb来输出调用栈。

SOS调试器扩展中包含一个命令 PrintException 参数是托管异常的地址,能以更容易理解的形式输出异常信息。
!PrintException <addr>
另一个有用的命令是 Threads 能显示出系统中各个托管线程的信息,包括该线程抛出的最后一个异常。
!Threads
StopOnException 这个命令的作用是在抛出特定异常时设置一个断点。语法
!StopOnException [-derived] [-create | -create2] <Exception> [<pseudo-register number>]

!StopOnException -create System.ArgumentException

线程的操作

SOS调试器扩展提供了一组线程命令

ClrStack

!ClrStack 可以输出线程ID和托管调用栈的所有栈帧
!ClrStack -l 用于显示局部变量信息(没有名字)
得到变量的地址后,可以用!DumpObj <addr> 来输出变量信息
!ClrStack -p 将显示调用栈上每个托管代码栈帧的所有参数。

Threads

!Threads 可以枚举进程中的所有托管代码线程。
Threads命令包含了一组开关,-live开关将Threads命令限制为只输出那些活跃状态的线程信息。 -special 开关表示输出进程的所有特殊的线程,如垃圾收集线程,调试器线程,线程池定时器线程等。

DumpStack

ClrStack只给出托管代码调用栈,k系列命令只给出非托管调用栈。要同时转储出托管代码调用栈和非托管代码调用栈,可以使用DumpStack命令。

DumpStack 使用-EE 开关表示只显示托管函数。

EEStack

EEStack 会对进程中每个活跃的线程调用DumpStack
两个开关: -short 只输出感兴趣的线程调用栈。即 (这个线程持有一个锁。线程被劫持以执行一个垃圾收集操作。线程当前正在托管代码中执行) -EE 这个开关会直接传给DumpStack命令,表示只显示托管代码调用栈。

COMState

当与COM子系统一起使用时,重点是要知道COM提供的不同套间模型。COM提供了两种主要的套间模型。1,单线程套间 STA 2,多线程套间 MTA 。每当一个线程希望使用COM对象时,它必须告诉COM子系统它需要使用哪一种套间模型。在具体套间模型中对线程进行初始化的概念非常重要,在调用COM互用性问题时,找出线程的套间模型是一个更为重要的方面。
COMState 可以找出系统中每个线程的套间模型。
!COMState

代码审查

反汇编代码

命令u 把代码字节流反汇编为汇编指令,因而能很容易推断出代码所要执行或者曾经执行的功能。
u适用于非托管代码,对于托管代码可以使用 !U <addr> U命令除了指定代码地址外,还可以指定一个方法描述符。

从代码地址上获得方法描述符

!IP2MD <code addr> 将任意的托管代码地址转为一个方法描述符,然后就可以使用DumpMD命令获得进一步的信息。

显示中间语言指令

!DumpIL <addr> 来查看函数的IL
该命令以方法描述符的地址作为参数,从IP2MD 可以得到方法描述符地址。

CLR内部命令

获得CLR的版本

!EEVersion

根据名字找到方法描述符

如果有了某个方法的名字后,找出方法描述符最有用的方法之一就是Name2EE命令 ,其参数是模块的名字和方法的全名,这个命令将输出方法的一些信息,包括方法描述符。

对象同步块的转储

每个CLR托管类型都有一个相应的同步块,用于实现同步行为。 SyncBlk 命令可以用来获得这个同步块的详细信息,这个命令在分析死锁问题时非常有用。

对象方法表的转储

每个托管对象都有一个相应的方法表,其中包含了该对象的一些信息。DumpMT命令可以用来显示方法表的信息,命令参数是方法表的地址。

托管堆和垃圾收集器信息的转储

命令描述
DumpHeap遍历托管堆,收集并输出这个堆以及位于堆上所有对象的详细信息
GCRoot显示对某个对象的引用(根对象)信息。当要找出某个对象为什么还没有被收集时,这将是非常有用的信息
TraverseHeap遍历托管堆,并把遍历结果输出到一个文件中,由CLR分析器来进行分析
VerifyHeap与任何堆一样,托管堆可能被破坏,这个命令将验证托管堆的完整性

诊断命令

找出对象的应用程序域

要找出指定的对象实例位于哪一个应用程序域中,可以使用 FindAppDomain 命令,知道了对象的应用程序域之后,可以使用 DumpDomain 来获取应用程序域的进一步信息。

进程信息

用ProcInfo命令
!ProcInfo [-env] [-time] [-mem] 如果不加参数会显示所有这三类信息。

SOSEX扩展命令

扩展的断点支持

断点列表

!mbl 显示所有的断点
mbc 将从列表中清除指定的断点,或清除所有断点
mbd 将禁用列表中指定的断点,或禁用所有的断点
mbe 将启用列表中指定的断点,或启用所有的断点

设置断点

mbp 这个命令可以在任意给定的源代码位置上设置断点。
mbm 可以在特定类型的指定IL偏移处设置一个断点。
!mbm *!Advanced.NET.Debugging.Chapter3.Simple.Main 0
mbm 的选项
!mbm <strTypeAndMethodFilter> <intILOffset> [Options]
其中Options 可以是 /1 只触发这个断点一次。 /p: 表示在第几次执行到断点时才停止执行程序。 /t: 只有由threadID指定线程才可以触发这个断点。
mbm 支持在strTypeAndMethodFilter中使用通配符。

托管元数据

如果想找出类型名和方法名,可以使用mx命令,语法为 !mx <filterString> 其中参数 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 工作原理是抓取指定进程的快照,并以离线方式对快照进行调试。
快照只是一个二进制文件,其中包含了抓取快照时进程所处的状态。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值