當然,也可以通過命令行來主動獲取微軟官方調試符號。
symchk /r C:\Windows\System32\*.dll /s SRV*D:\symbols\*/download/symbolssymchk C:\WINDOWS\System32\KERNELBASE.dll /s SRV*D:\symbols\*/download/symbols
2、加載正確版本CLR以及SOS(Son of Strike)。
Windbg用戶模式下,調試.Net 應用程序,我們需要正確的加載CLR以及SOS(Son of Strike)。需要註意的是,加載的CLR以及SOS版本壹定要正確。如果應用程序在X86平臺下編譯,則需要加載C:\Windows\Microsoft.NET\Framework\v4.0.30319 目錄下的SOS以及CLR,64位下編譯的話,需要加載C:\Windows\Microsoft.NET\Framework64\v4.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*/download/symbols;d:\source
Expanded Symbol search path is: cache*d:\symbols;srv*/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:\Windows\Microsoft.NET\Framework\v4.0.30319\clrjit.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:\Windows\Microsoft.NET\Framework\v4.0.30319\sos.dll
0:000> .load C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.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:\bin\Health.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:\bin\Health.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\02Health\Health.cs @ 48:
011f0050 55 push ebp
011f0051 8bec mov ebp,esp
... ...
d:\source\02Health\Health.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:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll]
ClassLoader: 003b25b8
Module Name
66191000 C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
--------------------------------------
Domain 1: 00364d50
LowFrequencyHeap: 003651a4
HighFrequencyHeap: 003651ec
StubHeap: 00365234
Stage: OPEN
SecurityDescriptor: 00366920
Name: Health.exe
Assembly: 003b24f0 [C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll]
ClassLoader: 003b25b8
SecurityDescriptor: 003af690
Module Name
66191000 C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
Assembly: 003bfbd8 [D:\bin\Health.exe]
ClassLoader: 003bfca0
SecurityDescriptor: 003bf880
Module Name
00142edc D:\bin\Health.exe
0:000> !dumpmodule -mt 00142edc
Name: D:\bin\Health.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:\bin\Health.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!Win