zoukankan      html  css  js  c++  java
  • 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


     

  • 相关阅读:
    [计算机视觉]算法
    [python]python中**是什么
    ubuntu16.04增大swap空间
    conda查看某个安装包的依赖项
    conda install -c anaconda
    conda安装指定版本的指定包
    ValueError: Unable to determine SOCKS version from socks://127.0.0.1:1080/
    [ubuntu]卸载老版并安装最新版firefox
    使用anaconda创建tensorflow环境后如何在jupyter notebook中使用
    [python]numpy.mean()用法
  • 原文地址:https://www.cnblogs.com/mrhgw/p/2149849.html
Copyright © 2011-2022 走看看