WPR/WPA 實務 ── 從系統整體調查「整台 PC 變慢」
· Go Komura · Windows, 效能, WPR, WPA, ETW, 效能調查, 故障調查, Windows 開發
「他們說裝了新應用之後整台 PC 就變慢。可是看工作管理員,CPU 和記憶體都還有餘裕。」「有一台 PC 開機要 3 分鐘。完全不知道哪裡不對。」── 效能諮詢真的常常是這種形狀。共通點是:只盯著特定處理程序得不到答案。
處理程序層級的工具已經齊備。檔案與登錄存取可用 Process Monitor 看到,.NET 應用的 CPU 與 GC 可用 PerfView 追。但「整台 PC 變慢」或「CPU 閒著卻還是慢」這種症狀,一開始連哪個處理程序是元兇都不知道。應用 A 變慢,可能是防毒掃描、可能是另一個服務猛寫磁碟,也可能是跨好幾個處理程序的鎖鏈。你需要的是不是處理程序內部、而是把整個 OS 記在同一條時間軸上的資料。
擷取並讀取那份資料的工具,就是 Windows Performance Recorder(WPR)與 Windows Performance Analyzer(WPA)。WPR 以 ETW(Event Tracing for Windows)為基礎記錄整個 OS 的活動,WPA 用圖表與表格分析那份紀錄。誰在哪個堆疊用了 CPU、執行緒在等誰、哪個處理程序對哪個檔案發出磁碟 I/O──比工作管理員再往下一兩層的事實,都帶著時間戳留下來。
本文以中小企業的 IT 人員與 Windows 應用開發者為對象,依 2026 年 8 月的一手資料,整理用 WPR 擷取的實務,以及怎麼讀 WPA──尤其是調查「CPU 很高」與「CPU 很低卻還是慢」的差異。
1. 先講結論
- 「整台 PC 變慢」調查的首選是擷取並讀取整個 OS 的 ETW 追蹤的 WPR/WPA。處理程序層級工具(工作管理員、Procmon、PerfView)釘不住的問題,只要把每個處理程序與核心放在同一條時間軸上就能追。12
- 擷取工具 wpr.exe 從 Windows 8.1 起就內建。不必另外安裝。GUI 版(WPRUI)與分析工具 WPA 包含在 Windows ADK 裡。12
- 基本步驟就三行。以系統管理員執行
wpr -start GeneralProfile -filemode→ 重現問題 →wpr -stop C:\temp\trace.etl。只記住這個就能開始擷取。3 - 現場基準是拆成「在客戶現場只用 wpr.exe 擷取;閱讀在自己機器上用 WPA」。即使不能安裝軟體的伺服器也能擷取。想法和封包擷取的「用標準工具擷取、用 Wireshark 讀」一樣。1
- 讀 WPA 從分類「CPU、等待、還是 I/O」開始。CPU 在燒就看 CPU Usage (Sampled);CPU 閒著卻還是慢就做 CPU Usage (Precise) 的等待分析;懷疑磁碟就看 Disk Usage──路徑一開始就分叉。45
- CPU Usage (Sampled) 以大約每 1 毫秒的取樣,顯示「哪個函式用了 CPU」。可以把工作管理員那個「50%」從處理程序 → 執行緒 → 堆疊 → 函式拆開。6
- CPU Usage (Precise) 是內容切換的完整紀錄,告訴你「執行緒在等誰」。走 Waits(等待時間)、ReadyingProcess(誰叫醒它)、ReadyThreadStack(叫醒者的堆疊),是本文最想傳達的手法。47
- 讀堆疊需要符號設定。WPA 預設會參照 Microsoft 的公開符號伺服器。要看到自己應用的函式名稱,請加上自己的 PDB 路徑。8
- ETL 檔含有處理程序名稱與檔案路徑等系統內部資訊。擷取只做到必要的最小量,並在擷取前決定若帶出公司要怎麼處理。
2. 工具各司其職 ── WPR 擷取,WPA 讀
Windows Performance Toolkit(WPT)是 Windows ADK(Windows Assessment and Deployment Kit)裡的效能調查工具組;核心是 WPR 與 WPA 這一對。2 角色分得很清楚。
- WPR(Windows Performance Recorder)=擷取。它把一組 ETW 提供者捆成稱為「設定檔」的單位,開始與停止記錄,產出 ETL 檔。命令列版 wpr.exe 從 Windows 8.1 起就內建,不必另外安裝。GUI 版(WPRUI.exe)包含在 ADK 裡。1
- WPA(Windows Performance Analyzer)=分析。它開啟 ETL 檔,用圖表與表格分析。需要安裝 ADK。2
換句話說,客戶現場什麼都不必放。用 OS 標準的 wpr.exe 擷取,把 ETL 檔帶回來,在自己的 PC 上用 WPA 讀──和封包擷取的「用 pktmon 擷取、用 Wireshark 讀」是同一種拆法。
flowchart TB
accTitle: 用 WPR 擷取、用 WPA 讀的分工
accDescr: 在客戶現場用 OS 標準的 wpr.exe 記錄並產出 ETL 檔;帶回來在自己已透過 ADK 安裝 WPA 的 PC 上分析
subgraph customer["客戶 PC(不必另外安裝)"]
wpr["wpr start → 重現 → stop"] --> etl["ETL 檔"]
end
subgraph office["自己的 PC(經 ADK 的 WPA)"]
wpa["分析圖表與表格"]
end
etl -->|"帶回來"| wpa
圖 1: 在客戶現場用 OS 標準的 wpr.exe 記錄並產出 ETL 檔;帶回來在自己已透過 ADK 安裝 WPA 的 PC 上分析。
和相近工具的差異,也值得先整理。
| Process Monitor | PerfView | WPR + WPA | |
|---|---|---|---|
| 它回答的問題 | 哪個處理程序對哪個路徑做了什麼、發生了什麼 | .NET 應用的 CPU、GC、配置看起來如何 | 整個 OS 上,時間消失在哪裡 |
| 範圍 | 檔案、登錄、處理程序啟動的操作紀錄 | 以受控程式碼為主 | 系統整體的 CPU、等待、磁碟、檔案 I/O、電源等 |
| 適合的症狀 | 設定沒被讀到、ACCESS DENIED | 只有自己的 .NET 應用變慢或記憶體問題 | 整台 PC 變慢、CPU 閒著卻還是慢、不知道元兇處理程序 |
| 文章 | Procmon 實戰指南 | PerfView 實務入門 | 本文 |
若 Procmon 是「做了什麼」的操作紀錄、PerfView 是「.NET 裡面發生了什麼」,WPA 就是跨每個處理程序稽核「時間消失在哪裡」的工具。ETW 本身的機制、以及怎麼為自己的應用加上 ETW 檢測,寫在〈Windows 事件記錄・ETW 入門〉。若自己的應用發出 ETW 事件,應用的檢查點會記在同一份追蹤裡,對齊起來就容易得多。不過 WPR 只記錄你選的記錄設定檔所啟用的提供者事件。GeneralProfile 不含自己的提供者,若要混進來,請準備啟用自己提供者的自訂記錄設定檔(.wprp),並用 wpr -start GeneralProfile -start MyApp.wprp!MyAppProfile 組合,用 ! 指定 .wprp 檔內的設定檔名稱3。
3. 擷取實務(WPR)── start、重現、stop
在系統管理員終端機裡的基本步驟。
:: List of built-in profiles you can use
wpr -profiles
:: 1. Start capture (general-purpose profile, file mode)
wpr -start GeneralProfile -filemode
:: 2. Reproduce the issue (check capture status with wpr -status)
:: 3. Stop and save (you can attach a description of the problem).
:: Create the destination folder in advance (without it, -stop fails to save)
mkdir C:\temp 2>nul
wpr -stop C:\temp\slow-pc.etl "Reproduced the issue where the whole PC becomes slow while starting App X"
:: To abandon without saving
wpr -cancel
傳給 -start 的是設定檔,一捆調查所需的 ETW 提供者。3 只記住常用的就夠。9
| 設定檔 | 它記錄什麼 | 何時使用 |
|---|---|---|
GeneralProfile |
含 CPU 取樣、內容切換、磁碟 I/O 的通用組合 | 從這裡開始。不知道哪裡不對時的第一手 |
CPU |
詳細的 CPU 使用 | 已經知道 CPU 在燒時 |
DiskIO |
磁碟 I/O 活動 | 懷疑磁碟時 |
FileIO |
檔案 I/O 活動 | 想追哪個檔案正在被存取時 |
可以連續寫幾個 -start 一次指定多個設定檔(例如 wpr -start GeneralProfile -start FileIO -filemode)。3
flowchart TB
accTitle: WPR 擷取流程與模式怎麼選
accDescr: 當場能重現的問題用檔案模式短而可靠地擷取;時機不明的問題用預設的記憶體模式環形緩衝區等待。開機或登入時的問題用開機追蹤。無論哪種,start/重現/stop 的步驟相同
q{"何時發生?"}
q -->|"當場"| file["檔案模式:短擷取"]
q -->|"時機不明"| mem["記憶體模式:等待(3.1)"]
q -->|"開機或登入"| boot["開機追蹤(第 8 章)"]
file --> s1["start → 重現 → stop"]
mem --> s1
圖 2: 當場能重現的問題用檔案模式短而可靠地擷取;時機不明的問題用預設的記憶體模式環形緩衝區等待。開機或登入時的問題用開機追蹤。無論哪種,start/重現/stop 的步驟相同。
3.1. Memory 模式與 File 模式 ── 能重現,還是要等
WPR 有兩種記錄目的地模式;預設是 Memory 模式(記憶體內的環形緩衝區)。它是從最舊事件開始覆寫的環形緩衝區,適合讓擷取持續跑、等時機不明的問題發生、發生時再停。加上 -filemode 就切到 File 模式,一切寫進連續檔案。這個不會被覆寫掉;上限只剩剩餘磁碟空間,檔案會無限成長。10
flowchart TB
accTitle: Memory 模式與 File 模式怎麼記錄
accDescr: Memory 模式寫入記憶體內的環形緩衝區;較舊的事件被覆寫、只留下最近的,因此適合等待。File 模式把一切留在檔案裡,但上限只剩剩餘磁碟空間,因此適合短而可靠的重現
ev["ETW 事件"] --> ring["Memory 模式:環形緩衝區"]
ev --> filem["File 模式:讓檔案成長"]
ring -.-> use1["等時機不明的問題"]
filem -.-> use2["短而可靠的重現"]
圖 3: Memory 模式寫入記憶體內的環形緩衝區;較舊的事件被覆寫、只留下最近的,因此適合等待。File 模式把一切留在檔案裡,但上限只剩剩餘磁碟空間,因此適合短而可靠的重現。
選擇的經驗法則如下。
- 當場能重現 → File 模式。重現前一刻開始、剛結束就停,把擷取控制在幾分鐘內
- 時機不明 → 用 Memory 模式(預設)等待。一發生就
wpr -stop - 即使只是幾分鐘的 GeneralProfile,也可能產出數百 MB 到 GB 級的 ETL。檔案太大時 WPA 可能無法分析,所以「擷取得愈久愈好」會適得其反1011
要用 GUI 擷取,啟動 WPRUI,選設定檔與 Logging mode,再 Start/Save。官方 How-to 整理了步驟。11 若請客戶現場窗口擷取,上面三條指令可以直接寫進程序。
4. 讀 WPA 的基礎 ── 圖表、表格金科玉律、縮小時間範圍
把擷取到的 ETL 在 WPA 開啟後,左側的 Graph Explorer 會依 System Activity、Computation、Storage、Memory 等分類列出圖表縮圖。12 把想看的圖表拖到右側 Analysis 頁籤,上方出現圖、下方出現表。先掌握這三件事。
- 表格的金科玉律──欄位順序決定分組。WPA 表格有金、藍兩條垂直線,金線左側的欄位依該順序把資料階層化(分組),藍線右側的欄位是彙總。13 排成 Process → Stack 就得到依處理程序的堆疊彙總;Stack → Process 就得到使用同一堆疊的每個處理程序的彙總──拖曳欄位重新排序本身就是分析操作。懂這一點,每張 WPA 表格都用同一種讀法。
flowchart TB
accTitle: 表格的金科玉律──兩條線與欄位角色
accDescr: 金線左側的欄位依該順序把資料階層化;金線與藍線之間是顯示欄;藍線右側是彙總。拖曳欄位重新排序本身就是分析操作
left["金線左側:分組"] --> gold["金線"]
gold --> mid["兩線之間:顯示"]
mid --> blue["藍線"]
blue --> right["藍線右側:彙總"]
left -.-> op["拖曳欄位來分析"]
圖 4: 金線左側的欄位依該順序把資料階層化;金線與藍線之間是顯示欄;藍線右側是彙總。拖曳欄位重新排序本身就是分析操作。
- 縮小時間範圍。在圖上拖曳選範圍,再右鍵「Zoom」,彙總就只切到該區間。效能調查原則上永遠只看「問題正在發生的區間」(第 9 章)。
- 設定符號。要用函式名稱讀堆疊,從選單執行 Trace > Load Symbols。14 預設會參照 Microsoft 的公開符號伺服器(msdl.microsoft.com),所以有網路連線就能解析 Windows 自己的堆疊。要看到自己應用的函式名稱,在 Trace > Configure Symbol Paths 加上自己應用的 PDB 資料夾。8 PDB 是什麼、為什麼即使 Release 組建也該一律保留,整理在〈PDB(程式資料庫)是什麼〉。對 .NET Framework 的 NGen 原生映像,WPR 在擷取時會產生 NGen PDB(.ngenpdb)並放在追蹤旁邊的資料夾,WPA 會自動參照。8 這只是 NGen 映像的機制,一般自己的 JIT .NET 應用程式碼不在範圍內。從 JIT 程式碼位址到函式名稱的對應,是從 CLR 發出的 JIT 事件解析的,因此調查 .NET 應用時,請準備啟用 CLR 提供者(Microsoft-Windows-DotNETRuntime 與對應的 Rundown)的記錄設定檔(.wprp),並用和第 3 章自己的提供者相同的方式組合,
wpr -start GeneralProfile -start MyDotNet.wprp!profile-name,讓 CLR 事件進追蹤(可用wpr -profiles確認本機 WPR 提供哪些內建設定檔)。除此之外,保留組建產生的 PDB 以便對應到原始碼行,並加到上面的符號路徑。
flowchart TB
accTitle: 用函式名稱讀堆疊的符號解析
accDescr: 執行 Trace Load Symbols 後,Windows 本身從 Microsoft 公開符號伺服器解析,自己的應用從加到符號路徑的組建 PDB 解析。NGen 映像使用 WPR 產生的 NGen PDB;JIT .NET 程式碼從追蹤裡的 CLR JIT 事件加上組建 PDB 解析
load["Trace > Load Symbols"] --> ms["Windows:公開符號"]
load --> own["自己的應用:組建 PDB"]
ms -.-> ngen["NGen:WPR .ngenpdb"]
own -.-> jit["JIT:CLR 事件 + PDB"]
圖 5: 執行 Trace Load Symbols 後,Windows 本身從 Microsoft 公開符號伺服器解析,自己的應用從加到符號路徑的組建 PDB 解析。NGen 映像使用 WPR 產生的 NGen PDB;JIT .NET 程式碼從追蹤裡的 CLR JIT 事件加上組建 PDB 解析。
準備好之後,從下一個分支進入。在那個區間,CPU 是高還是低?高就第 5 章(Sampled);低卻還是慢就第 6 章(Precise)。
flowchart TB
accTitle: 依症狀選擇 WPA 圖表的分支
accDescr: 縮放到問題區間;CPU 高就到 CPU Usage Sampled;低卻還是慢,先確認是否單核/單執行緒釘住,再做 CPU Usage Precise 的等待分析;懷疑磁碟就看 Disk Usage 與 File IO
zoom["縮放到問題區間"] --> cpu{"該區間的 CPU?"}
cpu -->|"高"| sampled["第 5 章:Sampled"]
cpu -->|"低卻還是慢"| core{"單核/單執行緒釘住?"}
core -->|"是"| sampled
core -->|"否"| precise["第 6 章:Precise"]
cpu -->|"懷疑磁碟"| disk["第 7 章:磁碟/檔案 I/O"]
圖 6: 縮放到問題區間;CPU 高就到 CPU Usage Sampled;低卻還是慢,先確認是否單核/單執行緒釘住,再做 CPU Usage Precise 的等待分析;懷疑磁碟就看 Disk Usage 與 File IO。
5. CPU 很高時 ── 用 CPU Usage (Sampled) 看「誰在燒哪個函式」
CPU 被釘住時,看的是 CPU Usage (Sampled)。這是大約每 1 毫秒在每顆 CPU 上記錄「現在哪個處理程序的哪個堆疊在跑」的取樣資料,樣本數比例就是 CPU 時間的拆解。6
flowchart TB
accTitle: CPU Usage Sampled 怎麼運作
accDescr: 大約每 1 毫秒記錄每顆 CPU 上正在跑的堆疊,彙總後的樣本比例就是 CPU 時間的拆解。從處理程序讀到執行緒、堆疊、函式。樣本之間就結束的短活動不會出現
tick["大約每 1 ms 中斷"] --> snap["記錄正在跑的堆疊"]
snap --> agg["樣本比例 = CPU 拆解"]
agg --> drill["處理程序 → 執行緒 → 堆疊"]
snap -.-> miss["樣本之間的活動會漏掉"]
圖 7: 大約每 1 毫秒記錄每顆 CPU 上正在跑的堆疊,彙總後的樣本比例就是 CPU 時間的拆解。從處理程序讀到執行緒、堆疊、函式。樣本之間就結束的短活動不會出現。
- 從 Graph Explorer 的 Computation 把 CPU Usage (Sampled) 放到 Analysis 頁籤,選 Utilization by Process, Stack 預設。5
- 依 Weight(或 Count)由大到小看處理程序。工作管理員裡那個「50%」的身分,先在處理程序層級清楚起來。
- 展開元兇處理程序的 Stack 欄。堆疊以樹狀彙總,走分叉處數字不太掉的那條路,就會落到正在燒 CPU 的函式。符號若已解析,就是直線對到自己程式碼裡的哪個函式。
- 若展開樹很煩,把圖表顯示切到 Flame。寬度=CPU 時間占比,哪條呼叫路徑占主導一目了然。CPU Usage (Sampled) 也有 Flame by Process, Stack 預設。13
有一個注意。因為是取樣,樣本之間就結束的短活動不會出現。6 把它記成看「整體上 CPU 用在哪裡」的工具,不是量每次呼叫精確持續時間的工具。
6. CPU 很低卻還是慢 ── CPU Usage (Precise) 與等待分析
這是本文的核心。不過在進入等待分析之前,先確認一件事。「整體 CPU 使用率低」不代表「瓶頸不是 CPU」。在 16 核 PC 上,釘在單核的序列工作(單一 UI 執行緒全力跑)整體看起來只有約 6%。先用第 5 章的 Sampled(或 CPU Usage (Precise) 的 Utilization by CPU)確認沒有特定核心或執行緒被釘住,若沒有,再到這一章──工作不是跑不了,而是在等。告訴你它在等什麼的,是 CPU Usage (Precise)。
Sampled 是取樣,Precise 則是內容切換(執行緒切換)的完整紀錄。執行緒進入等待、被誰叫醒(Ready)、落到 CPU──這一趟來回每列留下一行,你可以讀以下欄位。74
| 欄位 | 意義 |
|---|---|
| NewThreadStack | 該執行緒在哪個堆疊進入等待(=停下時在做什麼) |
| Waits (us) | 等了多久 |
| Ready (us) | 從被叫醒到落到 CPU,被讓它等多久(CPU 競爭) |
| ReadyingProcess / ReadyingThreadId | 叫醒該執行緒(解除等待)的處理程序與執行緒 |
| ReadyThreadStack | 叫醒者在哪個堆疊叫醒它 |
flowchart TB
accTitle: 一次等待來回與欄位如何對應
accDescr: 執行緒在留在 NewThreadStack 的堆疊上進入等待,等 Waits 的時間。有人叫醒它時,那一方留在 ReadyingProcess 與 ReadyThreadStack;它再等 Ready 時間的 CPU 競爭,然後再跑
run1["執行中"] -->|"進入等待"| waitst["等待(Waits us)"]
waitst -->|"有人叫醒它"| ready["Ready(CPU 競爭)"]
ready -->|"落到 CPU"| run2["再次執行"]
waitst -.-> col["NewThreadStack/ReadyingProcess"]
圖 8: 執行緒在留在 NewThreadStack 的堆疊上進入等待,等 Waits 的時間。有人叫醒它時,那一方留在 ReadyingProcess 與 ReadyThreadStack;它再等 Ready 時間的 CPU 競爭,然後再跑。
讀法如下。4
- 套用 Utilization by Process, Thread 預設,並把 NewThreadStack 與 ReadyThreadStack 加到欄位。
- 先找出正在執行延遲操作的執行緒(UI 執行緒、處理該請求的執行緒)。只依總 Waits 由大到小看會混亂,因為訊息迴圈或計時器這類「刻意一直在等」的執行緒會占據頂端。找到目標執行緒後,若它的 CPU Usage (ms) 很大就是第 5 章的 CPU 問題;若 Waits 占主導就是等待問題。
- 展開 NewThreadStack,看停下時在做什麼。
WaitForSingleObject或EnterCriticalSection是鎖等待;在ReadFile這類同步 I/O 裡是 I/O 等待;在通訊端接收裡是在等對端回應。 - 接著看誰解除了等待。展開 ReadyThreadStack,檢查 ReadyingProcess / ReadyingThreadId。若是從核心的
KiTimerExpiration叫醒,就是計時器(=睡到逾時);若是從 I/O 完成處理叫醒,就確認是 I/O 等待。4 - 若叫醒它的是另一個執行緒或另一個處理程序,用同一套步驟調查那個執行緒。「A 在等 B 放鎖,B 在等 C 的 RPC 回應,C 在等磁碟 I/O」──把這條鏈走到根,得到的就是延遲的關鍵路徑。7
flowchart TB
accTitle: 等待分析裡走的關鍵路徑鏈
accDescr: 從延遲執行緒 A 的 NewThreadStack 看出停下時在做什麼,從 ReadyThreadStack 與 ReadyingProcess 找出叫醒者 B,再用同一套步驟調查 B,直到根上的磁碟 I/O
a["執行緒 A(延遲的工作)"] -->|"鎖等待"| b["執行緒 B(握著鎖)"]
b -->|"RPC 等待"| c["處理程序 C"]
c -->|"同步 I/O 等待"| d["磁碟 I/O(根)"]
d -.->|"完成叫醒 C"| c
c -.->|"回應叫醒 B"| b
b -.->|"放鎖叫醒 A"| a
圖 9: 從延遲執行緒 A 的 NewThreadStack 看出停下時在做什麼,從 ReadyThreadStack 與 ReadyingProcess 找出叫醒者 B,再用同一套步驟調查 B,直到根上的磁碟 I/O。
在「我們多執行緒了卻沒變快」這種案例,這套步驟會把每個工作者都排在同一把鎖上原樣顯示出來。從設計上避開鎖競爭,寫在〈多執行緒的實務最佳實踐 .NET篇〉;不在同步 I/O 裡等待、而是依完成通知執行的 Windows 機制,寫在〈I/O 完成埠(IOCP)與 .NET 執行緒集區〉。在 WPA 裡釘下「它在等誰」,再用那些設計論點修,是一連貫的流程。
7. 磁碟與檔案 I/O ── 找出「有人在掃磁碟」
「整台 PC 變慢」的經典元兇不是 CPU 而是磁碟。用 Storage 分類裡的 Disk Usage 與 File I/O 調查。15
Disk Usage 是磁碟 I/O 的紀錄,有兩個欄位重要。Disk Service Time 是磁碟裝置實際花在處理該 I/O 的時間;IO Time 是從 I/O 進入 OS 佇列到完成的時間。IO Time 永遠至少是 Service Time 加上佇列量,所以若 IO Time 遠長於 Service Time,該 I/O 是「在佇列裡等」。6 不過單憑這一點,還不能決定讓佇列堆起來的元兇是另一個處理程序,還是只有該處理程序自己的沉重 I/O 排在慢裝置上。不要在這裡下結論;用 Service Time(裝置本身的回應)以及接下來依處理程序、路徑、堆疊的拆解來定案。
接著,用 Utilization by Process, Path Name, Stack 預設,依 IO Time 或 Size 由大到小看哪個處理程序從哪個堆疊對哪個檔案發出 I/O。15 現場常出現的答案是這兩種。
- 防毒正在掃每個檔案。在應用啟動變慢的視窗裡,看到防毒處理程序發出大量讀取。處理程序名稱、路徑、數量本身就是討論排除清單的證據。
- 另一個處理程序正在猛寫。備份、索引器、寫太多的日誌等。寫入何時到達磁碟牽涉快取管理員,因此「你寫的那一刻」與「磁碟忙碌的那一刻」可能岔開,這一點也寫在〈快取管理員──你的 WriteFile 究竟何時送達磁碟〉。
File I/O 再上一層,是應用發出的檔案操作(Create/Read/Write 等)紀錄,Duration by Process, Thread, Type 這類預設可以依檔名與操作彙總時間。15 在檔案系統或篩選驅動程式裡花時間、還沒到磁碟的案例不會出現在 Disk Usage,因此「Disk Usage 很平靜但 File I/O 很慢」這種不一致本身就是線索。若想從同步與非同步 I/O 的機制開始,見〈同步 I/O 與非同步 I/O──OVERLAPPED 的真正含義〉。
flowchart TB
accTitle: File IO 與 Disk Usage 看到的不同層
accDescr: 應用的檔案操作經檔案系統與篩選驅動程式,從 OS I/O 佇列走到磁碟裝置。File IO 記錄上層操作;Disk Usage 記錄到達磁碟的 I/O;IO Time 與 Disk Service Time 的差是佇列時間
app["應用:ReadFile/WriteFile"] --> fio["檔案系統與篩選器(File I/O)"]
fio --> queue["OS I/O 佇列"]
queue --> dev["磁碟裝置(Disk Usage)"]
fio -.-> n1["Disk Usage 看不到"]
queue -.-> n2["IO Time − Service Time"]
dev -.-> n3["Service Time = 裝置"]
圖 10: 應用的檔案操作經檔案系統與篩選驅動程式,從 OS I/O 佇列走到磁碟裝置。File IO 記錄上層操作;Disk Usage 記錄到達磁碟的 I/O;IO Time 與 Disk Service Time 的差是佇列時間。
「說不定是記憶體不夠在換頁」這條思路,可以在進 WPA 之前先用工作管理員與資源監視器做第一道隔離。不過不要只看認可記憶體就排除──即使認可還有餘裕,實體記憶體壓力裁切工作集、硬錯誤持續發生的情況仍可能。也要看可用實體記憶體與資源監視器的「Hard Faults/sec」。怎麼讀寫在〈Windows 的「記憶體使用量」代表什麼〉。
8. 開機與登入很慢 ── 開機追蹤的入口
「開機要 3 分鐘」這類,還沒來得及親手跑 wpr -start 就結束了。WPR 有開機追蹤,可以安排 OS 在下次開機時自動開始記錄。3
:: 1. Arrange automatic recording on the next boot
wpr -boottrace -addboot GeneralProfile -filemode
:: 2. Restart (reproduce the slow boot)
:: 3. After boot, stop recording and save (the arrangement is also cleared)
mkdir C:\temp 2>nul
wpr -boottrace -stopboot C:\temp\boot.etl "Issue where boot takes 3 minutes"
flowchart TB
accTitle: 開機追蹤流程
accDescr: 用 addboot 安排下次開機自動記錄並重新啟動後,OS 在開機時自動開始記錄。登入後用 stopboot 儲存也會清除安排。要放棄,用 cancelboot 清除
add["wpr -boottrace -addboot"] --> rebootpc["重新啟動(慢開機)"]
rebootpc --> auto["OS 在開機時記錄"]
auto --> stop2["登入後:-stopboot"]
add -.-> cancel["放棄:-cancelboot"]
圖 11: 用 addboot 安排下次開機自動記錄並重新啟動後,OS 在開機時自動開始記錄。登入後用 stopboot 儲存也會清除安排。要放棄,用 cancelboot 清除。
以前 xbootmgr 負責的開機與關機量測,在現行 WPR 也可以用 -onoffscenario Boot 這類選項跑。3 擷取到的追蹤用前面幾章同一套工具讀。在 Processes 圖上看哪個處理程序何時誕生,縮放到開機卡住的視窗,再分類 CPU、等待或磁碟──啟動應用串行等待某件事、服務啟動卡在特定 I/O 等就會看得見。開機分析本身是一門深的專長,本文只走到入口:「用手抓不到的問題,仍可用 WPR 擷取」。先用 GeneralProfile 開機追蹤掌握整體畫面。
9. 實作模式 ── 分類 → 縮放 → 堆疊,反覆做
工具清楚之後,這是調查整體的模式。
- 釘下現象的時間。不是「變慢了」,而是「10:23:40–10:24:10 變慢」。應用日誌、事件記錄、操作者的筆記──什麼都行。若自己的應用把檢查點寫到 ETW 或事件記錄,追蹤裡的事件就原樣成為時間樁。
- 只縮放到那個區間。整份追蹤的彙總會被平均掉,真正重要的異常被稀釋。WPA 分析永遠是「異常區間」對「正常區間」的比較。
- 先分類「CPU、等待、還是 I/O」。看 CPU Usage (Sampled);在燒就第 5 章。沒在燒就看 CPU Usage (Precise) 的 Waits(第 6 章)。若 Disk Usage 的 IO Time 膨脹,第 7 章。先走這三岔,才不會迷路。
- 反覆假設 → 縮放 → 堆疊。若覺得「防毒?」,縮到該處理程序再用堆疊佐證。站不住就下一個假設。在走到堆疊並佐證之前不下結論,是這類調查的紀律。
flowchart TB
accTitle: 效能調查的反覆迴圈
accDescr: 釘下現象時間、縮放到區間、分類 CPU/等待/I/O、形成假設並縮小範圍、用堆疊佐證。站得住就確認原因;站不住就用下一個假設再來
time["釘下時間"] --> zoomstep["縮放到該區間"]
zoomstep --> triage["分類 CPU/等待/I/O"]
triage --> hypo["假設並縮小範圍"]
hypo --> stack["用堆疊佐證"]
stack -->|"站得住"| fix["原因確認"]
stack -->|"站不住"| hypo
圖 12: 釘下現象時間、縮放到區間、分類 CPU/等待/I/O、形成假設並縮小範圍、用堆疊佐證。站得住就確認原因;站不住就用下一個假設再來。
最後是擷取檔的處理。ETL 檔廣泛反映系統內部:每個處理程序的名稱、開啟過的檔案路徑、載入的模組,以及(依設定檔而定)登錄機碼名稱。標準 GeneralProfile 擷取不含通訊內容這類資料本體,但若啟用了自訂提供者,該事件的承載(應用記錄的字串等)會原樣進去。確認你啟用的提供者會發出什麼之後,把它當成機密到足以帶出公司的檔案來對待。和封包擷取一樣,把必要的最小擷取、與交接對方的約定、以及保存期限與刪除寫進程序。
flowchart TB
accTitle: ETL 檔反映什麼、以及怎麼處理
accDescr: ETL 反映每個處理程序名稱、開啟過的檔案路徑、模組,以及依設定檔而定的登錄機碼名稱;啟用自訂提供者也會含其承載。當成機密:必要的最小擷取、與對方的約定、以及保存期限與刪除
etl["ETL 檔"] --> a1["名稱、路徑、模組"]
a1 --> a2["登錄機碼(部分)"]
a2 --> a3["自訂承載"]
a3 -.-> rule["當成機密對待"]
圖 13: ETL 反映每個處理程序名稱、開啟過的檔案路徑、模組,以及依設定檔而定的登錄機碼名稱;啟用自訂提供者也會含其承載。當成機密:必要的最小擷取、與對方的約定、以及保存期限與刪除。
10. 總結
- 工作管理員解釋不了的「整台 PC 變慢」,用整個 OS 的 ETW 追蹤調查──用 WPR 擷取、用 WPA 讀。wpr.exe 從 Windows 8.1 起就內建,因此「在客戶現場擷取、把 ETL 帶回來、在自己機器上用 WPA 讀」這種拆法成立。
- 擷取是
wpr -start GeneralProfile -filemode→ 重現 →wpr -stop trace.etl三步。能重現就用幾分鐘內的 File 模式;要等就用 Memory 模式(環形緩衝區)。愈長愈好並不成立。 - 掌握三點就能開始用 WPA:表格金科玉律(金線左側=分組)、縮放時間範圍、符號設定(自己的應用需要 PDB)。
- CPU 高,就在 CPU Usage (Sampled) 走處理程序 → 堆疊 → 函式。CPU 低卻還是慢,就在 CPU Usage (Precise) 把 NewThreadStack(停下時在做什麼)→ Waits(等了多久)→ ReadyingProcess 與 ReadyThreadStack(誰叫醒它)這條鏈走到根。
- 對磁碟,從 Disk Usage 的 IO Time 與 Service Time 之差看出「花在佇列裡的時間」,再從 Service Time 與依處理程序、路徑、堆疊的拆解找出原因(是裝置本身慢,還是誰讓佇列堆起來)。慢開機可用
wpr -boottrace擷取。 - 實作模式是(1)釘下時間(2)縮放到區間(3)分類 CPU、等待或 I/O(4)反覆假設 → 縮放 → 堆疊。ETL 含內部資訊,當成機密對待。
WPA 的畫面令人卻步,每個人第一個小時都會迷路。不過一旦「金線左側是分組」與「Sampled 是燒在哪、Precise 是等誰」這兩根主軸進去,其餘就是同一套操作反覆做。下次再接到「CPU 還有餘裕卻還是慢」的諮詢,關掉工作管理員,擷取一份追蹤。
相關文章
- 用 PerfView 與 dotnet-trace 找出「變慢」的原因 ── .NET 效能調查實務入門
- Process Monitor(ProcMon)實戰指南 ── 在10分鐘內找出「設定未被讀取」「ACCESS DENIED」的原因
- Windows 事件記錄・ETW 入門 ── 讓業務應用程式的日誌搭上 OS 標準機制
- Windows 的「記憶體使用量」代表什麼 ── 正確解讀 Working Set、Private Bytes、Commit 與分頁檔
- 把 Windows 的「處理器排程」改成「背景服務」會發生什麼 - 整理 quantum、優先權提升、P-Core/E-Core
- PDB(程式資料庫)是什麼 ── 理解偵錯資訊、符號與 Source Link
相關諮詢領域
小村軟體有限公司承接「整台 PC 變慢、不知道為什麼」「CPU 還有餘裕、應用卻還是慢」「只有特定環境開機極慢」這類系統整體效能問題的調查。我們把 WPR/WPA 的擷取設計(在哪個環境、哪個設定檔、擷取多少)、追蹤分析,以及隨之而來的應用側與設定側修正,當成一連貫的委託來處理。
參考連結
-
Microsoft Learn, Introduction to WPR. 關於 WPR 是以 ETW 為基礎的效能記錄工具;關於命令列版 WPR.exe 從 Windows 8.1 起就內建、不必另外安裝;它與 GUI 版 WPRUI.exe 的關係;以及記錄設定檔的想法。 ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Windows Performance Analyzer. 關於 WPA 包含在 Windows ADK,是從 WPR、Xperf 等記錄的 ETW 事件建立圖表與資料表的分析工具,可以開啟並分析任何 ETL 檔。 ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, WPR Command-Line Options. 關於 wpr -start/-stop/-cancel/-status/-profiles 的語法;-filemode(預設是記憶體模式);一次指定多個設定檔;用 -boottrace 做開機追蹤(addboot/stopboot/cancelboot);以及用 -onoffscenario 記錄 Boot 這類開/關轉換。 ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn, CPU Analysis. 關於 CPU Usage (Precise) 圖欄位(NewThreadStack、ReadyThreadStack、ReadyingProcess、Waits 等)的定義;展開 ReadyThreadStack、沿 ReadyingProcess/ReadyingThread 走到等待根本原因的步驟;以及怎麼分辨來自 KiTimerExpiration(計時器等待)或 I/O 完成的喚醒。 ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Troubleshoot processes and threads by using WPR and WPA. 關於高 CPU 使用率時把 CPU Usage (Sampled) 讀成 Process→Stack,以及等待分析時使用 CPU Usage (Precise) 的 Readying Process、Readying Thread、Readying Stack 與 Wait 欄等組態;以及依症狀對應設定檔與圖表的對照表。 ↩ ↩2
-
Microsoft Learn, Exercise 2 - Evaluate Fast Startup Using Windows Performance Toolkit. 關於 CPU Usage (Sampled) 大約以 1 毫秒間隔取樣、樣本之間的短活動不會被記錄;走處理程序 → 執行緒 → 堆疊以找出 CPU 消耗拆解的步驟;以及 Disk Usage IO Time(含佇列時間)與 Disk Service Time(磁碟處理時間)的意義。 ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Exercise 3 - Understand Critical Path and Wait Analysis. 關於關鍵路徑分析的想法(Running/Ready/Waiting 分類);CPU Usage (Precise) 表格欄位 NewThreadStack、ReadyThreadStack、ReadyingProcess、Waits、Ready 等的意義;以及依序走叫醒者執行緒以拆開延遲鏈的步驟。 ↩ ↩2 ↩3
-
Microsoft Learn, Loading Symbols. 關於 _NT_SYMBOL_PATH 未設定時 WPA 預設參照 Microsoft 公開符號伺服器(msdl.microsoft.com);為自己的元件加上 PDB 路徑;以及 WPR 為 .NET 受控符號在追蹤旁邊的 .ngenpdb 資料夾產生 PDB、WPA 自動參照。 ↩ ↩2 ↩3
-
Microsoft Learn, Built-in Recording Profiles. 關於 WPR 內建記錄設定檔的清單(CPU 使用、磁碟 I/O 活動、檔案 I/O 活動、登錄 I/O 活動、網路 I/O 活動等)以及各設定檔記錄什麼。 ↩
-
Microsoft Learn, Logging Mode. 關於記錄模式是 File(連續檔案)與 Memory(記憶體內環形緩衝區)、預設是 Memory;關於 Memory 適合時機不明的問題、較舊事件會被覆寫;以及 File 的上限只剩剩餘磁碟空間、檔案太大時 WPA 可能無法分析。 ↩ ↩2
-
Microsoft Learn, WPR How-to Topics. 關於在 WPRUI 開始與停止記錄的步驟;選擇設定檔、詳細程度與 Logging mode;以及長時間記錄會讓檔案巨大、WPA 可能無法分析,因此應選 Memory 模式的注意事項。 ↩ ↩2
-
Microsoft Learn, Graph Explorer. 關於 Graph Explorer 視窗依 System Activity、Computation、Storage、Memory 等分類列出圖表縮圖;以及把圖表拖到 Analysis 頁籤、與表格一起顯示。 ↩
-
Microsoft Learn, Graphs (WPA Features). 關於 WPA 的 Flame 圖顯示;金線左側欄位是分組、藍線右側欄位是彙總的表格結構;以及 CPU Usage (Sampled) 的 Flame by Process, Stack 預設。 ↩ ↩2
-
Microsoft Learn, Load Symbols or Configure Symbol Paths. 關於從 WPA 的 Trace 選單用 Load Symbols 載入符號;以及在 Configure Symbol Paths 對話方塊設定與變更符號路徑的步驟。 ↩
-
Microsoft Learn, List of WPA Graphs. 關於 WPA 可用圖表的清單。Disk Usage 預設如 IO Time by Process, IO Type;Service Time by Process, Path Name, Stack;Utilization by Process, Path Name, Stack;以及 File I/O 預設如 Duration by Process, Thread, Type。 ↩ ↩2 ↩3
相關文章
共用相同標籤的最新文章。能以相近的主題延伸理解。
從睡眠恢復就壞掉的應用程式 ── Windows 電源事件的機制,以及耐得住恢復的業務應用寫法
打開筆電,業務應用程式的連線卻斷了──原因是設計從未把睡眠算進去。本文依一次資訊整理 WM_POWERBROADCAST 的通知流程、Modern Standby 的行為、斷線/重連設計、睡眠抑制,以及調查指令。
DllMain 與載入器鎖定 ── 「DLL 初始化什麼都別做」真正的理由
為什麼不能從 DllMain 呼叫 LoadLibrary,也不能和其他執行緒同步。本文依一次資訊說明載入器鎖定如何序列化每個 DLL 通知、結構上必定成立的死結場景、延遲初始化的正確設計,以及無回應的調查方式。
「沒有回應」的真面目 ── Windows 如何判定應用程式當住,以及不卡住的設計
Windows 的「沒有回應」是作業系統判定視窗 5 秒未取出訊息後,換成幽靈視窗的機制。本文涵蓋該判定的內部、無回應的經典原因、把重工作移出 UI 執行緒的設計,以及調查無回應的程序。
Windows 的「記憶體使用量」代表什麼 ── 正確解讀 Working Set、Private Bytes、Commit 與分頁檔
工作管理員的記憶體、Working Set、Private Bytes、Commit 並非同一個數值。本文解說 Windows 虛擬記憶體與實體記憶體的關係、分頁檔的角色,以及在記憶體不足或洩漏調查時應該檢視的指標。
用 PerfView 與 dotnet-trace 找出「變慢」的原因 ── .NET 效能調查實務入門
當商用應用程式「變慢」「CPU 卡住不動」「偶爾當掉」時,該用哪個工具看什麼?本文整理 PerfView 與 dotnet-trace 的角色分工、CPU 取樣的讀法(inclusive/exclusive)、用 ThreadTime 調查阻塞時間,以及與 EventSou...
相關主題
與本文相近的主題頁面。以本文為起點,可進一步連到相關服務與其他文章。
Windows 技術主題
彙整 KomuraSoft LLC 關於 Windows 開發、故障調查與既有資產活用文章的主題中心。
故障調查 & 長期運行故障
整理間歇性故障、通訊診斷、長期運行當機、失敗路徑測試基礎的主題頁面。
與本主題相關的服務
本文連結到以下服務頁面,歡迎從最接近的入口查看。
Windows 應用程式開發
支援包含常駐處理、設備連動、運作日誌與可維護結構的 Windows 桌面應用程式。
故障調查 & 根本原因分析
調查難以重現的故障、長時間執行後的問題、記憶體洩漏、通訊停滯等棘手的正式環境問題。
常見問題
整理諮詢這個主題時常見的問題。
- WPR 和 WPA 要去哪裡拿?不能安裝軟體的客戶現場也能用嗎?
- 擷取工具 wpr.exe(命令列版)從 Windows 8.1 起就內建,不必另外安裝。GUI 版 WPRUI 與分析工具 WPA(Windows Performance Analyzer)包含在 Windows ADK(Windows Assessment and Deployment Kit)裡,需要另外安裝。實務上若拆成「在客戶現場只用 OS 標準的 wpr.exe 擷取 ETL 檔,帶回來在自己的機器上用 WPA 分析」,即使不能加裝軟體的現場也能做系統整體的效能調查。
- 工作管理員顯示 CPU 還有餘裕,為什麼還是慢?WPA 能看到什麼?
- CPU 使用率低卻仍然慢,代表工作不是用不了 CPU,而是停在「等某件事」。鎖競爭、等同步 I/O 完成、等另一個處理程序回應,都是典型情況。工作管理員只顯示結果──使用率;WPA 的 CPU Usage (Precise) 則從每次內容切換的紀錄,看出執行緒從哪開始等(NewThreadStack)、等了多久(Waits)、以及誰叫醒它(ReadyingProcess、ReadyThreadStack)。沿著讓它等待的那一方走下去,就能把「變慢的元兇」追到函式。
- 追蹤要擷取多久?檔案會不會變得巨大?
- 問題能重現時,基準是重現前一刻開始、剛重現完就停,控制在幾分鐘內。WPR 預設是 Memory 模式,寫入記憶體內的環形緩衝區;較舊的事件會被覆寫,適合等待發生時機不明的問題。加上 -filemode 的 File 模式會把一切連續寫進檔案,上限只剩剩餘磁碟空間,檔案太大時 WPA 可能無法分析。長時間等待用 Memory 模式,短而可靠的重現用 File 模式。
- PerfView 和 WPA 該怎麼選?
- 兩種工具都處理 ETW 追蹤,但強項不同。PerfView 深懂 .NET 執行階段,擅長 GC、配置、JIT 這類受控應用特有的調查。WPA 適合跨圖表與表格讀整個 OS 的 CPU、磁碟、檔案 I/O、電源等,是「不是特定應用而是整台 PC 慢」、「牽涉多個處理程序」、或「懷疑應用外面(防毒、驅動程式、另一個處理程序)」時的首選。經驗法則是:只有自己的 .NET 應用變慢用 PerfView,整個系統變慢用 WPR/WPA。
- 在客戶的正式環境跑 WPR 沒問題嗎?
- 短時間擷取在實務上很常見,但不是無條件安全。ETW 很輕,但連同堆疊記錄大量事件仍會消耗一定的 CPU 與記憶體。把「重現步驟前一刻開始、剛結束就停」「擷取控制在幾分鐘內」「選業務影響小的時段」納入和平常變更一樣的核准流程。另外,ETL 檔含有處理程序名稱、檔案路徑、可執行檔資訊等系統內部資訊,若要帶出公司,應事先決定怎麼處理(最小化、保存期限、刪除)。