惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

量子位
Stack Overflow Blog
Stack Overflow Blog
人人都是产品经理
人人都是产品经理
The GitHub Blog
The GitHub Blog
Engineering at Meta
Engineering at Meta
Vercel News
Vercel News
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Y
Y Combinator Blog
The Cloudflare Blog
Last Week in AI
Last Week in AI
B
Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Tailwind CSS Blog
V
Visual Studio Blog
博客园 - 三生石上(FineUI控件)
小众软件
小众软件
Google DeepMind News
Google DeepMind News
D
DataBreaches.Net
博客园 - 司徒正美
B
Blog RSS Feed
Microsoft Azure Blog
Microsoft Azure Blog
罗磊的独立博客
Hugging Face - Blog
Hugging Face - Blog
L
LangChain Blog

博客园 - Haozes

新Blog 拼写检查算法 Golang 版 (译)你必须知道的位运算技巧 Low Level Bit Hacks You Absolutely Must Know Windows 下 命令行增强工具 WPF Layout & Image异步加载 几篇文章了解编译原理 WPF Binding Validation 数据验证 WPF 实现Loading效果 推荐一个.NET 命令行参数Parser 库 常用开发工具介绍 使用.Net Memory Profiler 分析.Net程序内存泄露 使用Mdbg.exe 调试.Net 程序 WPF 多语言方案 .Net 2 Tip :捕获CSE和Thread.Timer与Thread.Sleep比较 使用CSharp Driver操作Mongodb介绍 使用Python操作MSSQL数据库. 运行.Net4.0程序是否要安装之前的.Net版本 javascript Disable <div> or other tag in Other Browser like FF,Chrome Delphi 无类型参数传递动态数组和静态数组
Windbg 离线调试.Net 程序入门
Haozes · 2012-09-20 · via 博客园 - Haozes

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

  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