程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> CLR探索系列 - Windbg+SOS動態調試分析托管代碼

CLR探索系列 - Windbg+SOS動態調試分析托管代碼

編輯:關於.NET

在使用VS進行托管應用程序的調試的時候,有的時候總感覺有些力不從心。譬如查看一個托管堆或者計算堆棧的時候,VS就不能勝任了。這個時候,Windbg+SOS擴展調試模塊就為我們提供了一個很好的解決方案。

我們看一段代碼:

class Program
  {
    static void Main(string[] args)
    {
      Program b = new Program();
      b.test();
      System.Console.ReadLine();
    }
    public void test()
    {
      int i = 67;
      System.Console.WriteLine((char)i);
      System.Console.WriteLine((char)67);
      i = 1;
    }
  }

這是C#裡面的一個強制類型轉換,我們現在用windbg+SOS來分析下計算堆棧,以及強制類型轉換之後的JIT代碼。

在windbg裡面加載這個正在運行的程序,attach to this process,然後加載SOS擴展調試模塊:

0:003> .load C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\SOS.dll

然後顯示當前的線程:

0:003> ~
  0 Id: cf0.450 Suspend: 1 Teb: 7ffdf000 Unfrozen
  1 Id: cf0.be8 Suspend: 1 Teb: 7ffdd000 Unfrozen
  2 Id: cf0.168 Suspend: 1 Teb: 7ffdc000 Unfrozen
. 3 Id: cf0.7d0 Suspend: 1 Teb: 7ffde000 Unfrozen

切換到第0個線程:

0:003> ~0s
eax=0012f2e4 ebx=00000000 ecx=0012f400 edx=00000008 esi=0012f1f4 edi=00250688
eip=7c92eb94 esp=0012f194 ebp=0012f1b4 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:
7c92eb94 c3       ret

顯示test方法相關的地址:

0:000> !name2ee TestConcoleApp.exe TestConcoleApp.Program.test
Module: 00ab2c24 (TestConcoleApp.exe)
Token: 0x06000002
MethodDesc: 00ab2ff0
Name: TestConcoleApp.Program.test()
JITTED Code Address: 00d000f8

顯示這個方法被C#編譯器編譯之後的IL代碼:

0:000> !dumpil 00ab2ff0
ilAddr = 00402074
IL_0000: nop
IL_0001: ldc.i4.s 67
IL_0003: stloc.0
IL_0004: ldloc.0
IL_0005: conv.u2
IL_0006: call System.Console::WriteLine
IL_000b: nop
IL_000c: ldc.i4.s 67
IL_000e: call System.Console::WriteLine
IL_0013: nop
IL_0014: ldc.i4.1
IL_0015: stloc.0
IL_0016: ret

這裡,sandwi對conv.u2這條指令一直困惑良多。我也對這個問題困惑了好久,翻閱了很多資料也沒找到,後來准備在sscli的C#編譯器裡面找到答案,不過沒找到地方......

後來被證實,這條指令是C#編譯器為了類型安全,而生成的一條指令。作用在於把一個integer轉換稱為一個unsigned int16,然後前面補0成為int32壓入堆棧裡面去。

這是一個語言編譯器行為,為了證實這個想法,同時寫了一段同樣的VB代碼來證實我們的想法:

Module Module1
  Sub Main()
    Dim i As Integer
    i = 67
    System.Console.WriteLine(Chr(i))
    System.Console.WriteLine(Chr(67))
    System.Console.ReadLine()
  End Sub
End Module

編譯之後的IL代碼也同樣支持上面的想法。

這裡,感謝微軟的張翼證實了我關於conv.u2的存在原因的猜想。但是,zhangyi說在test方法中的conv.u2指令在JIT生成的本地代碼中被優化掉了,我卻不同意這種看法:

0:000> !u 00d000f8

這條指令,是顯示JIT編譯了的test方法的本地代碼,根據

JITTED Code Address: 00d000f8

這一行得來的。顯示結果如下:

Normal JIT generated code
TestConcoleApp.Program.test()
push     esi
push     eax
mov     dword ptr [esp],ecx
cmp     dword ptr ds:[0AB2DD8h],0
je       00d0010b (跳到xor esi,esi這裡)
call      mscorwks!CorLaunchApplication+0x108b4 (7a08e179)
xor      esi,esi
nop
mov      esi,43h
movzx    ecx,si
call      mscorlib_ni+0x2f8b9c (793b8b9c) (System.Console.WriteLine(Char), mdToken: 06000759)
nop
mov      ecx,43h
call       mscorlib_ni+0x2f8b9c (793b8b9c) (System.Console.WriteLine(Char), mdToken: 06000759)
nop
mov      esi,1
pop      ecx
pop      esi
ret

這裡,movzx  ecx,si這條指令就對應了IL代碼裡面的Conv.u2,把對應的int前面補0放入到ecx寄存器裡面去。

後記:關於動態調試托管代碼,我也是剛接觸不久,上面有不准確的地方,歡迎大家多多指正。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved