.NET / Rotor原始碼研究3 – 除錯Rotor託管程式碼的利器:WinDbg和SOS

ATField發表於2007-05-12

WinDbg+SOS簡介

在動手進一步研究Rotor之前,我們需要首先解決一個問題:用什麼除錯工具最好? 很有可能你會說,這還不簡單,直接用Visual Studio不就好了?一般情況下是的,只不過,在這個情況下,Visual Studio並非是最好的選擇:

1.     CLR對程式碼的編譯是動態進行的(其實還有可能是靜態的,稱之為Prejit或者NGEN,不過可惜Rotor對此不支援),也就是說常規的設定斷點的方法並不適用,因為可能這個時候對應的原生程式碼還沒有生成呢

2.     CLR是一個複雜的執行時環境/虛擬機器。有些時候我們需要獲得CLR的一些相關資訊,而這個時候Visual Studio就顯得功能不夠強大

你可能會說,那為什麼VS可以除錯託管程式碼呢?確實,VS是可以除錯託管程式碼,但是注意我們是除錯CLR本身,這個時候VS本身的對託管程式碼的除錯功能會造成不少麻煩,得到一些你所不希望的結果。此外,由於我們是在除錯Rotor,這個時候不能直接執行託管程式碼,而是要在CLIX的支援下執行,這也使得VS的託管程式碼除錯功能無效。

這個時候,相對而言較好的工具是WinDbg+SOS的組合。

WinDbg是微軟提供的Windows下面的免費偵錯程式,具有下面特點:

1.     支援使用者模式和核心模式下的除錯

2.     支援GUI(雖然看起來是廢話,不過WinDbg也有所謂命令列版本NTSD/CDB/KD,相對於這幾個偵錯程式而言WinDbg算是很友好的了)

3.     支援遠端除錯

4.     支援符號伺服器和原始碼伺服器

5.     支援原始碼級別的除錯(不支援也不用出來混了,呵呵)

6.     非常輕量級,完整地版本僅數十兆(相對於VS而言),如果只需要偵錯程式本身,還可以更小

7.     支援對Dump檔案的除錯

8.     可擴充套件

WinDbg可以在下面的地址下載:

32位版本:http://www.microsoft.com/whdc/devtools/debugging/installx86.mspx

64位版本:http://www.microsoft.com/whdc/devtools/debugging/install64bit.mspx

SOS全稱是Son of Strike(別問我為什麼,我也不知道,這個要問Larry了),是一個WinDbgExtension,載入之後可以給WinDbg提供一些對託管程式碼除錯的命令。值得一提的是,WinDbg也有有限的對託管程式碼的支援,只不過功能非常有限,需要SOS的幫助。

SOS.NET Framework中和Rotor中均有提供,所以本文中對於WinDbgSOS的描述基本上適用於Rotor和發行版的.NET Framework(而且有可能也將適用於Silverlight)。比如.NET Framework 2.0中的SOSC:/Windows/Microsoft .NET/v2.0.50727下面,而RotorSOS.dll則是在binaries.<flavor>.rotor目錄下面(flavorBuild的配置,如CPUDebug/Release,等等)。

SOS可以做到:

1.     輸出物件的內容

2.     察看CLR的資料結構

3.     察看程式碼和堆疊

4.     顯示各種歷史資訊

5.     提供各種診斷資訊

SOS有如下限制:

1.     不可以在mscorwks載入之前被使用

2.     無法察看區域性物件的名稱

3.     以及很多其他的功能

SOS使用方法如下:

1.     使用.loadby / .load 命令載入。但是Rotor下面SOS由於對VC Runtime的依賴會發生Side By Side相關的問題導致無法找到MSVCR80.dll或者MSVCR80D.dll。解決方法在文章最後會提到

2.     !help命令可以提供所有命令的列表

3.     !help faq顯示SOS使用的方法

4.     !help <command>顯示具體Command的幫助

WinDbg+SOS的優勢在於:

1.     WinDbg的原生程式碼(非託管程式碼)除錯功能非常強,甚至比VS還強大,只是需要一些時間習慣

2.     SOS是除錯託管程式碼必須的,提供一些基本功能如設定對託管程式碼的斷點,顯示變數和Stack,等等

3.     SOS可以用來查詢對CLR內部的狀態,如GCHeapMethodTableMethodDescModuleAssembly等等各種資訊,對於除錯和診斷非常有用

如何解決Rotor中的SOS.dll命令無法載入的問題

由於SOS是在WinDbg程式中載入,而WinDbgManifest中並沒有VC Runtime除錯版的DLLSide By Side資訊,所以載入會失敗。解決方法有下面的兩種:

1.     Copy相應的的DLLWinDbg目錄或者到Rotor的目錄下面。我的VS2005編譯出來的SOS.dll依賴於8.0.50727.762版本的MSVCR80D.dll,因此可以在C:/windows/WinSxS/x86_microsoft.vc80.debugcrt_1fc8b3b9a1e18e3b_8.0 .50727.762_none_24c8a196583ff03b目錄下面找到。

2.     也可以從WinDbgManifest入手解決這個問題。既然WinDbgManifest沒有MSVCR80D的相關資訊,我們可以手動替WinDbg加上一個,步驟如下:

 

首先使用mt.exeWinDbgManifest提取出來,這個Manifest是在WinDbg.exe中作為資源存在的,其ID1。先開啟VS 2005的命令提示符(否則找不到mt),進入WinDbg所在目錄,輸入:

 

mt.exe -inputresource:windbg.exe;#1 -out:extracted.manifest

 

獲得了WinDbgManifest之後,開啟extracted.manifest可以看到下面內容:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">

<assemblyIdentity name="Microsoft.Windows.SdkTools.windbg" processorArchitecture="x86" version="6.7.0.0" type="win32"></assemblyIdentity>

<description>Windows GUI symbolic debugger</description>

<dependency>

    <dependentAssembly>

        <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"></assemblyIdentity>

    </dependentAssembly>

</dependency>

</assembly>

 

可以看到裡面並沒有對於MSVCR80D.dll的資訊,我們可以自己動手加上去:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">

<assemblyIdentity name="Microsoft.Windows.SdkTools.windbg" processorArchitecture="x86" version="6.7.0.0" type="win32"></assemblyIdentity>

<description>Windows GUI symbolic debugger</description>

<dependency>

    <dependentAssembly>

           <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"></assemblyIdentity>

    </dependentAssembly>

</dependency>

<dependency>

    <dependentAssembly>

           <assemblyIdentity type="win32" name="Microsoft.VC80.DebugCRT" version="8.0.50727.762" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>

    </dependentAssembly>

</dependency>

 

</assembly>

 

其中深紅色的為新增的內容,意思是該EXE依賴於Microsoft.VC80.DebugCRT這個DLLversion 8.0.50727.762architecturex86public key token1fc8b3b9a1e8e3bWindows會自動根據這個資訊找到對應的MSVCR80D.dll

下面我們可以動手修改WinDbg了,建議動手之前WinDbg之前請先備份WinDbg。準備好了之後鍵入:

 

mt.exe -manifest extracted.manifest -outputresource:windbg.exe;#1

這個命令會將Manifest作為資源重新新增到WinDbg.exe,覆蓋原來的Manifest,至此WinDbg已經被我們修改了。驗證一下,用WinDbg開啟CLIX.exe進行除錯,然後鍵入如下命令:

 

.loadby sos sscoree

沒有看到任何輸出,說明載入成功。再輸入

 

!help

SOS會輸出所有命令的列表。

後記

經過修改之後,SOS可以被正常載入了。很遺憾的是,在我後續的對Rotor的實驗中,發現Rotor的程式碼中對SOSBPMD命令和CLR notification支援存在問題,而BPMD命令正好是SOS的最重要功能之一:設定斷點。在我的下一篇文章中,會講到問題的具體情況以及如何修改Rotor程式碼來支援CLR NotificationSOSBPMD命令。在修改了Rotor程式碼使SOS正常工作之後,再下一篇文章則會以跟蹤一個簡單的HelloWorld的託管程式碼程式來揭示Rotor的基本執行機制和原理,敬請關注。

 

相關文章