.NET / Rotor原始碼研究3 – 除錯Rotor託管程式碼的利器:WinDbg和SOS
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了),是一個WinDbg的Extension,載入之後可以給WinDbg提供一些對託管程式碼除錯的命令。值得一提的是,WinDbg也有有限的對託管程式碼的支援,只不過功能非常有限,需要SOS的幫助。
SOS在.NET Framework中和Rotor中均有提供,所以本文中對於WinDbg和SOS的描述基本上適用於Rotor和發行版的.NET Framework(而且有可能也將適用於Silverlight)。比如.NET Framework 2.0中的SOS在C:/Windows/Microsoft .NET/v2.0.50727下面,而Rotor的SOS.dll則是在binaries.<flavor>.rotor目錄下面(flavor指Build的配置,如CPU,Debug/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內部的狀態,如GC,Heap,MethodTable,MethodDesc,Module,Assembly等等各種資訊,對於除錯和診斷非常有用
如何解決Rotor中的SOS.dll命令無法載入的問題
由於SOS是在WinDbg程式中載入,而WinDbg的Manifest中並沒有VC Runtime除錯版的DLL的Side By Side資訊,所以載入會失敗。解決方法有下面的兩種:
1. Copy相應的的DLL到WinDbg目錄或者到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. 也可以從WinDbg的Manifest入手解決這個問題。既然WinDbg的Manifest沒有MSVCR80D的相關資訊,我們可以手動替WinDbg加上一個,步驟如下:
首先使用mt.exe將WinDbg的Manifest提取出來,這個Manifest是在WinDbg.exe中作為資源存在的,其ID為1。先開啟VS 2005的命令提示符(否則找不到mt),進入WinDbg所在目錄,輸入:
mt.exe -inputresource:windbg.exe;#1 -out:extracted.manifest |
獲得了WinDbg的Manifest之後,開啟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這個DLL,version 8.0.50727.762,architecture為x86,public key token為1fc8b3b9a1e8e3b。Windows會自動根據這個資訊找到對應的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的程式碼中對SOS的BPMD命令和CLR notification支援存在問題,而BPMD命令正好是SOS的最重要功能之一:設定斷點。在我的下一篇文章中,會講到問題的具體情況以及如何修改Rotor程式碼來支援CLR Notification和SOS的BPMD命令。在修改了Rotor程式碼使SOS正常工作之後,再下一篇文章則會以跟蹤一個簡單的HelloWorld的託管程式碼程式來揭示Rotor的基本執行機制和原理,敬請關注。
相關文章
- .NET / Rotor原始碼分析5 - 開始使用WinDbg+SOS除錯,sscoree.dll,載入SOS並設定JIT斷點原始碼除錯斷點
- .NET / Rotor原始碼分析4 - 修改Rotor使其傳送CLR Notification原始碼
- windbg除錯系列教程:sos擴充套件的介紹和使用除錯套件
- .NET 5 程式高階除錯-WinDbg高階除錯
- 程式設計技巧 --- VS如何除錯.Net原始碼程式設計除錯原始碼
- .NET高階除錯系列-Windbg除錯入門篇高階除錯
- 編譯除錯Net6原始碼編譯除錯原始碼
- 託管程式碼和非託管程式碼
- 用WinDBG遠端除錯程式除錯
- windbg除錯命令除錯
- 使用 Chrome 除錯 Vue3 的 TypeScript 原始碼Chrome除錯VueTypeScript原始碼
- git提交程式碼託管平臺流程Git
- Git程式碼託管常用命令Git
- 除錯spark原始碼除錯Spark原始碼
- 重學c#系列——c# 託管和非託管資源與程式碼相關(四)C#
- 怎樣在Github上託管開原始碼庫Github原始碼
- Rotor實現中用到的Macro(摘自Shared Source CLI Essentials)Mac
- 【譯】在除錯時輕鬆導航程式碼委託除錯
- 託管程式碼(.net)比非託管程式碼(vc++)慢嗎?C++
- Javac 原始碼除錯教程Java原始碼除錯
- MariaDB原始碼除錯原始碼除錯
- 怎麼閱讀原始碼【除錯觀察原始碼】原始碼除錯
- 如何在Visual Studio中除錯.NET原始碼除錯原始碼
- [.net 物件導向程式設計進階] (8) 託管與非託管物件程式設計
- Git使用教程3:MyEclipse+Egit+GitHub實現程式碼託管 【上】EclipseGithub
- Git使用教程3:MyEclipse+Egit+GitHub實現程式碼託管 【下】EclipseGithub
- Docker 之 GitLab 區域網程式碼託管DockerGitlab
- 託管與非託管的混合程式設計程式設計
- GDB 除錯程式碼除錯
- GitHub:原始碼管理的利器Github原始碼
- Node.js 原始碼除錯Node.js原始碼除錯
- NodeJS的程式碼除錯和效能調優NodeJS除錯
- 使用git將自己的程式碼同時儲存在多個程式碼託管平臺Git
- Cordova應用的JavaScript程式碼和自定義外掛程式碼的除錯JavaScript除錯
- NgRx Store createSelector 的單步除錯和原始碼分析除錯原始碼
- 如何在沒有第三方.NET庫原始碼的情況,除錯第三庫程式碼?原始碼除錯
- Code:美團程式碼託管平臺的演進與實踐
- 基於Coding程式碼託管的Git基本使用指導Git