Windbg调试新手入门

此文仅记录本人初次使用Windbg调试IIS,方便本人或有需要的同学参考!

-------------------------------------------------------------------------

1. WinDbg下载和安装
    Install Debugging Tools for Windows 32-bit Version
http://www.microsoft.com/whdc/devtools/debugging/installx86.mspx
    Install Debugging Tools for Windows 64-bit Versions
http://www.microsoft.com/whdc/devtools/debugging/install64bit.mspx

建议不要下载winsdk_web.exe在线安装,超慢无法忍受,可直接下载 Windows Driver Kit (WDK) ISO image安装包(600多M好大),安装后在安装目录打开Debuggers/windbg.exe。

2. 设置并下载Windbg下载符号,请按以下步骤在Windbg命令行输入指令(注意下面的.不能去掉): 

    1)   .sympath srv*G:\WinDDK\7600.16385.1\Debuggers\Symbols*http://msdl.microsoft.com/download/symbols  (* 注意前面的绝对路径必须存在,不存在自己建立)。
    2)   !sym noisy
    3)   .reload

3.抓取DUMP

    推荐使用Procdump进行抓取(详细使用请猛击这里:好用的抓取dump的工具-ProcDump),也可以直接在任务管理器中右键IIS进程w3wp.exe,然后选择“创建转储文件”生成DUMP。

4.分析DUMP

    因为需要解决的是高CPU的问题,思路是分析某个线程在进程启动后占用的cpu时间。所以需要取多个dump,看"高CPU时间段"内"占用cpu时间增长最多"的是哪个线程,最终得到的两个文件如下(分别在任务管理器中在不同的时间段取两次DUMP):

打开第一个DUMP,运行!runaway命令可以看到各线程的CPU占用总时间:
Thread       Time

  18:fdc       0 days 1:20:28.390

  19:1370      0 days 1:16:36.359

  21:538       0 days 1:08:28.765

  22:698       0 days 1:07:55.968

  20:1180      0 days 0:58:22.046

 138:1284      0 days 0:56:53.890

 136:f9c       0 days 0:49:38.609

   9:1094      0 days 0:44:26.312

 147:db8       0 days 0:25:16.234

 149:6f4       0 days 0:22:00.687

 148:c8c       0 days 0:20:29.156

  13:1108      0 days 0:01:31.562

  12:d24       0 days 0:01:27.593

  14:5e8       0 days 0:01:26.203

  11:ce0       0 days 0:01:06.703

打开第二个DUMP,运行!runaway命令可以看到各线程的CPU占用总时间:
Thread       Time

  18:fdc       0 days 1:21:09.125

  19:1370      0 days 1:20:20.468

  21:538       0 days 1:08:43.140

  22:698       0 days 1:08:28.812

  20:1180      0 days 1:03:01.078

 138:1284      0 days 0:57:49.281

 136:f9c       0 days 0:55:01.250

   9:1094      0 days 0:44:50.781

 146:db8       0 days 0:27:10.062

 147:c8c       0 days 0:25:17.828

 148:6f4       0 days 0:25:03.656

  13:1108      0 days 0:01:32.328

将两个DUMP中相同编号的线程结果减一下,可以得出18线程在这段时间内增长得最快,也就是说cpu这段时间内都在完成18线程的事情,那它肯定就是高cpu的原因了。
为了查看18线程对应的托管堆栈,执行以下命令:
    1) 加载sos扩展,输入.load C:\Windows\Microsoft.NET\Framework64\v4.0.30319\SOS.dll(32位系统相应改)。
    2) 运行~18 s 切换到18线程。
    3) 运行!clrstack查看堆栈。


 从代码可以看出是StripHTML方法有问题,该方法执行一个正则式剔除所有HTML(取自cnblogs),可见该方法效率十分低下!

 剩下的事情就好办了,重写或弃用此方法!

 

查找内存占用过高的根源

执行 !eeheap -gc 查看托管堆的总信息

0:004> !eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x0000000002a26220
generation 1 starts at 0x0000000002a21cf0
generation 2 starts at 0x0000000002a11000
ephemeral segment allocation context: none
         segment             begin         allocated  size
0000000002a10000  0000000002a11000  0000000002af2238  0xe1238(922168)
Large object heap starts at 0x0000000012a11000
         segment             begin         allocated  size
0000000012a10000  0000000012a11000  0000000018e171e0  0x64061e0(104882656)
Total Size:              Size: 0x64e7418 (105804824) bytes.
------------------------------
GC Heap Size:    Size: 0x64e7418 (105804824) bytes.


看来大对象堆占用最多内存


执行 !dumpheap -min 200 -stat 获取占用堆内存的各对象的统计信息

0:004> !dumpheap -min 200 -stat
total 0 objects
Statistics:
              MT    Count    TotalSize Class Name
000007fef1369750        1          216 System.AppDomain
000007fef13745d0        2          432 System.Globalization.NumberFormatInfo
000007fef1373148        1          432 System.Collections.Generic.Dictionary`2+Entry[[System.Type, mscorlib],[System.Security.Policy.EvidenceTypeDescriptor, mscorlib]][]
000007fef136b7b0        2         1056 System.Globalization.CultureData
000007fef136c7e8        2         1128 System.Int32[]
000007fef136b328        3         3256 System.Char[]
000007fef136d110        1         4752 System.Collections.Generic.Dictionary`2+Entry[[System.String, mscorlib],[System.String, mscorlib]][]
0000000000664be0       22        31784      Free
000007fef136ae78        5        34072 System.Object[]
000007fef1370bc0       11    104858384 System.Byte[]
Total 50 objects

Byte数组类型占用最多内存


执行 !dumpheap -type Byte[] -min 200 看各Byte数组占用堆内存的详细信息

0:004> !dumpheap -type Byte[] -min 200
         Address               MT     Size
0000000002a25290 000007fef1370bc0      544     
0000000012a17048 000007fef1370bc0 10485784     
0000000013417060 000007fef1370bc0 10485784     
0000000013e17078 000007fef1370bc0 10485784     
00000000148170a8 000007fef1370bc0 10485784     
00000000152170d8 000007fef1370bc0 10485784     
0000000015c17108 000007fef1370bc0 10485784     
0000000016617138 000007fef1370bc0 10485784     
0000000017017168 000007fef1370bc0 10485784     
0000000017a17198 000007fef1370bc0 10485784     
00000000184171c8 000007fef1370bc0 10485784     
total 0 objects
Statistics:
              MT    Count    TotalSize Class Name
000007fef1370bc0       11    104858384 System.Byte[]
Total 11 objects


随便挑一个大的对象出来


执行 !gcroot <Byte数组对象地址> 看对象引用关系

0:004> !gcroot 0000000012a17048 
Note: Roots found on stacks may be false positives. Run "!help gcroot" for
more info.
Scan Thread 0 OSTHread a54
RSP:54e958:Root:  0000000002a21e60(System.Threading.ThreadStart)->
  0000000002a21cb0(ConsoleApplication1.Program)->
  0000000002a21cc8(System.Collections.Generic.List`1[[System.Byte[], mscorlib]])->
  0000000002a21dc0(System.Byte[][])->
  0000000012a17048(System.Byte[])


跟踪到Program类的List类型成员了


执行 !do <Program对象地址> 查看对象的详细信息

0:004> !do 0000000002a21cb0
Name:        ConsoleApplication1.Program
MethodTable: 000007ff00044140
EEClass:     000007ff00152350
Size:        24(0x18) bytes
File:        C:\Users\Administrator\Desktop\ConsoleApplication1\ConsoleApplication1.exe
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
000007ff00032600  4000001        8 ...yte[], mscorlib]]  0 instance 0000000002a21cc8 _list

就是_list了。有源代码文件的话这一步其实就可以略过,就算没有源代码文件也可以利用!dumpdomain与!SaveModule命令导出模块,然后使用Reflector进行反编译。

 

 ====================== Windbg的其它指令参考 ======================

!threadpool,查看线程池CPU使用量

!runaway,查看线程占用CPU时间,可以从中找到哪个线程占用时间更高。

~number s,number为具体哪个线程的ID。

!clrstack,到具体某个线程后,查看当前线程的托管代码

!name2ee ,找到哪个托管代码模块后,查看MethodTable,EEClass等信息。

!dumpmt,找到相关MethodTable处的相关信息。

!dumpmd,根据MethodDesc找到相关模块信息,比如MethodTable.

!dumpdomain,显示所有域里的程序集,或者根据参数获取指定域。

!savemodule,根据具体程序集地址,把当前程序集的代码生成到指定文件

查看占用内存过高的命令比如:

!eeheap,查看堆中信息,可以查看到大对象。

!dumpheap,查看堆中信息,一般带-min,-stat,-type等参数。

!gcroot,根据堆地址,查看相关模块引用代码信息。

lmvm clr  查看CLR调试版本

其他命令当然还非常多,也非常有用,需要的时候再翻资料,如果需要很精通windbg+sos,还是老老实实仔细看吧。

 

本文参考以下文章:

http://www.cnblogs.com/h-hello/articles/1502493.html

http://www.cnblogs.com/Jesses/archive/2011/05/03/2035560.html

http://www.cnblogs.com/Lawson/archive/2011/01/23/1942692.html

 


 

posted @ 2011-08-22 18:58  有容乃大  阅读(11534)  评论(3编辑  收藏  举报