首页 > 代码库 > Windbg CLR基础小测 《第六篇》

Windbg CLR基础小测 《第六篇》

  首先写一段代码如下:

namespace ConsoleApplication3
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello, Windbg!");

            Console.ReadKey();
        }
    }
}

  在Debug目录中启动该程序,然后在Debug中附加该进程。

0:007> .load C:/WINDOWS/Microsoft.NET/Framework/v4.0.30319/sos.dll
0:007> !dumpdomain
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll - 
PDB symbol for clr.dll not loaded
--------------------------------------
System Domain:      7106e898
LowFrequencyHeap:   7106ebbc
HighFrequencyHeap:  7106ec04
StubHeap:           7106ec4c
Stage:              OPEN
Name:               None
--------------------------------------
Shared Domain:      7106e4f0
LowFrequencyHeap:   7106ebbc
HighFrequencyHeap:  7106ec04
StubHeap:           7106ec4c
Stage:              OPEN
Name:               None
Assembly:           0051a1b8 [C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll]
ClassLoader:        00501660
  Module Name
697e1000    C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll

--------------------------------------
Domain 1:           004e78a8
LowFrequencyHeap:   004e7cfc
HighFrequencyHeap:  004e7d44
StubHeap:           004e7d8c
Stage:              OPEN
SecurityDescriptor: 004e9388
Name:               ConsoleApplication3.exe
Assembly:           0051a1b8 [C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll]
ClassLoader:        00501660
SecurityDescriptor: 0051a130
  Module Name
697e1000    C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll

Assembly:           00521db8 [C:\Users\ChenZhuo\Documents\visual studio 2010\Projects\ConsoleApplication3\ConsoleApplication3\bin\Debug\ConsoleApplication3.exe]
ClassLoader:        005016c8
SecurityDescriptor: 00521d30
  Module Name
00212eac    C:\Users\ChenZhuo\Documents\visual studio 2010\Projects\ConsoleApplication3\ConsoleApplication3\bin\Debug\ConsoleApplication3.exe

  从上面的输出结果中,有如下发现:

  1. .Net app默认情况下,会有三个AppDomain,其中分别是System Domain,Shared Domain和应用程序自己的Application Domain(就是上面的Domain1);
  2. 一个domain内包含一个或若干个Assembly;

 如果仅仅只是想查看一个domain,则可以使用如下命令,其输出结果与上面的结果一致:

0:007> !dumpdomain 004e78a8
--------------------------------------
Domain 1:           004e78a8
LowFrequencyHeap:   004e7cfc
HighFrequencyHeap:  004e7d44
StubHeap:           004e7d8c
Stage:              OPEN
SecurityDescriptor: 004e9388
Name:               ConsoleApplication3.exe
Assembly:           0051a1b8 [C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll]
ClassLoader:        00501660
SecurityDescriptor: 0051a130
  Module Name
697e1000    C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll

Assembly:           00521db8 [C:\Users\ChenZhuo\Documents\visual studio 2010\Projects\ConsoleApplication3\ConsoleApplication3\bin\Debug\ConsoleApplication3.exe]
ClassLoader:        005016c8
SecurityDescriptor: 00521d30
  Module Name
00212eac    C:\Users\ChenZhuo\Documents\visual studio 2010\Projects\ConsoleApplication3\ConsoleApplication3\bin\Debug\ConsoleApplication3.exe

  在上面的app domain中,我们可以看到包含了2个assembly,分别是mscorlib.dll和ConsoleApplication3.exe。

  都有1个module。这里我们看ConsoleApplication3.exe。

  看assembly信息,命令使用:!dumpassembly <assembly address>,我们执行:!dumpassembly <00212eac> 这个地址就在appdomain命令返回的程序集路径的左边。

0:007> !dumpassembly 00521db8
Parent Domain:      004e78a8
Name:               C:\Users\ChenZhuo\Documents\visual studio 2010\Projects\ConsoleApplication3\ConsoleApplication3\bin\Debug\ConsoleApplication3.exe
ClassLoader:        005016c8
  Module Name
00212eac    C:\Users\ChenZhuo\Documents\visual studio 2010\Projects\ConsoleApplication3\ConsoleApplication3\bin\Debug\ConsoleApplication3.exe

  注意:

  1. assembly包含了指向domain的信息,上面的004e78a8;
  2. assembly包含了一个或者若干个module;

  这里只有一个module,我们继续看module里面包含啥。

  命令!dumpmodule有一个有用的开关,是-mt <mt address>,我们跑一下看看:!dumpmodule -mt 1caa50

0:007> !dumpmodule -mt 00212eac
Name:       C:\Users\ChenZhuo\Documents\visual studio 2010\Projects\ConsoleApplication3\ConsoleApplication3\bin\Debug\ConsoleApplication3.exe
Attributes: PEFile 
Assembly:   00521db8
LoaderHeap:              00000000
TypeDefToMethodTableMap: 00210038
TypeRefToMethodTableMap: 00210044
MethodDefToDescMap:      00210098
FieldDefToDescMap:       002100a4
MemberRefToDescMap:      002100a8
FileReferencesMap:       002100fc
AssemblyReferencesMap:   00210100
MetaData start address:  012a206c (1628 bytes)

Types defined in this module

      MT  TypeDef Name
------------------------------------------------------------------------------
002137c0 0x02000002 ConsoleApplication3.Program

Types referenced in this module

      MT    TypeRef Name
------------------------------------------------------------------------------
69bb25e8 0x02000001 System.Object
69bb869c 0x02000013 System.Console

  我们能发现两个重要的东西:

  1. 这个module里面包含的types,这里是我们的程序主入口点:ConsoleApplication3.Program;
  2. 这个module引用的types,这里是object和Console;

  看上面的ConsoleApplication3.Program,这个type对应的MethodTable地址是:002137c0。我们可以继续看这个MethodTable包含啥东西:

0:007> !dumpmt 00212eac
00212eac is not a MethodTable
0:007> !dumpmt 002137c0
EEClass:         0021125c
Module:          00212eac
Name:            ConsoleApplication3.Program
mdToken:         02000002
File:            C:\Users\ChenZhuo\Documents\visual studio 2010\Projects\ConsoleApplication3\ConsoleApplication3\bin\Debug\ConsoleApplication3.exe
BaseSize:        0xc
ComponentSize:   0x0
Slots in VTable: 6
Number of IFaces in IFaceMap: 0

  这里有一些重要的东西可以看:

  1. EEClass,可以通过!dumpclass <class address>来查看这个class的信息。如:!dumpclass 0021125c;
  2. 包含了Module和name等信息;
  3. 包含了Method Description 信息;
  4. VTable里面的slots一共6个;

  MethodTable只是一个入口表,它实际指向了一堆Method Description,那么我们可以用!dumpmt继续来查看,只不过参数要修改一下:!dumpmt -md 002137c0

0:007> !dumpmt 002137c0
EEClass:         0021125c
Module:          00212eac
Name:            ConsoleApplication3.Program
mdToken:         02000002
File:            C:\Users\ChenZhuo\Documents\visual studio 2010\Projects\ConsoleApplication3\ConsoleApplication3\bin\Debug\ConsoleApplication3.exe
BaseSize:        0xc
ComponentSize:   0x0
Slots in VTable: 6
Number of IFaces in IFaceMap: 0
0:007> !dumpmt -md 00212eac
00212eac is not a MethodTable
0:007> !dumpmt -md 00212eac
00212eac is not a MethodTable
0:007> !dumpmt -md 00212eac
00212eac is not a MethodTable
0:007> !dumpmt -md 00212eac
00212eac is not a MethodTable
0:007> !dumpmt -md 002137c0
EEClass:         0021125c
Module:          00212eac
Name:            ConsoleApplication3.Program
mdToken:         02000002
File:            C:\Users\ChenZhuo\Documents\visual studio 2010\Projects\ConsoleApplication3\ConsoleApplication3\bin\Debug\ConsoleApplication3.exe
BaseSize:        0xc
ComponentSize:   0x0
Slots in VTable: 6
Number of IFaces in IFaceMap: 0
--------------------------------------
MethodDesc Table
   Entry MethodDe    JIT Name
69accd88 697e60bc PreJIT System.Object.ToString()
69ac6a90 697e60c4 PreJIT System.Object.Equals(System.Object)
69ac6660 697e60e4 PreJIT System.Object.GetHashCode()
69b467c0 697e60f8 PreJIT System.Object.Finalize()
0021c015 002137b8   NONE ConsoleApplication3.Program..ctor()
00480050 002137ac    JIT ConsoleApplication3.Program.Main(System.String[])

  加了-md之后多出来了6个method description的信息,这就是那个Slots in VTable:6里面的6

  对于每个方法,上表有一列JIT指明了CLR是否已经对该方法进行了编译动作。上面有一个方法是JIT过了,是那个Main函数,另外的4个CLR已经预先做了JIT(为什么?想想GAC和NGEN.EXE)。那么对于具体的方法是否做过了JIT,我们可以用命令:!dumpmd来看,比如看上面的Main方法,那么!dumpmd 002137c0

0:007> !dumpmt 002137c0
EEClass:         0021125c
Module:          00212eac
Name:            ConsoleApplication3.Program
mdToken:         02000002
File:            C:\Users\ChenZhuo\Documents\visual studio 2010\Projects\ConsoleApplication3\ConsoleApplication3\bin\Debug\ConsoleApplication3.exe
BaseSize:        0xc
ComponentSize:   0x0
Slots in VTable: 6
Number of IFaces in IFaceMap: 0
0:007> !dumpmt -md 00212eac
00212eac is not a MethodTable
0:007> !dumpmt -md 00212eac
00212eac is not a MethodTable
0:007> !dumpmt -md 00212eac
00212eac is not a MethodTable
0:007> !dumpmt -md 00212eac
00212eac is not a MethodTable
0:007> !dumpmt -md 002137c0
EEClass:         0021125c
Module:          00212eac
Name:            ConsoleApplication3.Program
mdToken:         02000002
File:            C:\Users\ChenZhuo\Documents\visual studio 2010\Projects\ConsoleApplication3\ConsoleApplication3\bin\Debug\ConsoleApplication3.exe
BaseSize:        0xc
ComponentSize:   0x0
Slots in VTable: 6
Number of IFaces in IFaceMap: 0
--------------------------------------
MethodDesc Table
   Entry MethodDe    JIT Name
69accd88 697e60bc PreJIT System.Object.ToString()
69ac6a90 697e60c4 PreJIT System.Object.Equals(System.Object)
69ac6660 697e60e4 PreJIT System.Object.GetHashCode()
69b467c0 697e60f8 PreJIT System.Object.Finalize()
0021c015 002137b8   NONE ConsoleApplication3.Program..ctor()
00480050 002137ac    JIT ConsoleApplication3.Program.Main(System.String[])
0:007> !dumpmd 00480050
00480050 is not a MethodDesc
0:007> !dumpmd 00480050
00480050 is not a MethodDesc
0:007> !dumpmd 002137ac
Method Name:  ConsoleApplication3.Program.Main(System.String[])
Class:        0021125c
MethodTable:  002137c0
mdToken:      06000001
Module:       00212eac
IsJitted:     yes
CodeAddr:     00480050
Transparency: Critical

  上面最重要的结果就是有一个IsJitted的标志,这里是yes,表明它已经被compile过了,对于compile之后的native code,我们就可以用命令!u <m_codeoril>来观察。下命令:!u 00480050

0:007> !u 00480050
Normal JIT generated code
ConsoleApplication3.Program.Main(System.String[])
Begin 00480050, size 32
*** WARNING: Unable to verify checksum for C:\Users\ChenZhuo\Documents\visual studio 2010\Projects\ConsoleApplication3\ConsoleApplication3\bin\Debug\ConsoleApplication3.exe

C:\Users\ChenZhuo\documents\visual studio 2010\Projects\ConsoleApplication3\ConsoleApplication3\Program.cs @ 14:
>>> 00480050 55              push    ebp
00480051 8bec            mov     ebp,esp
00480053 83ec10          sub     esp,10h
00480056 894dfc          mov     dword ptr [ebp-4],ecx
00480059 833d7831210000  cmp     dword ptr ds:[213178h],0
00480060 7405            je      00480067
00480062 e8dcc98470      call    clr!GetHistoryFileDirectory+0x8b758 (70ccca43) (JitHelp: CORINFO_HELP_DBG_IS_JUST_MY_CODE)
00480067 90              nop

C:\Users\ChenZhuo\documents\visual studio 2010\Projects\ConsoleApplication3\ConsoleApplication3\Program.cs @ 15:
00480068 8b0d7021eb02    mov     ecx,dword ptr ds:[2EB2170h] ("Hello, Windbg!")
*** WARNING: Unable to verify checksum for C:\Windows\assembly\NativeImages_v4.0.30319_32\mscorlib\045c9588954c3662d542b53f4462268b\mscorlib.ni.dll
0048006e e885796569      call    mscorlib_ni+0x2f79f8 (69ad79f8) (System.Console.WriteLine(System.String), mdToken: 06000995)
00480073 90              nop

C:\Users\ChenZhuo\documents\visual studio 2010\Projects\ConsoleApplication3\ConsoleApplication3\Program.cs @ 17:
00480074 8d4df0          lea     ecx,[ebp-10h]
00480077 e82460d369      call    mscorlib_ni+0x9d60a0 (6a1b60a0) (System.Console.ReadKey(), mdToken: 06000974)
0048007c 90              nop

C:\Users\ChenZhuo\documents\visual studio 2010\Projects\ConsoleApplication3\ConsoleApplication3\Program.cs @ 18:
0048007d 90              nop
0048007e 8be5            mov     esp,ebp
00480080 5d              pop     ebp
00480081 c3              ret

  见到源代码了,字符串都看到了。

  如果我们想看看StreamReader里面有哪些方法,应该用什么命令呢?

  命令:!name2ee mscorlib_ni!System.IO.StreamReader.ReadBuffer

0:007> !name2ee mscorlib_ni!System.IO.StreamReader.ReadBuffer
Module:      697e1000
Assembly:    mscorlib.dll
Token:       06004733
MethodDesc:  6985f3dc
Name:        System.IO.StreamReader.ReadBuffer()
JITTED Code Address: 69b18020
-----------------------
Token:       0600473f
MethodDesc:  6985f460
Name:        System.IO.StreamReader.ReadBuffer(Char[], Int32, Int32, Boolean ByRef)
JITTED Code Address: 69b763d0

  拿到了Method Description的地址后执行:!dumpmd 6985f3dc

0:007> !dumpmd 6985f3dc
Method Name:  System.IO.StreamReader.ReadBuffer()
Class:        6983e558
MethodTable:  69ba8fd0
mdToken:      06004733
Module:       697e1000
IsJitted:     yes
CodeAddr:     69b18020
Transparency: Transparent

  再执行!dumpmt -md 69ba8fd0,就可以得到这个class的所有method信息

0:007> !dumpmt -md 69ba8fd0
EEClass:         6983e558
Module:          697e1000
Name:            System.IO.StreamReader
mdToken:         02000789
File:            C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
BaseSize:        0x40
ComponentSize:   0x0
Slots in VTable: 69
Number of IFaces in IFaceMap: 1
--------------------------------------
MethodDesc Table
   Entry MethodDe    JIT Name
69accd88 697e60bc PreJIT System.Object.ToString()
69ac6a90 697e60c4 PreJIT System.Object.Equals(System.Object)
69ac6660 697e60e4 PreJIT System.Object.GetHashCode()
69b467c0 697e60f8 PreJIT System.Object.Finalize()
69b7f310 69872554 PreJIT System.MarshalByRefObject.GetLifetimeService()
69aafd70 6987255c PreJIT System.MarshalByRefObject.InitializeLifetimeService()
69b6fdbc 69872564 PreJIT System.MarshalByRefObject.CreateObjRef(System.Type)
69b1bbe0 6985f360 PreJIT System.IO.StreamReader.Close()
69b4ea80 6985ce74 PreJIT System.IO.TextReader.Dispose()
69b17a80 6985f368 PreJIT System.IO.StreamReader.Dispose(Boolean)  ... ...省略一部分