Windbg如何设置应用程序的断点

Posted hzwanglw

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Windbg如何设置应用程序的断点相关的知识,希望对你有一定的参考价值。

Windbg用户模式下,非托管代码,可以直接用bp命令设置断点。调试.Net 应用程序相对于非托管程序,要麻烦一些。因为.NET源码在编译的时候,首先是编译成IL文件,程序运行的时候,通过Load加载PE文件,然后JIT编译器负责将IL代码编译为汇编指令,然后执行。JIT编译器编译过后,就可以像非托管应用程序一样进行断点设置了。

断点设置步骤:

1、设置符号路径。

      我们可以在系统环境变量里,增加一个 _NT_SYMBOL_PATH  ,设置为   cache*d:symbols;srv*http://msdl.microsoft.com/download/symbols  可以将路径更改为自己本地的缓存路径。如果有一把好梯子的话,设置为全局代理模式,在调试的时候,会将.NET的框架PDB文件下载到该缓存目录里,我们在调试的时候就能看到更详细的信息。

  当然,也可以通过命令行来主动获取微软官方调试符号。

      symchk /r C:WindowsSystem32*.dll /s SRV*D:symbols*http://msdl.microsoft.com/download/symbols
      symchk C:WINDOWSSystem32KERNELBASE.dll /s SRV*D:symbols*http://msdl.microsoft.com/download/symbols

 

2、加载正确版本CLR以及SOS(Son of Strike)。

     Windbg用户模式下,调试.Net 应用程序,我们需要正确的加载CLR以及SOS(Son of Strike)。需要注意的是,加载的CLR以及SOS版本一定要正确。如果应用程序在X86平台下编译,则需要加载 C:WindowsMicrosoft.NETFrameworkv4.0.30319 目录下的SOS以及CLR,64位下编译的话,需要加载C:WindowsMicrosoft.NETFramework64v4.0.30319 目录下的SOS以及CLR。2.0的程序需要加载MSCORWKS。

3、例子

     以一个简单的Demo为例,来看一下如何在Windbg中设置断点。(在4.0FrameWork、X86平台下编译)。这个程序,根据体重身高来计算BMI指数,假定BMI指数为21的为最健康的,对给定的人员列表按照偏差大小的绝对值进行排序。让我们姑且认为偏差越小身体越健康吧。

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 
 6 namespace WindbugDemo
 7 {
 8     public class Person
 9     {
10         //认为BMI和21偏差最小的为最健康的
11         private const double BmiHelthConst = 21f;
12 
13         public string Name { get; set; }
14         public int Age { get; set; }
15         public double Height { get; set; }
16         public double Weight { get; set; }
17         public double BmiVariance { get { return GetBmiVariance(); } }
18         //18岁至65岁可以Weight/(Height*Height)计算,否则抛出异常
19         public double GetBmiVariance()
20         {
21             if (Age > 65 || Age < 18)
22                 throw new Exception("18岁以下65岁以上人群不适用此计算方法");
23             double bmi = Math.Round(Weight / Math.Pow(Height, 2),2);
24             double variance = Math.Abs(bmi - BmiHelthConst);
25             return variance;
26         }
27     }
28 
29     public class HelthCompare : IComparer<Person>
30     {
31         public int Compare(Person x, Person y)
32         {
33 
34             double diffx = x.BmiVariance;
35             double diffy = y.BmiVariance;
36             if (diffx == diffy)
37                 return 0;
38             if (diffx > diffy)
39                 return 1;
40             return -1;
41         }
42     }
43 
44     class Health
45     {
46         [STAThread]
47         static void Main(string[] args)
48         {
49             Person person1 = new Person { Name = "James", Age = 35, Height = 1.70, Weight = 70 };
50             Person person2 = new Person { Name = "Tony", Age = 25, Height = 1.65, Weight = 60 };
51             Person person3 = new Person { Name = "John", Age = 30, Height = 1.75, Weight = 90 };
52             Person[] array = new Person[] { person1, person2, person3 };
53             List<Person> list = new List<Person>();
54             list.AddRange(array);
55             HelthCompare comparer = new HelthCompare();
56             list.Sort(comparer);
57             foreach(var item in list)
58             {
59                 string text = string.Format("Name:{0}  Age:{1}  Height:{2}  Weight:{3}  BMI Variance:{4}",
60                                     item.Name,item.Age, item.Height, item.Weight, item.BmiVariance);
61                 System.Console.WriteLine(text);
62             }
63             Console.ReadKey();
64             /*
65             Name:Tony  Age:25  Height:1.65  Weight:60  BMI Variance:1.04
66             Name:James  Age:35  Height:1.7  Weight:70  BMI Variance:3.22
67             Name:John  Age:30  Height:1.75  Weight:90  BMI Variance:8.39
68              */
69         }
70     }
71 
72 }

3.1 打开Windbg,在File==》Open Executable...打开编译后的程序。指定PDB路径以及源码路径。

0:000> .sympath+ d:source
Symbol search path is: cache*d:symbols;srv*http://msdl.microsoft.com/download/symbols;d:source
Expanded Symbol search path is: cache*d:symbols;srv*http://msdl.microsoft.com/download/symbols;d:source
Source search path is: d:source
0:000> .reload
Reloading current modules
.....

3.2 可以使用 sxe ld:clrjit 命令,这个命令发生在clrjit加载的时候,在进入Main方法之前,这对于我们设置自己程序的断点很方便。然后输入g命令,当加载clrjit的时候,就可以命中断点了。

0:000> sxe ld:clrjit
0:000> g
(cc8.cd0): Unknown exception - code 04242420 (first chance)
ModLoad: 6d110000 6d18d000   C:WindowsMicrosoft.NETFrameworkv4.0.30319clrjit.dll
eax=00000000 ebx=00000000 ecx=001437a4 edx=00000001 esi=7ffdf000 edi=0027e340
eip=76df70b4 esp=0027e258 ebp=0027e2ac iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
ntdll!KiFastSystemCallRet:
76df70b4 c3              ret
0:000> .load C:WindowsMicrosoft.NETFrameworkv4.0.30319sos.dll
0:000> .load C:WindowsMicrosoft.NETFrameworkv4.0.30319clr.dll
0:000> .loadby sos clr  (等同于上面两条命令)

3.3 加载完SOS后,就可以用扩展命令来设置断点了。

0:000> !name2ee Health!WindbugDemo.Health
Module:      00142edc
Assembly:    Health.exe
Token:       02000004
MethodTable: 001437b8
EEClass:     00141304
Name:        WindbugDemo.Health
0:000> !dumpmt -md 001437b8
EEClass:         00141304
Module:          00142edc
Name:            WindbugDemo.Health
mdToken:         02000004
File:            D:inHealth.exe
BaseSize:        0xc
ComponentSize:   0x0
Slots in VTable: 6
Number of IFaces in IFaceMap: 0
--------------------------------------
MethodDesc Table
   Entry MethodDe    JIT Name
6647952c 6619612c PreJIT System.Object.ToString()
6648ec30 66196134 PreJIT System.Object.Equals(System.Object)
6648e860 66196154 PreJIT System.Object.GetHashCode()
6648e2a0 66196168 PreJIT System.Object.Finalize()
0014c015 001437b0   NONE WindbugDemo.Health..ctor()
0014c011 001437a4   NONE WindbugDemo.Health.Main(System.String[])
0:000> !dumpmt -md 001437b8
EEClass:         00141304
Module:          00142edc
Name:            WindbugDemo.Health
mdToken:         02000004
File:            D:inHealth.exe
BaseSize:        0xc
ComponentSize:   0x0
Slots in VTable: 6
Number of IFaces in IFaceMap: 0
--------------------------------------
MethodDesc Table
   Entry MethodDe    JIT Name
6647952c 6619612c PreJIT System.Object.ToString()
6648ec30 66196134 PreJIT System.Object.Equals(System.Object)
6648e860 66196154 PreJIT System.Object.GetHashCode()
6648e2a0 66196168 PreJIT System.Object.Finalize()
0014c015 001437b0   NONE WindbugDemo.Health..ctor()
0014c011 001437a4   NONE WindbugDemo.Health.Main(System.String[])
0:000> !bpmd -md 001437a4   
MethodDesc = 001437a4
Adding pending breakpoints...

 在上面的Windbg调试窗口里,JIT为NONE的就表示还没有进行JIT编译。这种情况下,我们不能使用bp命令来设置断点的。我们可以使用bpmd命令加md参数来设置断点。输入g命令程序继续执行。

0:000> !u 001437a4    
Normal JIT generated code
WindbugDemo.Health.Main(System.String[])
Begin 011f0050, size 5cc

d:source2HealthHealth.cs @ 48:
011f0050 55              push    ebp
011f0051 8bec            mov     ebp,esp

... ...

d:source2HealthHealth.cs @ 56:
011f0377 8b8d68ffffff    mov     ecx,dword ptr [ebp-98h]
011f037d 8b9564ffffff    mov     edx,dword ptr [ebp-9Ch]
011f0383 3909            cmp     dword ptr [ecx],ecx
011f0385 e822022b65      call    mscorlib_ni+0x3105ac (664a05ac) (System.Collections.Generic.List`1[[System.__Canon, mscorlib]].Sort(System.Collections.Generic.IComparer`1<System.__Canon>), mdToken: 06002283)
011f038a 90              nop

... ...

0:000> bp 011f0377 

0:000> g

3.4 对于已经JIT编译的方法,我们可以采用u命令,查看反汇编,然后就可以在对应的行号用非托管应用程序的bp命令来设置断点了。

以上两种方法可以在Windbg下设置断点。查找方法表还可以根据domain信息查找Module信息

0:000> !dumpdomain
--------------------------------------
System Domain:      67840f60
LowFrequencyHeap:   67841284
HighFrequencyHeap:  678412cc
StubHeap:           67841314
Stage:              OPEN
Name:               None
--------------------------------------
Shared Domain:      67840c08
LowFrequencyHeap:   67841284
HighFrequencyHeap:  678412cc
StubHeap:           67841314
Stage:              OPEN
Name:               None
Assembly:           003b24f0 [C:WindowsMicrosoft.NetassemblyGAC_32mscorlibv4.0_4.0.0.0__b77a5c561934e089mscorlib.dll]
ClassLoader:        003b25b8
  Module Name
66191000    C:WindowsMicrosoft.NetassemblyGAC_32mscorlibv4.0_4.0.0.0__b77a5c561934e089mscorlib.dll

--------------------------------------
Domain 1:           00364d50
LowFrequencyHeap:   003651a4
HighFrequencyHeap:  003651ec
StubHeap:           00365234
Stage:              OPEN
SecurityDescriptor: 00366920
Name:               Health.exe
Assembly:           003b24f0 [C:WindowsMicrosoft.NetassemblyGAC_32mscorlibv4.0_4.0.0.0__b77a5c561934e089mscorlib.dll]
ClassLoader:        003b25b8
SecurityDescriptor: 003af690
  Module Name
66191000    C:WindowsMicrosoft.NetassemblyGAC_32mscorlibv4.0_4.0.0.0__b77a5c561934e089mscorlib.dll

Assembly:           003bfbd8 [D:inHealth.exe]
ClassLoader:        003bfca0
SecurityDescriptor: 003bf880
  Module Name
00142edc    D:inHealth.exe

0:000> !dumpmodule -mt 00142edc    
Name:       D:inHealth.exe
Attributes: PEFile 
Assembly:   003bfbd8
LoaderHeap:              00000000
TypeDefToMethodTableMap: 00140038
TypeRefToMethodTableMap: 0014004c
MethodDefToDescMap:      0014009c
FieldDefToDescMap:       001400dc
MemberRefToDescMap:      00000000
FileReferencesMap:       001400f8
AssemblyReferencesMap:   001400fc
MetaData start address:  0123237c (2360 bytes)

Types defined in this module

      MT  TypeDef Name
------------------------------------------------------------------------------
001437b8 0x02000004 WindbugDemo.Health

Types referenced in this module

      MT    TypeRef Name
------------------------------------------------------------------------------
665941b8 0x02000001 System.Object
0:000> !dumpmt -md 001437b8
EEClass:         00141304
Module:          00142edc
Name:            WindbugDemo.Health
mdToken:         02000004
File:            D:inHealth.exe
BaseSize:        0xc
ComponentSize:   0x0
Slots in VTable: 6
Number of IFaces in IFaceMap: 0
--------------------------------------
MethodDesc Table
   Entry MethodDe    JIT Name
6647952c 6619612c PreJIT System.Object.ToString()
6648ec30 66196134 PreJIT System.Object.Equals(System.Object)
6648e860 66196154 PreJIT System.Object.GetHashCode()
6648e2a0 66196168 PreJIT System.Object.Finalize()
0014c015 001437b0   NONE WindbugDemo.Health..ctor()
0014c011 001437a4   NONE WindbugDemo.Health.Main(System.String[])

3.5 也可以通过JIT编译的代码地址来设置断点。

0:000> !name2ee  Health!WindbugDemo.HelthCompare
Module:      00142edc
Assembly:    Health.exe
Token:       02000003
MethodTable: 00143d6c
EEClass:     0014144c
Name:        WindbugDemo.HelthCompare
0:000> !dumpmt -md 00143d6c
EEClass:         0014144c
Module:          00142edc
Name:            WindbugDemo.HelthCompare
mdToken:         02000003
File:            D:inHealth.exe
BaseSize:        0xc
ComponentSize:   0x0
Slots in VTable: 6
Number of IFaces in IFaceMap: 1
--------------------------------------
MethodDesc Table
   Entry MethodDe    JIT Name
6647952c 6619612c PreJIT System.Object.ToString()
6648ec30 66196134 PreJIT System.Object.Equals(System.Object)
6648e860 66196154 PreJIT System.Object.GetHashCode()
6648e2a0 66196168 PreJIT System.Object.Finalize()
011f07b8 00143d5c    JIT WindbugDemo.HelthCompare.Compare(WindbugDemo.Person, WindbugDemo.Person)
011f0780 00143d64    JIT WindbugDemo.HelthCompare..ctor()
0:000> !dumpmd 00143d5c    
Method Name:  WindbugDemo.HelthCompare.Compare(WindbugDemo.Person, WindbugDemo.Person)
Class:        0014144c
MethodTable:  00143d6c
mdToken:      0600000c
Module:       00142edc
IsJitted:     yes
CodeAddr:     011f07b8
Transparency: Safe critical
0:000> bp 011f07b8

 

以上是关于Windbg如何设置应用程序的断点的主要内容,如果未能解决你的问题,请参考以下文章

WinDbg设置托管进程断点

转: windbg-跳过初始断点(调试技巧)

在windbg中的COM类方法上设置断点

windbg-bp bm bu bl bc ba(断点硬件断点)

windbg断点命中时,其它线程在运行吗

windbg查看内存断点是物理地址还是虚拟地址