• windbg调试C#代码(一)


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

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

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

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

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

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

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

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

    7. 如果加载不成功,可以.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:WindowsMicrosoft.NETFrameworkv4.0.30319sos) 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:projectTestWindbg_LocalVariablesTestWindbg_LocalVariablesinDebugTestWindbg_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:projectTestWindbg_LocalVariablesTestWindbg_LocalVariablesinDebugTestWindbg_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:projectTestWindbg_LocalVariablesTestWindbg_LocalVariablesMainWindow.xaml.cs @ 30:
    003e0318 c745f401000000  mov     dword ptr [ebp-0Ch],1
    
    d:projectTestWindbg_LocalVariablesTestWindbg_LocalVariablesMainWindow.xaml.cs @ 31:
    003e031f c745f016000000  mov     dword ptr [ebp-10h],16h
    
    d:projectTestWindbg_LocalVariablesTestWindbg_LocalVariablesMainWindow.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:projectTestWindbg_LocalVariablesTestWindbg_LocalVariablesMainWindow.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:windowsMicrosoft.NetassemblyGAC_MSILPresentationFrameworkv4.0_4.0.0.0__31bf3856ad364e35PresentationFramework.dll
    

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

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

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

    6、引用

  • 相关阅读:
    吴裕雄--天生自然Django框架开发笔记--Django 路由
    吴裕雄--天生自然Django框架开发笔记--Django 视图
    吴裕雄--天生自然Django框架开发笔记--Django 表单
    吴裕雄--天生自然Django框架开发笔记--Django 模型
    Redis07——Redis到底能用在什么地方(下)
    Redis06——Redis到底能用在什么地方(上)
    Redis05——Redis Cluster 如何实现分布式集群
    Redis04——五分钟明白Redis的哨兵模式
    Redis03——Redis是如何删除你的数据的
    Redis02——Redis内存数据如何保存到磁盘
  • 原文地址:https://www.cnblogs.com/AlexanderYao/p/4843711.html
Copyright © 2020-2023  润新知