阅读文章

Basic Windbg - 2 CLR基础

[日期:2008-03-05] 来源:  作者: [字体: ]

    
  调整一下写的思路,第一部分的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信息(结果从略,大家做练习吧!)
  
  (累死我了。。。园子这个post编辑上工具条上的Insert Layer,不好用。结构都乱了!只好重新修改一次)
  
    


阅读:
录入:blue1000

评论 】 【 推荐 】 【 打印
上一篇:关于分层结构的感悟,请指教
下一篇:什么是专业的程序员
相关文章      
本文评论
发表评论


点评: 字数
姓名:

  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或删除其管辖留言中的任意内容
  • 本站有权在网站内转载或引用您的评论
  • 参与本评论即表明您已经阅读并接受上述条款