您的位置:首页 > 编程语言 > C#

windbg调试C#代码(一)

2015-09-28 12:09 351 查看
用windbg调试C#代码是比较麻烦的,因为windbg是针对OS层级的,而C#被CLR隔了一层,很多原生的命令如查看局部变量dv、查看变量类型dt等在CLR的环境中都不能用了。必须使用针对CLR的扩展命令,比如sos、psscor2中的命令。下面以一个最简单的WPF程序来说明调试的典型步骤,以及典型的坑。

1、用正确版本的windbg、加载正确版本的扩展模块

把程序跑起来,看任务管理器中是32位 or 64位进程,32-bit用windbg x86、64-bit用windbg x64调试。

如果是.dump文件,恐怕就要在获取的时候,查看对应进程的信息了。

判断是.net2.0 or .net4.0的程序:lmvm clr,如果有详细信息,表示是net40的程序。如果为none,则lmvm mscorwks,显示详情则表示是net20的程序。

根据net20 or net40用.loadby sos clr或.loadby sos mscorwks加载对应的sos版本。当然也可以使用.load [moduleName]直接加载,但要确保版本正确。

可以用.chain查看已加载的extension,如果没有sos之类的,那么这个扩展下的命令都不可用,比如!DumpDomain 会报“No export NoExist found”。

不同的扩展可能存在相同的命令,比如sos和psscor2里大部分命令都是同名的,可能参数列表不同。可以用[extension].[command]的方式区分,比如!sos.DumpDomain和!psscor2.DumpDomain。如果不加extensionName,那么会默认使用.chain列表出来的第一个扩展的命令。

如果加载不成功,可以.cordll -ve -u -l看到出错的详细信息,一般都是版本没有正确匹配。一般都建议:不论是32位还是64位的进程,都抓32位的dump;具体操作中,由于TaskMgr和ProcExp都存在32位+64位的版本,因此强烈建议只用ProcDump -ma [ProcessName]抓Dump,然后根据Process的32 or 64位,选用Windbg的x86 or x64位。比如,我一个64位的进程,抓了一个64位的dump,.loadby sos clr时报如下错:

0:000> .loadby sos clr
The call to LoadLibrary(C:\Windows\Microsoft.NET\Framework\v4.0.30319\sos) failed, Win32 error 0n193
“%1 is not a valid Win32 application.”
Please check your debugger configuration and/or network access.


2、如何查看元数据,如何在感兴趣的地方下断点

2.1、如果不是自己写的代码,可以用如下命令:
!DumpDomain
Module Name
00252eac    D:\project\TestWindbg_LocalVariables\TestWindbg_LocalVariables\bin\Debug\TestWindbg_LocalVariables.exe

!DumpModule -mt [moduleName, such as: 00252eac]
MT  TypeDef Name
------------------------------------------------------------------------------
00258b6c 0x02000002 TestWindbg_LocalVariables.MainWindow
002572b4 0x02000003 TestWindbg_LocalVariables.App

!DumpMT -md 00258b6c
Entry MethodDe    JIT Name
003e02e8 00258afc    JIT TestWindbg_LocalVariables.MainWindow.Button_Click(System.Object, System.Windows.RoutedEventArgs)

!bpmd -md 00258afc
MethodDesc = 00258afc
Setting breakpoint: bp 003E02E8 [TestWindbg_LocalVariables.MainWindow.Button_Click(System.Object, System.Windows.RoutedEventArgs)]

2.2、如果是自己写的代码,那就方便了,直接:
!bpmd TestWindbg_LocalVariables MainWindow.Button_Click

3、如何查看和修改局部变量

Button_Click里的源码如下:

private void Button_Click(object sender, RoutedEventArgs e){
int a = 1;
int b = 22;
int c = a + b;
MessageBox.Show(c.ToString());
}

!clrstack -i //能很方便的提供对命令的链接
Child SP IP       Call Site
003ce5e0 003e0326 [DEFAULT] [hasThis] Void TestWindbg_LocalVariables.MainWindow.Button_Click(Object,Class System.Windows.RoutedEventArgs) (D:\project\TestWindbg_LocalVariables\TestWindbg_LocalVariables\bin\Debug\TestWindbg_LocalVariables.)

!ClrStack -i -a 0
PARAMETERS:
+ TestWindbg_LocalVariables.MainWindow this @ 0x268652c
+ System.Windows.Controls.Button sender @ 0x269b1d4
+ System.Windows.RoutedEventArgs e @ 0x2756720
LOCALS:
+ int a  = 1
+ int b  = 22
+ int c  = 0

!U /d 003e0326
d:\project\TestWindbg_LocalVariables\TestWindbg_LocalVariables\MainWindow.xaml.cs @ 30:
003e0318 c745f401000000  mov     dword ptr [ebp-0Ch],1

d:\project\TestWindbg_LocalVariables\TestWindbg_LocalVariables\MainWindow.xaml.cs @ 31:
003e031f c745f016000000  mov     dword ptr [ebp-10h],16h

d:\project\TestWindbg_LocalVariables\TestWindbg_LocalVariables\MainWindow.xaml.cs @ 32:
>>> 003e0326 8b45f4          mov     eax,dword ptr [ebp-0Ch]
003e0329 0345f0          add     eax,dword ptr [ebp-10h]
003e032c 8945ec          mov     dword ptr [ebp-14h],eax   // F10到这一步,寄存器eax里add的结果,即将被存到c里,修改eax即可

d:\project\TestWindbg_LocalVariables\TestWindbg_LocalVariables\MainWindow.xaml.cs @ 33:
003e032f 8d4dec          lea     ecx,[ebp-14h]
003e0332 e8e9001355      call    mscorlib_ni+0x2e0420 (55510420) (System.Int32.ToString(), mdToken: 06000d5f)
003e0337 8945e4          mov     dword ptr [ebp-1Ch],eax
003e033a 8b4de4          mov     ecx,dword ptr [ebp-1Ch]
003e033d e862a04005      call    PresentationFramework_ni+0x9fa3a4 (057ea3a4) (System.Windows.MessageBox.Show(System.String), mdToken: 06006934)
003e0342 8945e8          mov     dword ptr [ebp-18h],eax

可以看出汇编与源码间的对应关系,比如我们想修改c = 25,可以F10到“mov dword ptr [ebp-14h],eax”这一步,如下:

eax=00000017 // 0x17=23
r eax=19
r eax //验证一下是否已修改
eax=00000019
g

!DumpHeap -type System.Windows.Controls.Button
MT    Count    TotalSize Class Name
051bc3bc        1          248 System.Windows.Controls.Button

!dso
OS Thread Id: 0x310c (0)
ESP/REG  Object   Name
ebx      0269b1d4 System.Windows.Controls.Button

!do -nofields 0269b1d4
Name:        System.Windows.Controls.Button
MethodTable: 051bc3bc
EEClass:     04e39738
Size:        248(0xf8) bytes
File:        C:\windows\Microsoft.Net\assembly\GAC_MSIL\PresentationFramework\v4.0_4.0.0.0__31bf3856ad364e35\PresentationFramework.dll

run下去,可以看到MessageBox.Show出来的是25。另外也可以配合!DumpHeap、!DumpStackObjects(dso)、!DumpObj(do)等命令查找感兴趣的对象。最后,多看帮助,windbg的帮助非常详实。多看extension自带的帮助:!sos.help、!help DumpDomain等。

4、如何分析高内存(待续)

5、如何分析高CPU(待续)

6、引用

How can I view the local variables on the evaluation stack when debugging a .NET CLR application?

change local stack variable value
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: