Basic Windbg - 2 CLR基础 (转)
Basic Windbg - 2 CLR基础
调整一下写的思路,第一部分的sos basics继续有效,原定的crash/hang/memory等,序号顺延,中间插入这个clr basics
创建一个控制台程序,代码如下:
Code
using System;
using System.Collections.Generic;
using System.Text;
namespace HelloWindbg
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(\Hello, windbg!\);
Console.ReadLine();
}
}
}
在IDE中,按Ctrl+F5运行,等到显示出来\Hello, windbg!”之后(不要按回车),启动windbg,在窗口中按F6,此时出现一个process list窗口,向下找,找到这个hellowindbg.exe进程(名字是你project编译后的exe名字,不一定与我这个相同),然后点OK确定。此时windbg会显示类似信息:
(1270.11c4): Break instruction exception - code 80000003 (first chance)
eax=7ffdc000 ebx=00000000 ecx=00000000 edx=7786d094 esi=00000000 edi=00000000
eip=77827dfe esp=01bffbcc ebp=01bffbf8 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!DbgBreakPoint:
77827dfe cc int 3
这时候,加载sos扩展,命令如下:.load clr20\sos.dll
好了,可以干活了!
首先看第一个命令,!dumpdomain,运行后,我们得到如下类似结果:
0:003> !dumpdomain
--------------------------------------
System Domain: 7a3bc8b8
LowFrequencyHeap: 7a3bc8dc
HighFrequencyHeap: 7a3bc934
StubHeap: 7a3bc98c
Stage: OPEN
Name: None
--------------------------------------
Shared Domain: 7a3bc560
LowFrequencyHeap: 7a3bc584
HighFrequencyHeap: 7a3bc5dc
StubHeap: 7a3bc634
Stage: OPEN
Name: None
Assembly: 003b9368
--------------------------------------
Domain 1: 00374ec0
LowFrequencyHeap: 00374ee4
HighFrequencyHeap: 00374f3c
StubHeap: 00374f94
Stage: OPEN
SecurityDescriptor: 00376420
Name: HelloWindbg.exe
Assembly: 003b9368 [C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll]
ClassLoader: 0036cd88
SecurityDescriptor: 003ae6e8
Module Name
790c2000 C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll
001b239c C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\sortkey.nlp
001b2010 C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\sorttbls.nlp
Assembly: 003cab78 [C:\Users\charju\Documents\Visual Studio 2008\Projects\Windbg\HelloWindbg\HelloWindbg\bin\Debug\HelloWindbg.exe]
ClassLoader: 0036ced8
SecurityDescriptor: 003caaf0
Module Name
00192d8c C:\Users\charju\Documents\Visual Studio 2008\Projects\Windbg\HelloWindbg\HelloWindbg\bin\Debug\HelloWindbg.exe
对于上面的结果,我们有几个重要发现:
1、.net app默认情况下,会有三个AppDomain,其中分别是System Domain,Shared Domain和应用程序自己的Application Domain(就是上面的Domain 1)。
2、一个domain内包含了一个或若干个assembly
对于每个具体的domain,都可以用!dumpdomain <domain assress>来看。比如这里的app domain,可以执行命令:!dumpdomain 00374ec0,结果就不贴了,和上面的显示是一致的。
这个app domain里面我们看到,包含了两个assembly,分别是mscorlib.dll和hellowindbg.exe,前者包含了3个module,后者包含了1个module。这里我们看hellowindbg.exe的信息,对于mscorlib.dll,大家可以依据下面步骤做练习。
看assmebly信息,命令式:!dumpassembly <assembly address>,so,我们执行:!dumpassembly 003cab78(这个地址是上面的蓝色的那个数字),得到类似信息:
Parent Domain: 00374ec0
Name: C:\Users\charju\Documents\Visual Studio 2008\Projects\Windbg\HelloWindbg\HelloWindbg\bin\Debug\HelloWindbg.exe
ClassLoader: 0036ced8
SecurityDescriptor: 018c4448
Module Name
00192d8c C:\Users\charju\Documents\Visual Studio 2008\Projects\Windbg\HelloWindbg\HelloWindbg\bin\Debug\HelloWindbg.exe
这里有几个地方可以注意:
1、assembly包含了指向domain的信息,上面的:00374ec0
2、assembly包含了一个或者若干个module。
这里只有一个module,我们继续看module里面包含啥,结果从略(我们这里不关心它)。
命令!dumpmodule有一个有用的开关,是-mt <mt address>,我们跑一下看看:!dumpmodule -mt 1caa50
0:003> !dumpmodule -mt 00192d8c
Name: C:\Users\charju\Documents\Visual Studio 2008\Projects\Windbg\HelloWindbg\HelloWindbg\bin\Debug\HelloWindbg.exe
Attributes: PEFile
Assembly: 003cab78
LoaderHeap: 00000000
TypeDefToMethodTableMap: 00190148
TypeRefToMethodTableMap: 00190150
MethodDefToDescMap: 0019019c
FieldDefToDescMap: 001901a8
MemberRefToDescMap: 001901ac
FileReferencesMap: 001901f8
AssemblyReferencesMap: 001901fc
MetaData start address: 000b206c (1456 bytes)
Types defined in this module
MT TypeDef Name
------------------------------------------------------------------------------
00193180 0x02000002 HelloWindbg.Program
Types referenced in this module
MT TypeRef Name
------------------------------------------------------------------------------
790fd0f0 0x01000001 System.Object
79101118 0x01000012 System.Console
我们能发现两个重要的东西:
1、这个module里面包含的types,这里是我们的程序主入口点:HelloWindbg.Program
2、这个module引用的types,这里是object和Console
看上面的HelloWindbg.Program,这个type对应的MethodTable地址是:00193180。我们可以继续看这个MethodTable包含啥东西:
0:003> !dumpmt 00193180
EEClass: 001912e4
Module: 00192d8c
Name: HelloWindbg.Program
mdToken: 02000002 (C:\Users\charju\Documents\Visual Studio 2008\Projects\Windbg\HelloWindbg\HelloWindbg\bin\Debug\HelloWindbg.exe)
BaseSize: 0xc
ComponentSize: 0x0
Number of IFaces in IFaceMap: 0
Slots in VTable: 6
这里有一些重要的东西可以看:
1、EEClass,可以通过!dumpclass <class address>来查看这个class的信息。如:!dumpclass 001912e4
2、包含了Module和name等信息
3、包含了Method Description 信息
4、VTable里面的slots一共6个。
MethodTable只是一个入口表,它实际指向了一堆Method Description,那么我们可以用!dumpmt继续来查看,只不过参数要修改一下:!dumpmt -md 00193180
0:003> !dumpmt -md 00193180
EEClass: 001912e4
Module: 00192d8c
Name: HelloWindbg.Program
mdToken: 02000002 (C:\Users\charju\Documents\Visual Studio 2008\Projects\Windbg\HelloWindbg\HelloWindbg\bin\Debug\HelloWindbg.exe)
BaseSize: 0xc
ComponentSize: 0x0
Number of IFaces in IFaceMap: 0
Slots in VTable: 6
--------------------------------------
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()
00330070 00193170 JIT HelloWindbg.Program.Main(System.String[])
0019c01c 00193178 NONE HelloWindbg.Program..ctor()
注意---------------------以上的部分,和我们不加-md参数效果是一样的,下面多出来了6个method description的信息,这就是那个Slots in VTable:6里面的6
对于每个方法,上表有一列JIT指明了CLR是否已经对该方法进行了compile动作。上面有一个方法是JIT过了,是那个Main函数,另外的4个CLR已经预先做了JIT(为什么?想想GAC和NGEN.EXE)。那么对于具体的方法是否做过了JIT,我们可以用命令:!dumpmd来看,比如看上面的Main方法,那么!dumpmd 00193170
0:003> !dumpmd 00193170
Method Name: HelloWindbg.Program.Main(System.String[])
Class: 001912e4
MethodTable: 00193180
mdToken: 06000001
Module: 00192d8c
IsJitted: yes
m_CodeOrIL: 00330070 text here
上面最重要的结果就是有一个IsJitted的标志,这里是yes,表明它已经被compile过了,对于compile之后的native code,我们就可以用命令!u <m_codeoril>来观察。下命令:!u 00330070,如下代码:
0:003> !u 00330070
Normal JIT generated code
HelloWindbg.Program.Main(System.String[])
Begin 00330070, size 28
>>> 00330070 50 push eax
00330071 890c24 mov dword ptr [esp],ecx
00330074 833d582f190000 cmp dword ptr ds:[192F58h],0
0033007b 7405 je HelloWindbg!HelloWindbg.Program.Main(System.String[])+0x12 (00330082)
0033007d e8c582df79 call mscorwks!JIT_DbgIsJustMyCode (7a128347)
00330082 90 nop
00330083 8b0d4030d602 mov ecx,dword ptr ds:[2D63040h] (\Hello, windbg!\)
00330089 e8c69c0b79 call mscorlib_ni!System.Console.WriteLine(System.String) (793e9d54)
0033008e 90 nop
0033008f e850990b79 call mscorlib_ni!System.Console.ReadLine() (793e99e4)
00330094 90 nop
00330095 90 nop
00330096 59 pop ecx
00330097 c3 ret
类似的,我们可以!dumpmd 00193178,看到该方法并没有被compile(现在),native code自然也没有生成:
0:003> !dumpmd 00193178
Method Name: HelloWindbg.Program..ctor()
Class: 001912e4
MethodTable: 00193180
mdToken: 06000002
Module: 00192d8c
IsJitted: no
m_CodeOrIL: ffffffff
好了,对于appdomain/assembly/module/method table(class)/method description,这里做了一个简单的research,我们看点别的。
0:000> !clrstack
OS Thread Id: 0x12d0 (0)
ESP EIP
002bf148 77839a94 [NDirectMethodFrameStandaloneCleanup: 002bf148] System.IO.__ConsoleStream.ReadFile(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr)
002bf164 7948d2bb System.IO.__ConsoleStream.ReadFileNative(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte[], Int32, Int32, Int32, Int32 ByRef)
002bf190 7948d1ed System.IO.__ConsoleStream.Read(Byte[], Int32, Int32)
002bf1b0 793a3350 System.IO.StreamReader.ReadBuffer()
002bf1c0 793aaa2f System.IO.StreamReader.ReadLine()
002bf1d4 79497b5a System.IO.TextReader+SyncTextReader.ReadLine()
002bf1dc 793e99f0 System.Console.ReadLine()
002bf1e0 00330094 HelloWindbg.Program.Main(System.String[])
002bf404 79e7c74b [GCFrame: 002bf404]
这里列出来的都是一些name,比如说我想看StreamReader里面有哪些方法,怎么看呢?
1、转换:用命令!name2ee <modulename>!<type or method name>。那么,module name是什么呢?我们要用lm来看一眼,加上一点clr的知识:
0:000> lm
start end module name
000b0000 000b8000 HelloWindbg C (private pdb symbols) C:\Users\charju\Documents\Visual Studio 2008\Projects\Windbg\HelloWindbg\HelloWindbg\bin\Debug\HelloWindbg.pdb
75070000 7520e000 comctl32 (deferred)
75310000 753ab000 MSVCR80 (deferred)
761f0000 762cb000 KERNEL32 (pdb symbols) c:\symcache\kernel32.pdb\093FA0AF6A7B4CE9B12506584036EC882\kernel32.pdb
762d0000 76328000 SHLWAPI (deferred)
764b0000 7654d000 USER32 (deferred)
76610000 7662e000 IMM32 (deferred)
766c0000 7676a000 msvcrt (deferred)
76770000 767bb000 GDI32 (deferred)
767c0000 76883000 RPCRT4 (deferred)
76890000 76956000 ADVAPI32 (deferred)
76ab0000 775bf000 shell32 (deferred)
775c0000 77688000 MSCTF (deferred)
77690000 777d4000 ole32 (deferred)
777e0000 77907000 ntdll (pdb symbols) c:\symcache\ntdll.pdb\B958B2F91A5A46B889DAFAB4D140CF252\ntdll.pdb
77910000 77919000 LPK (deferred)
779c0000 77a3d000 USP10 (deferred)
79000000 79046000 mscoree (pdb symbols) c:\symcache\mscoree.pdb\B3B672BC69034D0F8EF2A40525E64BDE2\mscoree.pdb
79060000 790b6000 mscorjit (deferred)
790c0000 79bf6000 mscorlib_ni C (pdb symbols) c:\symcache\mscorlib.pdb\446AC27A973142A6900EEAF1E9EC50451\mscorlib.pdb
79e70000 7a3ff000 mscorwks (pdb symbols) c:\symcache\mscorwks.pdb\8BDE5914A40043B3BCC3E7F49A6C29D22\mscorwks.pdb
最终决定用这个命令:!name2ee mscorlib_ni!System.IO.StreamReader.ReadBuffer,得到下面结果:
Module: 790c2000 (mscorlib.dll)
Token: 0x06003622
MethodDesc: 7926c0a0
Name: System.IO.StreamReader.ReadBuffer()
JITTED Code Address: 793a32f4
-----------------------
Token: 0x06003623
MethodDesc: 7926c0a8
Name: System.IO.StreamReader.ReadBuffer(Char[], Int32, Int32, Boolean ByRef)
JITTED Code Address: 79498020
好了,可以走第二步了,因为上面已经拿到了Method Description的地址了,跑:!dumpmd 7926c0a0,如下:
0:000> !dumpmd 7926c0a0
Method Name: System.IO.StreamReader.ReadBuffer()
Class: 7911b210
MethodTable: 7911b288
mdToken: 06003622
Module: 790c2000
IsJitted: yes
m_CodeOrIL: 793a32f4
看到了上面的Method table地址了没?用!dumpmt -md 7911b288,可以得到这个class的所有method信息(结果从略,大家做练习吧!)