如何為WinDbg編寫ClrMD外掛
在之前的 如何為WinDbg編寫ClrMD擴充套件的CriteoLabs 文章中,我們介紹了在Windows上面寫外掛。當遷移到Linux時,由於LLDB是Linux上.NET網路核心的通用偵錯程式,因此我決定編寫一個相容層,以便能夠在新環境中載入擴充套件。
什麼是ClrMD外掛
如何建立一個適用於WinDbg和LLDB的擴充套件?第一步仍然是建立一個新的類庫專案。無論是.NET Framework和.NET Standard都可以,但有些事情需要注意:
1.如果選擇.NET Framework,請確保不要使用與.NET Core不相容的任何功能(例如AppDomain),否則您將無法在LLDB中執行擴充套件; 2.如果選擇.NET Standard,請記住釋出專案以將所有依賴項包含在一個資料夾中,因為在編譯時預設情況下不會這樣做
建立專案後,新增 對ClrMDExports nuget 包的引用。它會自動將ClrMD和 UnmanagedExports.Repack 作為依賴項。UnmanagedExports.Repack是UnmanagedExports的一個分支,它增加了與.NET Framework 4.7+和.NET Standard的相容性,並支援PackageReference。
請注意,新的Init.cs檔案將新增到您的專案中(如果您使用包引用,則不應該看到它)。 不要對此檔案進行任何更改。 每次更新nuget包時都會被覆蓋。
Init檔案負責匯出WinDbg所需的DebugExtensionInitialize方法,並設定所有內容,只要依賴項與擴充套件位於相同的資料夾中,就可以正確載入它們。
下一步是新增自定義命令。您需要為每個命令建立一個靜態方法,並使用以下簽名:
public static void HelloWorld(IntPtr client, [MarshalAs(UnmanagedType.LPStr)] string args) { }
然後使用UnmanagedExports附帶的DllExport屬性裝飾它。您可以使用 ExportName
屬性的引數來定義WinDbg / LLDB可見的命令名稱。請記住,名稱區分大小寫!
[DllExport("helloworld")] public static void HelloWorld(IntPtr client, [MarshalAs(UnmanagedType.LPStr)] string args) { }
在該方法中,您應該只調用 DebuggingContext.Execute
由ClrMDExports提供的方法。它接受 client
和 args
作為引數的值,以及帶有 (ClrRuntime runtime, string args)
簽名的另一個靜態方法的委託。在靜態回撥方法中,實現命令。
[DllExport("helloworld")] public static void HelloWorld(IntPtr client, [MarshalAs(UnmanagedType.LPStr)] string args) { DebuggingContext.Execute(client, args, HelloWorld); } private static void HelloWorld(ClrRuntime runtime, string args) { Console.WriteLine("The first 10 types on the heap are: "); foreach (var type in runtime.Heap.EnumerateTypes().Take(10)) { Console.WriteLine(type); } }
為方便起見,控制檯輸出會自動重定向到偵錯程式。
您可以直接在WinDbg中載入和使用您的擴充套件:
在Linux上執行LLDB
由於擴充套件是為WinDbg API編寫的,因此無法直接載入到LLDB中。相反,我編寫了一個進行翻譯 的外掛 。
首先, 下載最新版本 的LLDB-LoadManaged元外掛並將其解壓縮到一個資料夾中。
然後啟動LLDB並附加到目標:
./lldb -c dump.dmp
接下來,載入元外掛:
plugin load ./loadmanaged/libloadmanaged.so
確保Mono.Cecil.dll和PluginInterop.dll檔案與libloadmanaged.so位於同一資料夾中。
載入後,LLDB-LoadManaged將嘗試通過瀏覽除錯目標中載入的模組來定位CoreCLR。如果失敗(例如,因為您在與目標不同的機器上執行lldb),您可以通過呼叫 SetClrPath
以下命令手動設定路徑:
SetClrPath /usr/local/share/dotnet/shared/Microsoft.NETCore.App/2.2.0/
最後,使用以下 LoadManaged
命令載入WinDbg擴充套件:
LoadManaged /home/k.gosse/TestExtension.dll
(該 LoadManaged
命令尚不支援相對路徑)
就是這樣!現在,您可以像在WinDbg中一樣呼叫擴充套件程式。
WinDbg的ClrMD擴充套件在Linux上的LLDB中完美執行
注意:libloadmanaged.so和libsosplugin.so都會根據自己的需要託管CLR。但是, .NET Core CLR不支援並排方案 。這意味著我們不能同時使用LoadManaged和SOS外掛。這可以講是一個限制,它不太可能在.NET Core端修復。我們這裡有一種解決方法,可以通過LoadManaged載入並替換libsosplugin.so的SOS託管版本。
接下來的路
這雖然是LLDB-LoadManaged的早期版本。在接下來的幾周裡,我想改進錯誤處理並使CLR路徑檢測變得更加智慧。還有一點,其實我們已經在Criteo上使用它,這麼來說它對於常見的用例應該足夠穩定。使用LLDB與獨立ClrMD應用程式的主要附加價值是可以附加到實時程序(Linux上的ClrMD尚不支援)。我也想知道基於ClrMD( https://github.com/dotnet/diagnostics/tree/master/src/Tools )的跨平臺REPL環境有一些知識,所以很高興看到這兩項工作如何匯合。
*參考來源 medium ,由周大濤編譯,轉載請註明來自FreeBuf.COM