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:source 2HealthHealth.cs @ 48: 011f0050 55 push ebp 011f0051 8bec mov ebp,esp ... ... d:source 2HealthHealth.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如何设置应用程序的断点的主要内容,如果未能解决你的问题,请参考以下文章