WinDBG工具简介(.net大会中张银奎先生介绍)

对于一般的程序不需要使用WinDBG工具去调试,使用MDBG就OK

使用WinDBG + SOS调试.Net程序的一般步骤

2009-03-28

1. 加载进程和SOS扩展:

       a. F6或者使用菜单Files –> Attach to a process…Attach一个托管进程

       b.使用命令.loadby sos mscorwks来加载SOS扩展(注意:.Net1.1时代的SOS扩展已经自带于下载安装的WinDBG中,从.Net2.0以后,SOS扩展已经自带到.Net框架中:C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\SOS.dll,为了不至于引起混淆,最好的方法就是使用前面的loadby调试器元命令来让WinDBG自己决定加载什么版本的SOS mscorwks表示.Net框架的工作站版本,现在我们安装的.Net Framework都是该版本)

       c.加载SOS后,使用命令.chain来查看调试链中是否已经成功包含SOS扩展,如下图的WinDBG输出就表示已经成功的加载了SOS

      

2. 查看进程加载的模块并加载调试符号:

       Attach到进程并顺利加载SOS扩展后,我们可以使用lm命令来查看当前进程已经加载的模块,WinDBG会列出一个模块加载列表,这个列表非常有用,我们后面设置断点,查看方法表和IL以及汇编代码都需要使用到相应的Module Name。另外对于我们常用的情况:调试IISHost的进程,由于进程的名字都是w3wp,我们需要区分出哪个进程才是我们想要调试的Application,这个时候我们就可以通过lm命令列出的加载模块列表来判断:

 

       接下来我们就可以根据Module Name来给对应的Module加载调试符号库,由于在使用WinDBG进行调试的时候大多数时候我们都会查看汇编代码,所以加载调试符号对于辅助我们更好的看懂汇编代码和相关的变量命名是必须的,WinDBG默认会帮我们加载相关Module的调试符号,我们也可以使用ld [ModuleName]来显示的加载某一个Module的调试符号,例如ld BasicDebugDemo指定加载BasicDebugDemo这个Module的调试符号。

 

3. 准备就绪,开始调试:

       经过12两个步骤的准备,我们的调试环境已经准备好了,接下来就可以开始调试了,下面介绍一些重要的和常用的调试命令:

       1) 根据调试符号库的信息查看类或者方法在.Net执行引擎中的具体元信息,包括类的方法表地址,类在运行时的元信息(父类元信息地址,虚方法表地址等很多有用信息),这些信息是我们接下来调试的基础!使用命令!name2ee [ModuleName] [ClassName or MehodName]来进行查看,例如:!name2ee BasicDebugDemo BasicDebugDemo.Program

 

       我们也可以使用!name2ee命令直接查看某个类方法的元信息:

 

 

2) 有了上面得到的一个类的方法表的入口地址后,我们就可以使用!dumpmt [-md] [MethodTabel Address]来查看这个类中每一个方法的具体运行时信息(加带md选项表示我们要查询每一个方法声明的入口地址),例如我们利用上面得到的00993030这个地址,!dumpmt –md 00993030

 

3) 另外我们通过上面的!name2ee命令除了拿到MehodTable的地址外,还拿到了Program这个Class的运行时元信息地址EEClass Address,所以我们可以通过!dumpclass [Class Address]来查看这个类中的具体内容(比如其中的静态变量的地址等):

4) 上面提到了一个很重要的信息MehodDesc,得到这个MethodDesc有什么用呢?首先我们可以使用!dumpil [MethodDesc]来查看其编译后的IL代码:

5) 另外如果这个方法已经被JIT了,那么我们调用!dumpmd [MethodDesc]会得到下面的信息:

       接下来我们就可以使用!u [LocalCodeAddr]来查看生成的本地汇编代码:

 

6) 上面能获取的元信息我们基本上都获取了,有了这些元信息(主要是代码地址信息)我们要下断点就很简单了,我们有很多种方式来给我们的代码的指定位置加上断点P

    a.首先如果调试符号加载成功的话,我们可以直接使用很直观的原始方法命名来给指定的方法入口处加上断点:!bpmd [ModuleName] [MethodName],例如!bpmd BasicDebugDemo BasicDebugDemo.Program.CreateFooObject

    b.如果我们有MethodDesc,也可以直接使用!bpmd -md [MethodDesc]来给方法入口处加上断点,例如!bpmd -md 0099301c

   c.我们上面看到了本地汇编代码,所以我们也可以使用bp [CodeAddr]来给某个方法内部的某行代码加上断点。

   d.我们可以使用bl命令来列出所有已加载的断点,也可以使用bc [BreakPoint ID]命令来删除指定ID的断点(用bl命令列出的断点列表种有各个断点的ID),或者使用bc *来删除所有的断点。

    e.断点加载成功后,WinDBG会给出类似下面这样的提示:

         Found 1 methods...

MethodDesc = 00993028

Adding pending breakpoints...

    f.当我们的断点命中后,我们仍然可以像普通调试一样来使用F10 Step Out单步执行,使用F11Step Into单步执行。注意:这里的单步执行,都是指单步汇编代码,而不是我们所写的代码。

7) 在命中断点后我们就可以通过查看类命令来查看栈和堆中的变量的值以及当前的调用堆栈:

  a.使用!clrstack可以查看当前的条用堆栈,使用!clrstack –l可以查看当前调用堆栈以及其上的局部变量和值,使用!clrstack –p可以查看当前调用堆栈上的参数变量以及值,使用!clrstack –a可以查看当前堆栈上所有局部变量和参数变量以及值(格式为StackAddress =  StackValue)!clrstack命令只会显示托管代码的调用堆栈,如果想查看完整的调用堆栈可以使用!dumpstack命令:

b.使用!dso命令可以查看堆栈上的所有对象

  c.使用!do [ObjectAddress]查看指定对象的具体内容:

 

8) 除了通过堆栈查看栈上的变量外,我们还可以直接通过!dumpheap来查看目前堆中的所有对象,但是由于一般情况下堆中存在的变量会非常的多(包含.Net框架里的很多预定义对象),所以直接使用!dumpheap得到的结果一般我们很难查看。大多数情况下,我们需要查看的是堆中指定类型的对象,所以我们可以使用!dumpheap –type [ClassName]来查看指定类型的对象:

9)如何检查内存泄漏?WinDBG SOS中有一个很有用的命令!GCRoot [ObjectAddress]可以帮助我们查看指定对象的引用情况,这个信息可以很好的帮助我们分析那些本应该没有引用但却一直还存在有效引用的对象,由此发现我们代码中潜在的内存泄漏,同时我们也可以观察到哪些对象是目前没有引用了,但是GC还没有回收的:

 

4. 总结:

1) WinDBG不是专门用于调试.Net程序的工具,它更偏向于底层,可用于内核和驱动调试。进行普通的.Net程序调试还是使用微软专为.Net开发的调试工具MDBG更方便一些。但是WinDBG能看到更多的底层信息,对于某些特别疑难的问题调试有所帮助,例如内存泄漏等问题。

2) SOS扩展命令中最有用的命令是!help命令J,使用该命令可以列出所有可用的SOS扩展命令列表,使用!help [SOSCommandName]可以查看每一个具体扩展命名的详细使用说明,例如!help dumpheap就可以查看!dumpheap这个扩展命名的具体使用方法。多多利用!help命名可以很快上手SOS

3) WinDBG本身的资料可以参考 张银奎 先生的《软件调试》一书,另外在互联网上也有非常多的WinDBG资料。

posted @ 2009-03-28 23:15  msp  阅读(1497)  评论(0编辑  收藏  举报