用 WinDbg + SOS 解讀當機傾印檔 ── 收集之後的實務分析入門
· 小村 豪 · WinDbg, SOS, 當機傾印, .NET, CSharp, 偵錯, PDB, 缺陷調查, 技術諮詢
之前的文章「Windows 當機傾印收集入門」整理了用 WER LocalDumps、ProcDump、MiniDumpWriteDump 把傾印檔「收集起來」的做法。不過,傾印檔光是收集到手邊並不會告訴你任何事。要實際動手解讀「是哪個執行緒」「為什麼」當機,或者「是什麼」一直握著記憶體不放,才能成為調查的材料。
這篇文章接續收集篇,把焦點放在如何用 WinDbg 與 SOS 擴充功能實際讀取已收集的傾印檔。內容涵蓋安裝與符號設定、.NET 應用程式中不可或缺的 SOS 擴充功能載入、!clrstack、!dumpheap -stat 等代表性指令「該看什麼、怎麼判斷」、原生當機的 !analyze -v,以及不使用 WinDbg 的 dotnet-dump analyze 該如何區分使用。
1. 先講結論
- 傾印分析的主角是 WinDbg(目前版本,舊稱 WinDbg Preview)。可用
winget install Microsoft.WinDbg或透過 Microsoft Store 取得,在 Windows 10 週年更新(1607)以後 / Windows 11 的 x64、ARM64 上皆可運作。1 - 即使符號(PDB)沒有正確載入,
!clrstack、!dumpheap -stat、!gcroot等指令仍會直接從 CLR 的中繼資料與堆積資料運作。失去的只是受管理程式碼的原始檔名、行號,以及原生堆疊框架的符號名稱。話雖如此,若想追到原始碼行數,那就是另一回事了,所以慣例是在_NT_SYMBOL_PATH同時放入 Microsoft 的公開符號伺服器與自家 PDB 的位置。2 - 在 .NET(Framework / Core / 5+)應用程式的傾印檔中,要載入 SOS 擴充功能後才能看到受管理端的資訊。光靠原生的
k(顯示堆疊)指令是追不到 C# 程式碼的。3 - 代表性的調查型態有 3 種。因例外而當機時走
!clrstack→!pe、記憶體持續增加時走!dumpheap -stat→!gcroot、原生當機時從!analyze -v入手。 - 不使用 WinDbg 的選項是
dotnet-dump analyze。多數 SOS 指令都能直接沿用,但無法處理原生的堆疊框架。若手邊要調查的是純受管理、不涉及原生 DLL 或 COM 的情境,這個工具導入起來更輕巧。4 - 這裡寫的是「怎麼讀」而不是「怎麼取」。傾印檔的取得方式(WER / ProcDump /
MiniDumpWriteDump)請參考收集篇,而當機時與日誌互相對照的設計,請參考「Windows 應用程式當機時保留日誌與傾印檔的設計」。
2. 安裝 WinDbg 並設定符號
2.1 安裝
目前版本的 WinDbg 可透過以下任一方式安裝。1
winget install Microsoft.WinDbg
透過 Microsoft Store 安裝也會得到相同的引擎,指令、擴充功能、工作流程都是共通的。安裝後會自動更新(透過 Store 或直接安裝時是在背景進行,透過 winget 安裝時可用 winget upgrade Microsoft.WinDbg),因此不太需要煩惱版本差異造成的行為不同。1
2.2 開啟傾印檔
windbg -z C:\CrashDumps\MyApp\MyApp_20260702_101500.dmp
-z 是指定傾印檔並啟動的選項。透過 GUI 選單「File > Open Dump File」也能達到同樣效果。
2.3 設定符號路徑
Windows 偵錯器尋找符號檔(PDB)的位置,是透過 _NT_SYMBOL_PATH 環境變數,或在偵錯階段內用 .sympath 指令指定。2 實務上的基本形式是同時放入 Microsoft 的公開符號伺服器與自家 PDB 的位置。
.symfix C:\Symbols\Microsoft
.sympath+ C:\Symbols\MyApp
.reload
.symfix是設定通往 Microsoft 公開符號伺服器(https://msdl.microsoft.com/download/symbols)路徑的捷徑指令,並附帶指定的本機快取。OS 標準 DLL 的符號會從這裡自動下載。5.sympath+會把自家 PDB 的存放位置附加到現有路徑之後。自家程式碼的 PDB 必須自行準備,不會出現在 Microsoft 的符號伺服器上。- 用
.reload重新載入,確認模組清單的符號狀態。
若要用環境變數永久設定,格式如下。這種方式適合 CI 或建置伺服器上的自動分析。
set _NT_SYMBOL_PATH=srv*C:\Symbols\Microsoft*https://msdl.microsoft.com/download/symbols;C:\Symbols\MyApp
符號是否正確讀取,可用 lm(loaded modules)指令列出模組清單,確認目標模組是否顯示為 pdb symbols。若仍顯示 deferred,表示符號還沒被解析。
3. 載入 SOS 擴充功能
.NET 應用程式的傾印檔,光靠原生的 WinDbg 指令是看不到「受管理堆積的內容」「C# 的堆疊框架」「例外物件的內容」的。填補這個缺口的正是 SOS(Son of Strike)擴充功能。堆積的調查、堆積損壞的偵測、執行環境內部資料型別的顯示,一直到執行中受管理程式碼狀態的掌握,都是透過 SOS 指令完成的。3
3.1 依執行環境而異
目標應用程式是 .NET Framework,還是 .NET (Core) / .NET 5+,會影響該載入的執行環境本體與 SOS 的來源。
| 目標 | 執行環境本體 | 載入指令 |
|---|---|---|
| .NET Framework | clr.dll |
.loadby sos clr |
| .NET Core / .NET 5+ | coreclr.dll |
.loadby sos coreclr |
.loadby 是從指定模組(clr 或 coreclr)所在的目錄,尋找同一位置的擴充功能 DLL(sos.dll)並載入的指令。優點是不需要打完整路徑,就能確實取得對應傾印檔採集環境的 SOS 版本。6
10.0.18317.1001 以後版本的 WinDbg、cdb,只要偵測到目標行程載入了 coreclr.dll(或 Linux/macOS 上的 libcoreclr.so),就會自動從 Microsoft Extension Gallery 載入對應 .NET 的擴充功能。6 上述的 .loadby 手動指令,適用於自動載入沒有生效,或使用較舊版本偵錯器的情況。
3.2 找不到 SOS 時
在自動載入不生效的環境中,可用 dotnet-sos 工具在本機安裝。
dotnet tool install --global dotnet-sos
dotnet-sos install
安裝完成後,也能在 WinDbg 中如下手動載入(較舊的偵錯器可能需要這個步驟)。7
.load %USERPROFILE%\.dotnet\sos\sos.dll
3.3 確認是否載入成功
!sos.help
或者,目標若是 Core 系列可嘗試 !Threads,Framework 系列可嘗試 !sosstatus,只要沒有出錯並回傳資訊,就代表載入成功。若指令在此以類似 Unable to find module 的錯誤失敗,幾乎都是符號路徑或執行環境不一致所致(例如傾印檔採集環境與手邊執行環境的位元數、版本不同)。在進入下一章的指令之前,卡在這一步的情況在實務上並不少見。
4. 讀取例外與堆疊 ── !clrstack 與 !pe
這是因未處理例外而當機的傾印檔的第一步。
!threads
先用 !Threads(在 lldb 環境中別名為 clrthreads)確認受管理執行緒清單,以及各執行緒的 Exception 欄位。8 若有執行緒持有例外,就切換到那個執行緒。
~5s
!clrstack
!CLRStack 只會顯示受管理程式碼的堆疊追蹤。9 若想連引數與變數都看到,可加上 -a(相當於 -l 與 -p 的組合捷徑)。
!clrstack -a
- 若看到自家程式碼的方法排列出來,就能直接讀出「在哪裡」「經過什麼呼叫路徑」當機。能顯示到原始檔名、行號,是因為符號正確讀取了(第 2 章)。
CLRStack是直接從 CLR 的中繼資料列舉受管理框架,因此符號有無並不影響框架是否顯示。符號不足時失去的只是原始檔名、行號,框架本身不會因此被省略。9 - 若一個自家程式碼的框架都看不到,先別懷疑符號不足,該懷疑的是:選到的執行緒其實是沒有例外的另一個執行緒(選錯執行緒)、只有原生端當機而受管理框架本來就不存在,或是傾印檔的種類(如 Mini)沒有包含足夠的堆疊資訊。
接下來看例外物件本身。
!pe
!PrintException(簡稱 !pe)在未指定位址時,會顯示目前執行緒最後一次擲出的例外。可取得型別名稱、訊息、內部例外(用 -nested 顯示),一直到堆疊追蹤字串。10 像 System.NullReferenceException 這種光看型別名稱看不出任何訊息的例外,就需要與 !clrstack -a 看到的區域變數值互相對照。
5. 追蹤堆積與洩漏 ── !dumpheap -stat 與 !gcroot
在「記憶體慢慢增加,數小時到數天後才當機」這類調查中是核心指令。要先分辨是等待 GC 還是真正的洩漏,前置的判斷方式已在「用 .NET 分辨 GC 等待與記憶體洩漏」中詳細寫過。這篇文章接續其後,屬於讀一份傾印檔並深入到「是什麼握著」的部分。
!dumpheap -stat
-stat 選項只顯示受管理堆積的統計摘要。會依型別的件數與合計大小,大致由多到少排列,因此可先鎖定「數量壓倒性多的型別」。11 實務上常見的有以下 2 種型態:
- 業務類別本身在增加(例如
MyApp.Models.Customer有數十萬件)── 表示某處持續存在強參考握著它 - 只有
System.String或陣列異常地多── 多半是排名靠前的業務類別內部資料所致,先懷疑業務類別,往往比逐一檢視個別執行個體更快
鎖定目標後,取得個別執行個體的位址。
!dumpheap -type MyApp.Models.Customer
然後調查該物件為什麼沒被 GC 回收,一直殘留著。
!gcroot 000001a2b3c4d5e0
!GCRoot 會搜尋整個受管理堆積與控制代碼表,找出所有能到達指定物件的根(堆疊上的變數、靜態欄位、GC 控制代碼等)。12 若輸出中看到快取用的靜態欄位或事件處理器的訂閱,那裡就是解除訂閱漏掉的候選犯人。以下這種「放進快取後就不釋放」的程式碼是典型範例:
public static class CustomerCache
{
// 沒有解除的途徑,只會不斷增加的靜態字典
private static readonly Dictionary<int, Customer> _cache = new();
public static void Add(Customer c) => _cache[c.Id] = c;
}
若 !gcroot 的輸出中出現像 CustomerCache 這樣的靜態容器,就是考慮加上有效期限、上限件數,或改用 WeakReference 的材料。11
6. 原生當機的自動分析 ── !analyze -v
牽涉 C++ DLL、COM、廠商 SDK 的原生當機(如存取違規),一律先從這個指令入手。
!analyze -v
!analyze 是進行當機・例外自動分析的擴充指令,加上 -v 會顯示詳細內容。13 輸出中特別需要留意的有以下 3 項:
EXCEPTION_CODE/BUGCHECK_STR: 是哪一種異常(存取違規、堆疊溢位等)FAULTING_IP/FOLLOWUP_IP: 實際當機的指令位址,以及對應的模組・函式名稱MODULE_NAME/IMAGE_NAME: 當機處是自家模組,還是第三方 DLL
若當機發生在自家模組之外的廠商 DLL 中,要繼續往下追就需要廠商的 PDB(通常拿不到)。實務上比較現實的做法,是回溯到「呼叫端(自家程式碼最後傳遞的引數)」,懷疑傳入的值是否有異常。請用 !analyze -v 的 STACK_TEXT 欄位確認自家程式碼呼叫了什麼之後才當機。
!analyze 不只能用在例外導致的傾印檔上。懷疑是掛死(hang)時,選定目標執行緒後執行以下指令,會分析執行緒之間的封鎖關係。
!analyze -hang
7. 不使用 WinDbg 的選項 ── dotnet-dump analyze
若只想調查 .NET Core / .NET 5+ 的受管理程式碼(不涉及原生 DLL 或 COM),比 WinDbg 更輕量的 dotnet-dump 也是一個選項。
dotnet tool install --global dotnet-dump
dotnet-dump analyze C:\CrashDumps\MyApp\MyApp_20260702_101500.dmp
analyze 子指令會開啟一個預先安裝 SOS 的互動階段,clrstack、dumpheap、gcroot 等本文介紹過的多數指令,都能直接使用而不需要 ! 前綴。4
使用區分的大致標準如下:
| 觀點 | WinDbg + SOS | dotnet-dump analyze |
|---|---|---|
| 原生堆疊框架 | 可見 | 不可見(僅受管理端)4 |
!analyze -v 的原生自動分析 |
可用 | 不可用 |
| Linux 的傾印檔 | 可在 Windows 上用 WinDbg 分析(x64 傾印檔用 x64 版、Arm64 傾印檔用 x64 版、x86 傾印檔用 x86 版)14 | 支援(使用相同平台位元數的工具)14 |
| macOS 的傾印檔 | 不支援(WinDbg 的 Linux 傾印支援不含 macOS) | 支援(.NET 5 以後)4 |
| 導入的輕便度 | 安裝程式或 winget | dotnet global tool 一道指令 |
| 納入跨平台 CI | 較費工 | 較容易 |
「可能牽涉 COM、P/Invoke、原生 DLL」就用 WinDbg,「純受管理程式碼的記憶體洩漏調查,且想在 CI 或多平台上執行」就用 dotnet-dump analyze,是實務上的分界線。兩者共用 SOS 的指令體系,因此在一邊學到的指令,在另一邊幾乎都能直接沿用。若要處理在 macOS 上採集的傾印檔,WinDbg 不在選項之內,這一點請留意,只能選 dotnet-dump(或 LLDB)。
8. 符號讀不到就什麼都做不了
到目前為止的部分步驟,即使符號(PDB)沒有正確載入,也還算能運作。如第 4 章所寫,!clrstack、!dumpheap -stat、!gcroot 是直接讀取 CLR 的中繼資料與堆積資料,因此即使沒有 PDB,框架與型別資訊本身仍會顯示。PDB 缺失時失去的是受管理程式碼的原始檔名、行號,以及原生框架、原生模組的符號名稱(只顯示位址而非函式名稱)。若手邊只有傾印檔與執行檔,在「先想掌握發生了什麼事」的場景下,不必卡在尋找 PDB 上,直接從 !threads → !clrstack 著手也無妨。話雖如此,若想追到原始碼行數以確定問題所在,那就是另一回事了。第 2 章在 .sympath+ 加上了自家 PDB 的路徑,但實務上光是「不知道對應已發佈 EXE/DLL 的 PDB 在哪裡」,就足以讓調查卡在無法確定原始碼行數的階段,這種情況比收集篇中寫到的還要頻繁。
PDB 本身持有與不持有什麼、Portable PDB,以及 Source Link(把原始碼控管的中繼資料嵌入組件內,讓偵錯器能直接取得對應建置時間點提交的原始碼的機制),都已整理在「PDB 是什麼」這篇文章中。15 若要把傾印分析納入持續性的運維流程,按建置版本保存 PDB、並啟用 Source Link,這件事的重要性和收集設定本身不相上下。這一步偷懶的話,!clrstack 的輸出就完全不會出現原始碼行數,只能靠位址與型別名稱摸索。
9. 實例 ── 控制代碼洩漏調查中的傾印分析
之前寫過的「工業相機長期運轉當機調查 - 控制代碼洩漏篇」,是長時間運轉後突然當機的工業相機控制應用程式調查案例,主犯不是記憶體洩漏而是控制代碼洩漏。這類調查中傾印檔能發揮作用,是在以下組合成立的時候:
- 先用
!dumpheap -stat確認受管理堆積端正常(各型別的件數、大小沒有持續增加) - 若行程的控制代碼數量仍持續增加,就能區分出洩漏的不是受管理物件,而是 OS 控制代碼(檔案、事件、相機 SDK 內部配置的控制代碼等)
- 若還殘留持有
SafeHandle的受管理包裝物件,就用!gcroot追蹤該包裝物件的 GC 根,找出「應該釋放卻仍有參考殘留」的位置
也就是說,!dumpheap -stat 是判斷「是不是受管理堆積在增加」的分岔點,一旦確定不是,調查的主軸就會轉移到像 Application Verifier 這類原生邊界異常偵測工具上。這套異常路徑測試基礎的建置方式,在「用 Application Verifier 建置 Windows 異常路徑測試基礎」中有介紹。傾印分析扮演的是「掌握現在發生的狀態」,Application Verifier 扮演的是「提前重現異常」,兩者並用是長時間運轉系統故障調查的定式。
10. 總結
當機傾印檔「收集」比較容易,「讀懂」則需要更多時間才能熟練,但型態並不算多。
- 安裝 WinDbg,並在符號路徑中同時放入 Microsoft 的公開符號伺服器與自家 PDB(第 2 章)
- .NET 應用程式要載入 SOS 擴充功能(
.loadby sos clr/.loadby sos coreclr,或自動載入。第 3 章) - 因例外而當機就走
!clrstack→!pe,記憶體增加就走!dumpheap -stat→!gcroot,原生當機就從!analyze -v入手(第 4~6 章) - 若用途純粹是受管理端的調查,也可以考慮輕量的
dotnet-dump analyze(第 7 章)
而支撐這一切的基礎,就是 PDB 與符號的管理。在建置傾印收集設定的同時,一併決定好按建置版本保存 PDB、啟用 Source Link,實際發生故障時的調查時間會有很大差異。若自家難以進行分析,或抽不出時間,只要把傾印檔與日誌一併寄來,我們也能協助分析。
相關文章
- Windows 當機傾印收集入門 - WER/ProcDump/WinDbg
- Windows 應用程式當機時保留日誌與傾印檔的設計
- 用 .NET 分辨 GC 等待與記憶體洩漏
- PDB(程式資料庫)是什麼
- 工業相機長期運轉當機調查 - 控制代碼洩漏篇
- 用 Application Verifier 建置 Windows 異常路徑測試基礎
相關諮詢領域
合同會社小村軟體提供結合當機傾印檔與日誌的缺陷原因調查、僅在長期運轉後才發生的故障排除,以及傾印檔・PDB 保存與分析體制本身的設計諮詢。
參考連結
-
Microsoft Learn, Install the Windows debugger. 說明透過 winget / Microsoft Store 安裝 WinDbg 的方式、支援的 OS(Windows 10 1607 以後・Windows 11)與架構(x64・ARM64),以及自動更新的行為。 ↩ ↩2 ↩3
-
Microsoft Learn, Symbol path for Windows debuggers. 說明透過
_NT_SYMBOL_PATH環境變數設定符號路徑的方式,以及.symfix指令可設定通往公開符號伺服器的預設路徑。 ↩ ↩2 -
Microsoft Learn, SOS debugging extension. 說明 SOS 擴充功能可用於受管理堆積資訊的收集、堆積損壞的偵測、執行環境內部資料型別的顯示,以及 WinDbg 上的語法為
![command]。 ↩ ↩2 -
Microsoft Learn, Dump collection and analysis utility (dotnet-dump). 說明
dotnet-dump analyze提供可直接使用 SOS 指令的互動階段,但因不是原生偵錯器而無法顯示原生堆疊框架,以及 macOS 支援始於 .NET 5。 ↩ ↩2 ↩3 ↩4 -
Microsoft Learn, Microsoft public symbol server. 說明
srv*DownstreamStore*https://msdl.microsoft.com/download/symbols格式的符號路徑語法,以及.symfix可附帶本機快取進行設定。 ↩ -
Microsoft Learn, Debugging Managed Code Using the Windows Debugger. 說明 .NET Framework 的執行環境為
clr.dll、.NET Core/.NET 5+ 的執行環境為coreclr.dll,.loadby可從模組附近載入擴充功能,以及 WinDbg 10.0.18317.1001 以後版本的自動載入機制。 ↩ ↩2 -
Microsoft Learn, SOS installer (dotnet-sos). 說明透過
dotnet-sos install在本機安裝 SOS 擴充功能,以及在較舊版本偵錯器中手動載入的指令。 ↩ -
Microsoft Learn, SOS debugging extension - Commands. 說明
Threads(在 lldb 環境中別名為clrthreads)指令會列出各執行緒的 ID、網域、最後擲出的例外等資訊。 ↩ -
Microsoft Learn, SOS debugging extension - Commands. 說明
CLRStack指令只顯示受管理程式碼的堆疊追蹤,-a選項可同時顯示區域變數與引數,以及符號(SYMOPT_LOAD_LINES)僅影響原始檔名、行號能否顯示,與框架本身是否顯示無關。 ↩ ↩2 -
Microsoft Learn, SOS debugging extension - Commands. 說明
PrintException(pe)指令在省略位址時,會顯示目前執行緒最後一次擲出的例外,並可用-nested顯示巢狀例外。 ↩ -
Microsoft Learn, Debug a memory leak in .NET 以及 Dump collection and analysis utility (dotnet-dump) - Analyze memory leaks and allocations. 說明
dumpheap -stat依型別顯示件數與合計大小的統計,以及以此為起點推進調查的方式。 ↩ ↩2 -
Microsoft Learn, SOS debugging extension - Commands. 說明
GCRoot指令會搜尋整個受管理堆積與控制代碼表,找出通往指定物件的參考(根)。 ↩ -
Microsoft Learn, Using the !analyze Extension 以及 !analyze (WinDbg). 說明
!analyze -v的當機・例外自動分析,輸出中FAULTING_IP、MODULE_NAME等欄位的意義,以及用於掛死調查的!analyze -hang。 ↩ -
Microsoft Learn, Debug Linux dumps. 說明可在 Windows 上用 WinDbg 或 dotnet-dump 分析 Linux 的傾印檔,且需要依採集環境(x64/Arm64/x86)的位元數使用對應版本的工具。 ↩ ↩2
-
Microsoft Learn, Source Link. 說明在建立 NuGet 套件時,把原始碼控管的中繼資料嵌入組件,讓偵錯器能直接存取建置時間點的原始碼的機制。 ↩
相關文章
共用相同標籤的最新文章。能以相近的主題延伸理解。
用 PerfView 與 dotnet-trace 找出「變慢」的原因 ── .NET 效能調查實務入門
當商用應用程式「變慢」「CPU 卡住不動」「偶爾當掉」時,該用哪個工具看什麼?本文整理 PerfView 與 dotnet-trace 的角色分工、CPU 取樣的讀法(inclusive/exclusive)、用 ThreadTime 調查阻塞時間,以及與 EventSou...
不只是 appsettings.json ── Windows 商用應用程式的組態管理實務(環境別設定・機密資訊・寫入位置)
本文從實務角度整理 Windows 商用應用程式的組態管理,涵蓋 appsettings.json 的分層、IConfiguration 與 IOptions/IOptionsSnapshot/IOptionsMonitor 的選用方式、環境別設定、可寫入設定的存放位置、機...
Windows 業務應用程式的列印與 PDF 輸出 ── System.Drawing.Printing / WPF / 報表函式庫的取捨
本文以要件別判斷表整理 PrintDocument 的 WinForms 列印、WPF 的 FlowDocument/FixedDocument 列印,以及 PDF 輸出的各種選項。並從實務角度說明分頁控制、DPI 落差等實作陷阱,以及從 Windows 服務列印、依賴預設...
PDB(程式資料庫)是什麼 ── 理解偵錯資訊、符號與 Source Link
整理 PDB(Program Database / 程式資料庫)是什麼、裡面有什麼、沒有什麼,並從實務角度說明 Debug / Release、Portable PDB、Source Link、符號伺服器、傾印分析之間的關係。
Windows 事件記錄・ETW 入門 ── 讓業務應用程式的日誌搭上 OS 標準機制
Windows 業務應用程式的日誌,是不是只靠檔案日誌解決?事件記錄與 ETW,是維運人員與 OS 標準工具能看到的另一個層級的記錄。本文說明三種手段的分工、.NET 的寫入方式、透過 EventSource 進行 ETW 儀器化、收集與調查的實務,以及來源未註冊、日誌肥大...
相關主題
與本文相近的主題頁面。以本文為起點,可進一步連到相關服務與其他文章。
Windows 技術主題
彙整 KomuraSoft LLC 關於 Windows 開發、故障調查與既有資產活用文章的主題中心。
與本主題相關的服務
本文連結到以下服務頁面,歡迎從最接近的入口查看。
Windows 應用程式開發
支援包含常駐處理、設備連動、運作日誌與可維護結構的 Windows 桌面應用程式。
技術諮詢 & 設計審查
協助釐清設計方向、架構邊界、生命週期責任,以及既有 Windows 資產的處理方式。
常見問題
整理諮詢這個主題時常見的問題。
- .NET 應用程式的當機傾印檔該從哪個指令開始讀?
- 調查的型態大致分成 3 種。若因未處理例外而當機,先用 !threads 找出持有例外的執行緒,再用 !clrstack 顯示受管理的堆疊,並以 !pe 確認例外物件的型別、訊息與內部例外。若是記憶體持續增加的調查,先用 !dumpheap -stat 找出數量偏多的型別,再用 !gcroot 找出握住該物件的根(靜態欄位或事件處理器的訂閱等)。若是原生當機,則從 !analyze -v 的自動分析入手。
- 在 WinDbg 中要如何載入 SOS 擴充功能?
- .NET Framework 用 .loadby sos clr,.NET Core/.NET 5+ 用 .loadby sos coreclr。10.0.18317.1001 以後版本的 WinDbg,只要偵測到目標行程載入了 coreclr.dll,就會自動載入 SOS。若自動載入不生效,可以用 dotnet-sos 工具在本機安裝後手動 .load。是否成功載入,可以用 !sos.help 或 !Threads 執行後不出錯並回傳資訊來確認。
- 沒有 PDB(符號)也能做傾印分析嗎?
- 在一定程度上可以。!clrstack・!dumpheap -stat・!gcroot 是直接讀取 CLR 的中繼資料與堆積資料,因此即使沒有 PDB,堆疊框架與型別資訊仍會顯示。失去的是受管理程式碼的原始檔名、行號,以及原生堆疊框架的符號名稱。若想追到原始碼行數以確定問題所在,就需要在符號路徑同時放入 Microsoft 的公開符號伺服器與自家 PDB 的位置。按建置版本保存 PDB,並啟用 Source Link,在運維上相當重要。
- dotnet-dump analyze 與 WinDbg 該如何區分使用?
- 若可能牽涉 COM、P/Invoke 或原生 DLL,用 WinDbg;若是純受管理程式碼的調查,dotnet-dump analyze 是基本方針。dotnet-dump 只需一道 dotnet global tool 指令即可安裝,clrstack、dumpheap 等多數 SOS 指令都能直接沿用,但無法處理原生堆疊框架,也無法使用 !analyze -v。在 macOS 上採集的傾印檔無法用 WinDbg 分析,因此只能選 dotnet-dump 或 LLDB。兩者共用 SOS 的指令體系,因此學會的指令幾乎可以互通使用。
作者檔案
本文作者的個人檔案頁面。
Go Komura
小村軟體有限公司 代表
以 Windows 軟體開發、技術諮詢與故障調查為中心,在難以重現的故障調查與既有資產仍在運作的專案上具有優勢。