.NET調試
診斷工具
Dump
dump在計算機科學中是一個廣泛運用的動詞、名詞。
作為動詞:一般指將數據導出、轉存成文件或靜態形式。比如可以理解成:把內存某一時刻的內容,dump(轉存,導出,保存)成文件。
作為名詞:一般特指上述過程中所得到的文件或者靜態形式。
1、為什么要dump(dump的目的)?
因為程序在計算機中運行時,在內存、CPU、I/O等設備上的數據都是動態的(或者說是易失的),也就是說數據使用完或者發生異常就會丟掉。如果我想得到某些時刻的數據(有可能是調試程序Bug或者收集某些信息),就要把他轉儲(dump)為靜態(如文件)的形式。否則,這些數據你永遠都拿不到。
2、dump轉儲的是什么內容(dump的對象)?
其實上邊已經提到了,就是將動態(易失)的數據,保存為靜態的數據(持久數據)。像程序這種本來就保存在存儲介質(如硬盤)中的數據,也就沒有必要dump。
WinDbg調式
1、理論學習
WinDbg:Windows 調試工具(WinDbg、KD、CDB、NTSD)
win10上安裝后目錄:C:\Program Files (x86)\Windows Kits\10\Debuggers\x64
Windbg是Microsoft公司免費調試器調試集合中的GUI的調試器,支持Source和Assembly兩種模式的調試。Windbg不僅可以調試應用程序,還可以進行Kernel Debug。結合Microsoft的Symbol Server,可以獲取系統符號文件,便于應用程序和內核的調試。Windbg支持的平臺包括X86、IA64、AMD64。雖然windbg也提供圖形界面操作,但它最強大的地方還是有著強大的調試命令,一般情況會結合GUI和命令行進行操作,常用的視圖有:局部變量、全局變量、調用棧、線程、命令、寄存器、白板等。其中“命令”視圖是默認打開的。
Windbg在用戶態和內核態下,都支持兩種調試模式,即“實時調試模式(Living)”和“事后調試模式(Postmortem)”。
- 所謂實時模式,是被調試的目標對象(Target)當前正在運行當中,調試器可以實時分析、修改被調試目標的狀態,如寄存器、內存、變量,調試exe可執行程序或雙機實時調試都屬于這種模式;
- 所謂事后模式,是被調試的目標對象(Target)已經結束了,現在只是事后對它保留的快照進行分析,這個快照稱為轉儲文件(Dump文件)。
Windbg另一個重大優點,還在于它支持源碼級的調試,就像VC自帶的調試器一樣。雖然提供了用戶界面,但Windbg歸根結底還是需要用戶一個個地輸入命令來指揮其行動。這就是他的Command窗口。每個調試命令都各有使用范圍,有些命令只能用于內核調試,有些命令只能用于用戶調試,有些命令只能用于活動調試。但用戶也不必記得這許多,一旦在某個環境下,使用了不被支持的命令,都會顯示“No export XXX found”的字樣。
其他參考:
WinDbg的安裝、配置和功能 (系列博文)使用WinDbg調試入門(用戶模式)
Windbg命令相關
WinDbg 命令三部曲:(二)WinDbg SOS 擴展命令手冊
!DumpHeap 將遍歷 GC 堆對對象進行分析。通過指定不同的選項,可以查看特定的類型、數組和鎖。
!GCRoot [-nostacks] <Object address> 查詢一個對象的所有引用根。
!syncblk 看看有沒有 lock 的情況
!DumpStack Objects 顯示當前調用棧上的所有托管對象的信息,可配合 k 或 CLRStack 命令使用
!clrstack 看調用棧
!eeheap -gc 命令,看下托管堆大小【即遍歷進程內存中的 CLR 數據結構】
!eeheap -loader 命令,看下 loader 堆大小(非托管)
WinDbg 命令三部曲:(三)WinDbg SOSEX 擴展命令手冊
用 sosex 擴展的 !dlk 命令可以自動檢索是否有死鎖
2、概念原理之:什么是SOS、mscordacwks
mscorwks:通用語言運行時 (CLR) 是執行托管代碼的 Microsoft .NET 框架的核心引擎。mscorwks.dll是CLR 2.0實現的主要文件。此引擎在本機代碼中實現。
SOS:SOS.DLL可以提供關于CLR的信息,幫助我們在vs和windbg調試托管程序。例如,可以顯示有關托管堆的信息、查找堆損壞情況、顯示運行時使用的內部數據類型以及查看有關運行時內運行的所有托管代碼的信息。
mscorwks使用本機代碼實現了CLR,SOS可以提供托管的CLR信息,而mscordacwks即為連接本機代碼和托管代碼之間的橋。SOS無需了解CLR底層細節。
官網:SOS.dll (SOS debugging extension)
3、Windbg話題
幾個分析思路:
內存泄漏:首先就要排查到底是 托管堆 還是 非托管堆 的問題 ,參考:記一次 .NET 某HIS系統后端服務 內存泄漏分析
實踐
如何在 .NET 程序萬種死法中有效的生成 Dump
用Windbg打開生成的Dump文件【用vs也可以打開dmp文件進行分析】
執行命令如下:
!address -summary 查看當前 process 的內存占用量
!dumpheap -stat -min 1024 尋找大對象(單位Byte),在托管堆中
!dumpheap -type System.String -min 10240 用 -type 屬性篩選出 >10k 的字符串。
!gcroot 4a855060 【參數是 MT對應的任意一個address】
以下是一些說明:
ProcDump生成Dump文件
procdump ConsoleApp2 -m 1024 -ma E:\net5\ConsoleApp1\ConsoleApp2\bin\Debug
如果你的機器有多個 ConsoleApp2 ,可以將其替換成 pid(進程id)
報錯error opening

是需要使用管理員命令提示符才能成功抓取dump
應用場景:主要用于高CPU占用率的性能分析優化,程序停止響應的調試,First chance異常捕獲等,還可以監視內存使用、結合系統性能計數器使用。
在命令提示符下procdump -?可以查看全部參數的說明,這里列舉幾個常用的:
-c/-cl: 監視CPU占用率閥值,-c為當CPU占用率高于該值時創建dump,-cl則在低于該值時創建dump。
-u: 監視單核心的CPU占用率,與-c一起使用
-s: 時間,以秒為單位,結合-c使用實現當連續N秒CPU占用高于某值時保存dump。
-n: 設置數量,保存多個dump后才退出該程序。
-h: 當進程中存在掛起窗口(不響應窗口消息)時創建dump。
!dumpheap -stat
報錯1:no export dumpheap found
則執行.load sos 繼續報錯:系統找不到指定的文件
參考:加載擴展DLL:load 需要跟完整路徑:0:000> .load C:\Windows\Microsoft.NET\Framework64\v4.0.30319\SOS.dll
【注意:32的windbg則加載 .load C:\Windows\Microsoft.NET\Framework\v4.0.30319\SOS.dll ,否則報錯:"%1不是有效的Win32應用程序"】
繼續執行 !dumpheap -stat
報錯2:Failed to find runtime DLL (clr.dll),0x80004005

再加載一個其他版本的sos:C:\Windows\Microsoft.NET\Framework64\v2.0.50727\SOS.dll
繼續報錯:Failed to find runtime DLL (mscorwks.dll),0x80004005
改為加載.netcore 2.1 的sos 【因為.netcore3.1下沒有sos,這就為后續調試.netcore3.1的程序時埋了個坑】:.load C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.1.28\sos
!gcroot -all報錯:The version of SOS does not match the version of CLR you are debugging

步驟:!dumpheap -type System.String -min 10240
正確輸出了,單擊最下面一個。結果顯示 crl版本 與SOS版本不匹配。。所以要下載一個netcore版本下面的SOS。

可是.nercore3.1里面沒有sos.dll,只有一個rm

打開可以看到,進到 Installing SOS on Windows

然后執行 .load C:\Users\huy\.dotnet\sos\sos 就好了。
注意:2和3 綜合起來,下次分析dump時,分別執行:
.loadby sos crl
.load C:\Users\huy\.dotnet\sos\sos
!gcroot 返回 Found 0 unique roots
參考:WinDbg not telling me where my string is rooted
這是因為,WinDbg告訴您的是正確的-這些沒有根,它們是垃圾,但是因為它們在LOH上,它們可能不會很快被清除(如果有的話)。您肯定需要重新考慮如何處理XML,將數據流輸入/輸出,而不是預先在內存中加載/創建數據。
修改代碼為:
public static void TestMemory() { List<string> list = new List<string>(); for (int i = 0; i < int.MaxValue; i++) { string temp = string.Join(",", Enumerable.Range(0, 10000)); list.Add(temp); if (i % 30 == 0) SY.Filer.FileHelper.AppendAllText(temp, "temp.txt"); } Console.ReadLine(); }
重新生成dump去分析

其他參考
【VS】使用vs2017自帶的診斷工具(Diagnostic Tools)診斷程序的內存問題

浙公網安備 33010602011771號