Windows SDK 里面的Mdbg.exe 是一个命令行的CLR调试工具,在没有VS的情况下,可以较为方便调试.
下面演示一下如何使用这个工具 调试 一个winform 应用程序:MemLeakProfileDemo.exe
调试的前提是 pdb文件在,如MemLeakProfileDemo.pdb 在exe目录下会自动加载.
Sample code:
namespace MemLeakProfileDemo { public partial class Form1 : Form { private Fool fool; private FoolBrother brother; public Form1() { InitializeComponent(); fool = new Fool(); brother = new FoolBrother(); brother.YoungFool = fool; } private void btnAlloc_Click(object sender, EventArgs e) { var i = 10; fool.AllocalHugeMem(); }
mdbg> r MemLeakProfileDemo.exe
运行exe ,此时已经默认断点在 entrypoint上了,
我希望在 btnAlloc_Click 里断点,br 命令格式:
br modulename!namespace.class.methodname
[p#:1, t#:0] mdbg> br MemLeakProfileDemo!MemLeakProfileDemo.Form1.btnAlloc_Click
Breakpoint #1 bound (MemLeakProfileDemo!MemLeakProfileDemo.Form1::btnAlloc_Click(+0))
断点已打上.输入"g" 继续运行,点击界面按钮触发btnAlloc_Click 事件,断点被触发,断点在大括号处,
输入"s" 继续执行到下一行.
查看堆栈中的变量: 可使用 print i 打印 i值.
使用print 可以打印当前作用域所有变量:
[p#:1, t#:0] mdbg> print
i=10
this=MemLeakProfileDemo.Form1
sender=System.Windows.Forms.Button
e=System.Windows.Forms.MouseEventArgs
查看fool对象,即如下:
[p#:1, t#:0] mdbg> print this.fool
this.fool=MemLeakProfileDemo.Fool
list=System.Collections.Generic.List`1<System.Byte[]>
[p#:1, t#:0] mdbg> print this.fool.list
this.fool.list=System.Collections.Generic.List`1<System.Byte[]>
_items=array [0]
_size=0
_version=0
_syncRoot=<null>
_emptyArray=array [0]
另: 可以使用sh 命令显示源代码:
[p#:1, t#:0] mdbg> sh
26 private void btnAlloc_Click(object sender, EventArgs e)
27 {
28 var i = 10;
29:* fool.AllocalHugeMem();
30 }
31
*表示,当前代码执行到的地方.
是不是比windbg那个牛刀简单易用的多?
命令参数其他说明:
参照:
命令 | 说明 |
ap[rocess] [number] | 切换到另一个调试的进程或打印可用进程。number 不是真正的 PID,而是一个从 0 开始索引的列表。 |
a[ttach] [pid] | 附加到进程或打印可用进程。 |
b[reak] [ClassName.Method | FileName:LineNo] | 在指定方法处设置断点。按顺序对各个模块进行扫描。breakFileName:LineNo用于在源代码中的某个位置设置断点;break~number用于在当前使用x命令显示的符号处设置断点;breakmodule!ClassName.Method+IlOffset用于在完全限定位置设置断点。 |
ca[tch] [exceptionType] | 让调试器在遇到所有异常时都中断,而不仅是在遇到未处理的异常时中断。 |
conf[ig] [option value] | 显示所有可配置选项,并显示在不提供任何可选值的情况下如何调用选项。如果指定了选项,请将value设置为当前选项。 当前可用选项有: extpath:设置路径,使用load命令时在该路径中搜索扩展。 extpath+:向现有路径中添加路径,可以从这些现有路径加载扩展。 |
del[ete] | 删除断点。 |
de[tach] | 与调试的进程分离。 |
d[own] [frames] | 下移活动堆栈帧。 |
echo | 向控制台回显消息。 |
ex[it] [exitcode] | 退出 MDbg.exe 外壳程序,可以选择指定进程退出代码。 |
fo[reach] [OtherCommand] | 对所有线程执行命令。OtherCommand是对一个线程执行的有效命令;foreachOtherCommand则对所有线程执行同一命令。 |
f[unceval] [-ad Num] functionName [args ... ] | 对当前活动线程进行函数求值,其中,要求值的函数为functionName。函数名必须完全限定,包括命名空间。 -ad选项指定用于解析函数的应用程序域。如果未指定-ad选项,则用于解析的应用程序域默认为用于函数求值的线程所在的应用程序域。 如果要进行求值的函数不是静态的,则传入的第一个参数应为this指针。在所有应用程序域中搜索函数求值所需的参数。 若要从应用程序域中请求值,请在变量前使用模块名和应用程序域名作为前缀。例如funceval -ad 0 System.Object.ToString hello.exe#0!MyClass.g_rootRef。 此命令在应用程序域0中对函数System.Object.ToString进行求值。由于ToString方法为实例函数,因此,第一个参数必须为this指针。 |
g[o] | 让程序继续执行,直至遇到断点、程序退出代码或导致程序停止的事件(例如,未处理的异常)。 |
h[elp] [command] 或者 ? [command] | 显示所有命令的说明或对指定命令的详细说明。 |
ig[nore] [event] | 让调试器仅在遇到未处理的异常时停止。 |
int[ercept] FrameNumber | 将调试器回滚到指定的帧号码。 如果调试器遇到异常,使用此命令可以将调试器回滚到指定的帧号码。可以使用set命令更改程序状态,然后使用go命令继续。 |
k[ill] | 停止活动进程。 |
l[ist] [modules|appdomains|assemblies] | 显示已加载的模块、应用程序域或程序集。 |
lo[ad] assemblyName | 按以下方式加载扩展:加载指定的程序集,然后尝试从Microsoft.Tools.Mdbg.Extension.Extension类型运行静态方法LoadExtension。 |
mo[de] [option on/off] | 设置不同的调试器选项。option参数应为双字母对。 |
newo[bj] typeName [arguments...] | 创建typeName类型的新对象。 |
n[ext] | 运行代码并移动到下一行(即使下一行包含多个函数调用)。 |
o[ut] | 移动到当前函数的末尾。 |
pa[th] [pathName] | 如果二进制文件中的位置不可用,则在指定路径中搜索源文件。 |
p[rint] [var] | [-d] | 打印相应范围内的所有变量 (print),打印指定变量 (printvar),或者打印调试器变量 (print-d)。 |
pro[cessenum] | 显示活动进程。 |
q[uit] [exitcode] | 退出 MDbg.exe 外壳程序,可以选择指定进程退出代码。 |
re[sume] [*|[~]threadNumber] | 继续当前线程或threadNumber参数指定的线程。 如果指定threadNumber参数为*,或者线程号以~开始,则该命令将应用于所有线程,threadNumber指定的线程除外。 无法继续未挂起的线程。 |
r[un] [-d(ebug) | -o(ptimize) | -enc] [[path_to_exe] [args_to_exe]] | 停止当前进程(如果存在)并启动一个新进程。如果未提供可执行参数,此命令将运行上次使用run命令执行的程序。如果提供了可执行参数,将使用提供的参数(可选)运行指定的程序。 如果忽略类加载、模块加载和线程启动事件(默认情况下如此),程序将在遇到主线程的第一条可执行指令时停止。 使用以下三个有效标志中的任何一个,可以强制调试器对代码进行实时 (JIT) 编译 : -d(ebug)默认用于 MDbg.exe,该标志禁用优化。 -o(ptimize)默认用于调试器的外部。该标志强制代码的运行方式更接近于调试器外部的运行方式,但也使调试工作更加困难。 -enc启用“编辑并继续”功能,但会招致性能问题。 |
Set variable=value | 更改任意的范围内变量的值。 您还可以创建自己的调试器变量,然后在自己的应用程序中将引用值赋给这些变量。这些值作为原始值的句柄,即使原始值超出范围也不例外。所有调试器变量都必须以$开头(例如$var)。使用set $var=命令将这些句柄设置为空,便可以清除这些句柄。 |
Setip [-il] number | 将文件中当前的指令指针设置到指定位置。如果指定了-il选项,则 number 表示方法中的中间语言偏移量;否则 number 表示源代码中的行号。 |
sh[ow] [lines] | 指定要显示的行数。 |
s[tep] | 开始执行当前行的下一个函数;如果没有要单步执行的函数,则移动到下一行。 |
su[spend] [*|[~]threadNumber] | 挂起当前线程或threadNumber参数指定的线程。如果 指定threadNumber为*,则该命令应用于所有线程。如果线程号以~开头,则该命令应用于所有线程,threadNumber指定的线程除外。使用go或step命令运行进程时,不会运行挂起的线程。如果进程中没有未挂起的线程,即使您发出go命令,该进程也不会继续。在这种情况下,可以发出Ctrl-C命令强行进入该进程。 |
sy[mbol] commandName [commandValue] | 指定下列命令之一: symbol path ["value"] - 显示或设置当前符号路径。 symbol addpath"value" - 添加到当前符号路径。 symbol reload ["module"]- 重新加载所有符号或指定模块的符号。 symbol list [module] - 显示当前为所有模块或指定模块加载的符号。 |
t[hread] [newThread][-nick name] | 将name作为昵称分配给当前的活动线程。可以使用昵称来代替线程名称。昵称不能为数字。如果已经为当前线程分配了昵称,则使用新的昵称来替换旧的昵称。如果新的昵称为 "",则删除当前线程的昵称,并且不会为该线程分配新的昵称。 threadnewThread - 将活动线程设置为newThread。newThread可以是线程的昵称,也可以是线程号。 thread - 显示当前进程中的所有托管线程。 线程通常以线程号标识;但是,如果为线程分配了昵称,则显示昵称。 |
u[p] | 上移活动堆栈帧。 |
uwgc[handle] [var] | [address] | 打印句柄所跟踪的变量。可以按名称或地址来指定句柄。 |
when | 显示当前处于活动状态的when语句。 whendelete all |num[num[num…]] - 删除 num 指定的when语句;如果指定all,则删除所有when语句。 when stopReason [specific_condition] do cmd[cmd[cmd…] ] - stopReason参数可以为: StepComplete,ProcessExited,ThreadCreated,BreakpointHit,ModuleLoaded,ClassLoaded,AssemblyLoaded,AssemblyUnloaded,ControlCTrapped,ExceptionThrown,UnhandledExceptionThrown,AsyncStop,AttachComplete, UserBreak, EvalComplete,EvalException,RemapOpportunityReached,NativeStop. specific_condition可以为: number - 对于ThreadCreated和BreakpointHit,只有当具有相同值的线程 ID/断点号引起停止时才会触发操作。 [!]name - 对于ModuleLoaded、ClassLoaded、AssemblyLoaded、AssemblyUnloaded、ExceptionThrown和UnhandledExceptionThrown,只有当名称与stopReason的名称匹配时才会触发操作。 对于其他stopReason的值,specific_condition必须为空。 |
w[here] [-v] [-c depth] [threadID] | 显示有关堆栈帧的调试信息。 -v选项为显示的每个堆栈帧提供详细信息。 为depth指定一个数字可以限制显示的帧数。使用all命令可以显示所有帧。默认值为 100。 如果指定threadID参数,则可以控制哪个线程与堆栈关联。默认只与当前线程关联。使用all命令可以获取所有线程。 |
x [-c numSymbols] [module[!pattern]] | 显示与模块的pattern匹配的函数。 如果指定了numSymbols,则输出将局限于指定的数目。如果未指定!regex,则显示所有函数。如果未提供module,则显示加载的所有模块。符号 (~#) 可用于通过break命令来设置断点。 |
注意:
MDbg.exe 命令区分大小写。