Windbg在Managed App中設定函式斷點的幾種方法
本文介紹兩種使用Windbg在Managed App中設定斷點的方法。一種是在live Debug的時候,attach到了Process之後。另外一種是動態除錯的時候,如何給幾個模組的特定方法下一個斷點。
使用Windbg在Native Code裡面下斷點是比較方便的,bp加上一個記憶體地址就可以做到。但是在託管的時候給一個方法下一個斷點稍微有點麻煩。因為Windbg是一個native Debugger,而Managed App在沒有Jited的時候,是沒有生成Native Code的。每個方法在第一次呼叫之後,才生成了native code。當然,這裡不說ngen。
還是先給個小程式做小白鼠:
class Program
{
static void Main(string[] args)
{
System.Console.WriteLine("Show Params in Windbg");
Program p = new Program();
p.ShowParams(123456, "TestParams", 'L');
System.Console.ReadLine();
}
public void ShowParams(int a, string b ,char c)
{
}
}
在使用live Debug的時候,設定一個沒有原生代碼的方法斷點不是很麻煩,首先:
0:000> !name2ee *!FounctionParams.Program
Module: 790c2000 (mscorlib.dll)
--------------------------------------
Module: 00a82c3c (FounctionParams.exe)
Token: 0x02000002
MethodTable: 00a83038
EEClass: 00a811d8
Name: FounctionParams.Program
得到了MethodTable的地址了,然後:
0:000> !dumpmt -md 00a83038
EEClass: 00a811d8
Module: 00a82c3c
Name: FounctionParams.Program
mdToken: 02000002 (E:"myProject"FounctionParams"FounctionParams"bin"Debug"FounctionParams.exe)
BaseSize: 0xc
ComponentSize: 0x0
Number of IFaces in IFaceMap: 0
Slots in VTable: 7
--------------------------------------
MethodDesc Table
Entry MethodDesc JIT Name
79371278 7914b928 PreJIT System.Object.ToString()
7936b3b0 7914b930 PreJIT System.Object.Equals(System.Object)
7936b3d0 7914b948 PreJIT System.Object.GetHashCode()
793624d0 7914b950 PreJIT System.Object.Finalize()
00db0070 00a83020 JIT FounctionParams.Program.Main(System.String[])
00db0110 00a83028 JIT FounctionParams.Program.ShowParams
(Int32, System.String, Char)
00db00e0 00a83030 JIT FounctionParams.Program..ctor()
這裡,得到了這個方法的MethodDesc的地址之後,繼續:
0:000> !dumpmd 00a83028
Method Name: FounctionParams.Program.ShowParams(Int32, System.String, Char)
Class: 00a811d8
MethodTable: 00a83038
mdToken: 06000002
Module: 00a82c3c
IsJitted: yes
m_CodeOrIL: 00db0110
可以看到,已經JIT過了,生成了本地的程式碼。列個時候,就有很多選擇了,順便看看native code:
0:000> !u 00db0110
Normal JIT generated code
FounctionParams.Program.ShowParams(Int32, System.String, Char)
Begin 00db0110, size 20
>>> 00db0110 83ec08 sub esp,8
00db0113 890c24 mov dword ptr [esp],ecx
00db0116 89542404 mov dword ptr [esp+4],edx
00db011a 833d082ea80000 cmp dword ptr ds:[0A82E08h],0
00db0121 7405 je 00db0128
00db0123 e81f823779 call mscorwks!JIT_DbgIsJustMyCode (7a128347)
00db0128 90 nop
00db0129 90 nop
00db012a 83c408 add esp,8
00db012d c20800 ret 8
感覺和方法本身的程式碼不是很像,不過也就是他了,這個時候下斷點可以有很多選擇:
在地址上面下斷點:
0:000> bp 00db0110
用BPMD命令在這個方法的MethodDesc上面下斷點:
0:000> !bpmd -md 00a83028
MethodDesc = 00a83028
Setting breakpoint: bp 00DB0110 [FounctionParams.Program.ShowParams(Int32, System.String, Char)]
That will work。
如果是動態除錯下面,該如何做呢?這個比較麻煩點:
使用Windbg的Open Executable File選單,開啟上面編寫的測試的引用程式:
0:000> sxe ld:mscorjit
0:000> g
ModLoad: 79060000 790b6000 C:"WINDOWS"Microsoft.NET"Framework"v2.0.50727"mscorjit.dll
eax=00000000 ebx=00000000 ecx=00d90000 edx=7c90eb94 esi=00000000 edi=00000000
eip=7c90eb94 esp=0012e554 ebp=0012e648 iopl=0 nv up ei ng nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000296
ntdll!KiFastSystemCallRet:
7c90eb94 c3 ret
接著檢視執行緒:
0:000> !threads
ThreadCount: 2
UnstartedThread: 0
BackgroundThread: 1
PendingThread: 0
DeadThread: 0
Hosted Runtime: no
Hosted Runtime: no
PreEmptive GC Alloc
ID Context Domain Count APT Exception
0 1 013f16d0:013f1fe8 0015c410 2 MTA
2 2 00000000:00000000 0015c410 0 MTA (Finalizer)
接著就檢視這個domain:
0:000> !dumpdomain 0015c410
--------------------------------------
Domain 1: 0015c410
LowFrequencyHeap: 0015c434
HighFrequencyHeap: 0015c48c
StubHeap: 0015c4e4
Stage: OPEN
SecurityDescriptor: 0015d740
Name: FounctionParams.exe
Assembly: 0019f408
[C:"WINDOWS"assembly"GAC_32"mscorlib"2.0.0.0__b77a5c561934e089"mscorlib.dll]
ClassLoader: 0019f4a0
SecurityDescriptor: 0019be58
Module Name
790c2000
C:"WINDOWS"assembly"GAC_32"mscorlib"2.0.0.0__b77a5c561934e089"mscorlib.dll
Assembly: 001a6b90
[E:"myProject"FounctionParams"FounctionParams"bin"Debug"FounctionParams.exe]
ClassLoader: 001a7270
SecurityDescriptor: 001a6a58
Module Name
00a82c3c
E:"myProject"FounctionParams"FounctionParams"bin"Debug"FounctionParams.exe
然後檢視module的資訊:
0:000> !dumpmodule -mt 00a82c3c
Name: E:"myProject"FounctionParams"FounctionParams"bin"Debug"FounctionParams.exe
Attributes: PEFile
Assembly: 001a6b90
LoaderHeap: 00000000
TypeDefToMethodTableMap: 00a80038
TypeRefToMethodTableMap: 00a80040
MethodDefToDescMap: 00a8008c
FieldDefToDescMap: 00a8009c
MemberRefToDescMap: 00a800a0
FileReferencesMap: 00a800ec
AssemblyReferencesMap: 00a800f0
MetaData start address: 00402094 (1580 bytes)
Types defined in this module
MT TypeDef Name
------------------------------------------------------------------------------
00a83038 0x02000002 FounctionParams.Program
Types referenced in this module
MT TypeRef Name
------------------------------------------------------------------------------
790fd0f0 0x01000001 System.Object
恩,找到MT的地址了:
0:000> !dumpmt -md 00a83038
EEClass: 00a811d8
Module: 00a82c3c
Name: FounctionParams.Program
mdToken: 02000002 (E:"myProject"FounctionParams"FounctionParams"bin"Debug"FounctionParams.exe)
BaseSize: 0xc
ComponentSize: 0x0
Number of IFaces in IFaceMap: 0
Slots in VTable: 7
--------------------------------------
MethodDesc Table
Entry MethodDesc JIT Name
79371278 7914b928 PreJIT System.Object.ToString()
7936b3b0 7914b930 PreJIT System.Object.Equals(System.Object)
7936b3d0 7914b948 PreJIT System.Object.GetHashCode()
793624d0 7914b950 PreJIT System.Object.Finalize()
00a8c011 00a83020 NONE FounctionParams.Program.Main(System.String[])
00a8c015 00a83028 NONE FounctionParams.Program.ShowParams
(Int32, System.String, Char)
00a8c019 00a83030 NONE FounctionParams.Program..ctor()
恩,這個時候還沒有被JIT,正好可以在這個地方下一個斷點,當執行到這個方法的時候就觸發斷點:
0:000> !bpmd -md 00a83028
MethodDesc = 00a83028
Adding pending breakpoints...
恩,到這個地方,就給這個managed的方法,而且是沒jited的給加上了一個斷點。恩,兩種不同的情況下下斷點的方法。