zoukankan      html  css  js  c++  java
  • Windbg 离线调试.Net 程序入门

    在哪些情况下,必须祭出一些复杂的调试器呢?大概有以下:

    1. 程序异常崩溃
    2. 程序内存泄露
    3. 程序挂起
    4. 程序消耗cpu 高

    内存泄露有.Net Memory Profiler神器情况下,能比windbg更容易找到问题(当然限于托管代码内存泄露,许多非托管的还是比较难搞). 参考 使用.Net Memory Profiler 分析.Net程序内存泄露

    同样CPU监控工具也有ANT Profiler 之类工具.

    但总有一些BUG难以重现,特别是在非开发机器出现,此时抓个dump,可能更为方便.

    下面就以一个demo为例简单使用windbg 分析程序崩溃和挂起这两种情况.


    异常的最佳实践

    按照我的经验,很多代码反映出的是---不知道什么是异常,什么时候抛异常,什么地方捕获异常,或有日志,连个堆栈信息都看不到.

    如何编写正确的代码才是,这应当才是问题的根源.再方便的调试的工具,耗费的时间也依然很多.

    我很长时间用不上windbg是因为.NET程序调试太为简单了.

    关于使用异常推荐一篇文章

    http://www.codeproject.com/Articles/9538/Exception-Handling-Best-Practices-in-NET

    windbg 安装

    下载安装后要配置symbol path

    symbol

    抓Dump

    程序崩溃时弹出错误对话框之类.只要进程还没退出,也就是我们说”没飞”

    使用Windows任务管理器创建转储文件

    dump

    Windows xp 好像没这功能,不过也没关系,可以使用Process Explorer

    debug2

    如何程序会”飞”,就得使用Windbg下的 ADPlus,监视某个进程,该进程崩溃时自动保存dump.

    如:

    adplus –quiet –crash –p 432 –o d:\debug

    还有一种方法是,程序调用windows api自己生成dump.

    以下以一个简单的Winform 程序演示:

    code

    试验1: 查找异常源

    点击button 触发 btnException_Click 产生异常,抓dump

    首先加载组件扩展,如果客户机器和你的机器不一样要copy 客户机器的.net framework 文件:

    如:

    >>.load  "E:\dmp\v4.0.30319\SOS.dll"

    用!pe印详细异常信息:

    0:000> !pe
    Exception object: 017ed918
    Exception type:   System.NullReferenceException
    Message:         
    Object reference not set to an instance of an object.
    InnerException:   <none>
    StackTrace (generated):
        SP       IP       Function
        0012EC78 002A0680 MemLeakProfileDemo!MemLeakProfileDemo.Form1.btnException_Click(System.Object, System.EventArgs)+0x10
        0012EC80 592A4507 System_Windows_Forms_ni!System.Windows.Forms.Control.OnClick(System.EventArgs)+0x7f
        0012EC94 592A6CA2 System_Windows_Forms_ni!System.Windows.Forms.Button.OnClick(System.EventArgs)+0xa2
        0012ECAC 5988A4E0 System_Windows_Forms_ni!System.Windows.Forms.Button.OnMouseUp(System.Windows.Forms.MouseEventArgs)+0xac
        0012ECC8 59853E11 System_Windows_Forms_ni!System.Windows.Forms.Control.WmMouseUp(System.Windows.Forms.Message ByRef, System.Windows.Forms.MouseButtons, Int32)+0x2d1
        0012ED5C 59BF6A8F System_Windows_Forms_ni!System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message ByRef)+0x8fc6ef
        0012EDB4 59BFE3F1 System_Windows_Forms_ni!System.Windows.Forms.ButtonBase.WndProc(System.Windows.Forms.Message ByRef)+0x8ec9dd
        0012EDF8 593119F8 System_Windows_Forms_ni!System.Windows.Forms.Button.WndProc(System.Windows.Forms.Message ByRef)+0x20
        0012EE04 592FA393 System_Windows_Forms_ni!System.Windows.Forms.Control+ControlNativeWindow.OnMessage(System.Windows.Forms.Message ByRef)+0x13
        0012EE0C 592FA311 System_Windows_Forms_ni!System.Windows.Forms.Control+ControlNativeWindow.WndProc(System.Windows.Forms.Message ByRef)+0x31
        0012EE20 592FA256 System_Windows_Forms_ni!System.Windows.Forms.NativeWindow.Callback(IntPtr, Int32, IntPtr, IntPtr)+0x96

    StackTraceString: <none>
    HResult: 80004003

    或者使用 !analyze -v 查看异常.有了堆栈信息很明了.

    或者我们再使用 !dumpheap看了托管堆里有哪些信息,此时我只关心 MemLeakProfileDemo 命名空间的对象,也就是我自己代码使用的.或者使用!dso 打印当前堆栈的对象

    0:000> !dumpheap -type MemLeakProfileDemo
    Address       MT     Size
    017cd058 00226410      352    
    017d30e8 00226ba4       12    
    017d310c 00226c34       12    
    total 0 objects
    Statistics:
          MT    Count    TotalSize Class Name
    00226c34        1           12 MemLeakProfileDemo.FoolBrother
    00226ba4        1           12 MemLeakProfileDemo.Fool
    00226410        1          352 MemLeakProfileDemo.Form1
    Total 3 objects

    Fool 对象只有一个. MT (Method Table)是00226ba4 ,在上面可以看到它的地址是017d30e8

    再用do 命令查看该地址的信息:

    0:000> !do 017d30e8
    Name:        MemLeakProfileDemo.Fool
    MethodTable: 00226ba4
    EEClass:     002b054c
    Size:        12(0xc) bytes
    File:        C:\Users\jhwang\Documents\Visual Studio 2010\Projects\MemLeakProfileDemo\MemLeakProfileDemo\bin\Release\MemLeakProfileDemo.exe
    Fields:
          MT    Field   Offset                 Type VT     Attr    Value Name
    00212990  400000c        4 ...yte[], mscorlib]]  0 instance 017d30f4 list

    再顺藤摸瓜查看 它的fields, list 需要用 !dumpvc来看

    0:000> !dumpvc 00212990  400000c
    Name:        System.Collections.Generic.IList`1[[System.Byte[], mscorlib]]
    MethodTable: 00212990
    EEClass:     631323c4
    Size:        0(0x0) bytes
    File:        C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
    Fields:

    这个列表是个空的.

    试验2:查找线程挂起原因

    点击另一个按钮触发btnHang_Click,然后关闭窗口,由于这个线程没有正确退出,进程不会退出,僵死在那里.抓dump然后打开.load sos后.

    查看当前的线程:

    0:000> !threads
    ThreadCount:      3
    UnstartedThread:  0
    BackgroundThread: 2
    PendingThread:    0
    DeadThread:       0
    Hosted Runtime:   no
                                       PreEmptive   GC Alloc                Lock
           ID  OSID ThreadOBJ    State GC           Context       Domain   Count APT Exception
       0    1  1e68 003fe898   2016220 Enabled  019dd3f0:019ddfe8 003f8410     0 STA
       2    2  11e4 00409cc8      b220 Enabled  00000000:00000000 003f8410     0 MTA (Finalizer)
       5    3  14b4 00445430   200b020 Enabled  019c2014:019c3fe8 003f8410     0 MTA

    STA 和MTA是个啥,我也不清楚.正常 program.cs 里常有这个

         [STAThread]         static void Main()

    这个估计不是我们想看的,第二个MTA线程,看样子是GC之类的,那肯定是第三个了.切换到这个线程并看堆栈

    
    

    0:003> ~5s
    eax=001363c8 ebx=00000001 ecx=000003e8 edx=019c1458 esi=0455ef50 edi=00000000
    eip=776b7094 esp=0455ef0c ebp=0455ef74 iopl=0         nv up ei pl nz na pe nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
    ntdll!KiFastSystemCallRet:
    776b7094 c3              ret
    0:005> !clrstack
    OS Thread Id: 0x14b4 (5)
    Child SP IP       Call Site
    0455f034 776b7094 [HelperMethodFrame: 0455f034] System.Threading.Thread.SleepInternal(Int32)
    0455f080 001a06ed MemLeakProfileDemo.Form1.DoWork() [C:\Users\jhwang\Documents\Visual Studio 2010\Projects\MemLeakProfileDemo\MemLeakProfileDemo\Form1.cs @ 80]
    0455f088 6338b30b System.Threading.ThreadHelper.ThreadStart_Context(System.Object)
    0455f098 63318004 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
    0455f0bc 63317f44 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
    0455f0d8 6338b298 System.Threading.ThreadHelper.ThreadStart()
    0455f2fc 69a1219b [GCFrame: 0455f2fc]
    0455f5c0 69a1219b [DebuggerU2MCatchHandlerFrame: 0455f5c0]

    对照代码.一切了然.

    
    
    
    

    小结: sos本身的命令很多,用过的也不多,除了sos.dll 这个扩展以外,网上还有sosex,PssCor4 等调试扩展.稍微了解一点windbg也能给我们的生活easy一点.

    source code: downoad
  • 相关阅读:
    Java8的List过滤
    docker 部署zookeeper集群
    docker部署haproxy
    安装和配置jenkins
    webpack安装和简单配置
    javascript
    javascript-模板方法模式-提示框归一化插件
    javascript-组合模式
    javascript设计模式-工厂方法模式
    javascript函数的几种写法集合
  • 原文地址:https://www.cnblogs.com/solo/p/2695426.html
Copyright © 2011-2022 走看看