之前的文章「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...
Process Monitor(ProcMon)實戰指南 ── 在10分鐘內找出「設定未被讀取」「ACCESS DENIED」的原因
「明明已經修改了設定檔,卻沒有生效」「昨天還能正常運作,今天卻無法啟動」──在動手修改原始碼之前,Process Monitor(ProcMon)能從檔案、登錄檔存取的實際情況中找出原因。本文從不具調查現場的角度,解說 Filter 的實務用法、NAME NOT FOUND...
WinForms/WPF 應用程式的多語言化 ── resx、附屬組件與文化特性切換的實務
本文從實務角度整理 Windows 桌面應用程式的多語言化,包括 CurrentCulture 與 CurrentUICulture 的差異、resx 與附屬組件(Satellite Assembly)所構成的資源機制、WinForms 的 Localizable 屬性、W...
CSV不是「純文字」而已 ── C# 業務應用程式的 CSV 實務(字元編碼・Excel 相容性・注入攻擊防範)
整理業務應用程式在 CSV 讀寫時容易出事故的典型模式──用 Split(',') 自行解析、無 BOM 的 UTF-8 在 Excel 中亂碼、開頭零消失、CSV 注入攻擊──從 RFC 4180 的規則、.NET 中 Shift_JIS 的處理方式(CodePagesE...
不要用 using 包住 HttpClient ── C# 商用應用程式的 HTTP 通訊實務(生成模式・逾時・重試)
C# 的 HttpClient 若每次都用 using 生成,會導致 Socket 枯竭;改用 static 又無法跟上 DNS 變更──本文從 Windows 商用應用程式的實務角度,整理這兩大問題的成因,以及透過 PooledConnectionLifetime、IHt...
相關主題
與本文相近的主題頁面。以本文為起點,可進一步連到相關服務與其他文章。
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 軟體開發、技術諮詢與故障調查為中心,在難以重現的故障調查與既有資產仍在運作的專案上具有優勢。