「新しいアプリを入れたら、PC全体の動作が重くなったと言われた。しかしタスクマネージャーを見ると、CPUにもメモリにも余裕がある」「起動に3分かかるPCが1台だけある。何が悪いのかまったく分からない」──性能がらみの相談には、この形が本当に多い。共通するのは、特定のプロセスを眺めても答えが出ないことです。
プロセス単位の道具は揃っています。ファイルやレジストリへのアクセスはProcess Monitorで見られますし、.NETアプリのCPUやGCはPerfViewで追えます。しかし「PC全体が重い」「CPUは暇なのに遅い」という症状は、犯人がどのプロセスかすら分からないところから始まります。アプリA が遅い原因がウイルス対策ソフトのスキャンかもしれず、別サービスのディスク大量書き込みかもしれず、複数プロセスにまたがるロックの連鎖かもしれない。プロセスの中ではなく、OS全体を1本の時間軸で記録したデータが要るのです。
それを採って読むための道具が、Windows Performance Recorder(WPR)とWindows Performance Analyzer(WPA)です。WPRはETW(Event Tracing for Windows)ベースでOS全体の動きを記録し、WPAはその記録をグラフとテーブルで解析します。CPUを誰がどのスタックで使ったか、スレッドが誰を待っていたか、ディスクI/Oをどのプロセスがどのファイルに発行したか──タスクマネージャーの一段も二段も下の事実が、時刻付きで全部残ります。
この記事では、中小企業の情シス担当者とWindowsアプリ開発者を対象に、WPRでの採取の実務と、WPAの読み方──特に「CPUが高い場合」と「CPUが低いのに遅い場合」の調べ方の違い──を、2026年8月時点の一次情報にもとづいて整理します。
1. まず結論
- 「PC全体が重い」系の調査の本命は、OS全体のETWトレースを採って読むWPR/WPAです。プロセス単位の道具(タスクマネージャー・Procmon・PerfView)で特定できない問題も、全プロセス・カーネルを1本の時間軸で見れば辿れます。12
- 採取ツールのwpr.exeはWindows 8.1以降のOSに標準搭載です。追加インストールなしで使えます。GUI版(WPRUI)と解析ツールのWPAはWindows ADKに含まれます。12
- 基本手順は3行です。管理者権限で
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つです。2 役割は明確に分かれています。
- WPR(Windows Performance Recorder) = 採取。ETWのプロバイダー群を「プロファイル」という単位で束ねて記録を開始・停止し、ETLファイルを作ります。コマンドライン版のwpr.exeはWindows 8.1以降のOSに標準搭載で、追加インストール不要です。GUI版(WPRUI.exe)はADKに含まれます。1
- WPA(Windows Performance Analyzer) = 解析。ETLファイルを開き、グラフとテーブルで分析します。ADKのインストールが必要です。2
つまり、顧客環境に置くべきものは何もありません。OS標準のwpr.exeで採り、ETLファイルを持ち帰り、手元のPCのWPAで読む──パケットキャプチャで「pktmonで採ってWiresharkで読む」のと同じ分担が成立します。
flowchart LR
accTitle: WPRで採りWPAで読む分担
accDescr: 顧客環境ではOS標準のwpr.exeで記録してETLファイルを作り、持ち帰って手元のPCにADKで入れたWPAで解析する
subgraph customer["顧客環境(追加インストール不要)"]
wpr["wpr -start → 事象を再現 → wpr -stop"] --> etl["ETLファイル"]
end
subgraph office["手元のPC(ADKでWPA導入済み)"]
wpa["WPAでグラフ・テーブル解析"]
end
etl -->|"持ち帰る"| wpa
似た道具との使い分けも先に整理しておきます。
| 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
管理者権限のターミナルでの基本手順です。
:: 使える組み込みプロファイルの一覧
wpr -profiles
:: 1. 採取開始(汎用プロファイル、ファイルモード)
wpr -start GeneralProfile -filemode
:: 2. 事象を再現させる(採取中の状態確認は wpr -status)
:: 3. 停止して保存(問題の説明を添えられる)。
:: 保存先フォルダーは事前に作っておく(無いと -stop が保存に失敗する)
mkdir C:\temp 2>nul
wpr -stop C:\temp\slow-pc.etl "アプリX起動中にPC全体が重くなる事象を再現"
:: 途中でやめる場合は保存せず破棄
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: 手元で再現できる事象はファイルモードで短時間・確実に採り、いつ起きるか分からない事象は既定のメモリモードのリングバッファで待ち受ける。起動やログオン中の事象はブートトレースを使う。いずれも開始・再現・停止の手順は同じ
q{"事象はいつ起きるか"}
q -->|"手元で再現できる"| file["-filemodeで短時間・確実に採取"]
q -->|"いつ起きるか分からない"| mem["メモリモード(既定・リングバッファ)で待ち受け(3.1節)"]
q -->|"起動・ログオン中"| boot["ブートトレース(8章)"]
file --> s1["wpr -start → 事象を再現・発生 → wpr -stop"]
mem --> s1
3.1. MemoryモードとFileモード ── 再現できるか、待ち受けるか
WPRの記録先には2モードあり、既定はMemoryモード(メモリ上の循環バッファ)です。古いイベントから上書きされるリングバッファなので、いつ起きるか分からない事象を採りっぱなしで待ち受け、起きたら止める使い方に向きます。-filemodeを付けるとFileモードになり、連続したファイルにすべて記録されます。こちらは上書きで消えない代わりに、上限はディスクの空き容量だけで、ファイルは際限なく育ちます。10
flowchart LR
accTitle: MemoryモードとFileモードの記録のされ方
accDescr: Memoryモードはメモリ上の循環バッファに記録し、古いイベントから上書きされて直近だけが残るため待ち受け向き。Fileモードはファイルに全部残るが上限はディスク空き容量だけで、短時間の確実な再現向き
ev["ETWイベントの流れ"] --> ring["Memoryモード: 循環バッファ(古い順に上書き → 直近だけ残る)"]
ev --> filem["Fileモード: ファイルに全部残る(上限はディスク空き容量)"]
ring -.-> use1["いつ起きるか分からない事象の待ち受け向き"]
filem -.-> use2["短時間で確実に再現できる事象向き"]
使い分けの目安は次のとおりです。
- その場で再現できる → Fileモード。再現の直前に開始、直後に停止し、採取は数分以内に収める
- いつ起きるか分からない → Memoryモード(既定)で待ち受け。発生したらすぐ
wpr -stop - 数分のGeneralProfileでもETLは数百MB〜GB級になり得ます。大きすぎるファイルはWPAで解析できないことがあるため、「長く採るほど良い」は逆効果です1011
GUIで採る場合はWPRUIを起動し、プロファイルとLogging modeを選んでStart/Saveするだけです。手順の詳細は公式のHow-toにまとまっています。11 顧客先の担当者に採取を依頼する場合も、上の3コマンドをそのまま手順書にできます。
4. WPAの読み方の基本 ── グラフ、テーブルの黄金律、時間の絞り込み
採れたETLをWPAで開くと、左側のGraph Explorerに、System Activity・Computation・Storage・Memoryなどのカテゴリでグラフのサムネイルが並びます。12 見たいグラフを右のAnalysisタブへドラッグすると、上にグラフ、下にテーブルが表示されます。最初に押さえるのは3つだけです。
- テーブルの黄金律 ── 列の並びがグルーピングを決める。WPAのテーブルには金色と青色の2本の縦バーがあり、金色バーより左の列の順にデータが階層化(グルーピング)され、青色バーより右の列が集計値になります。13 「Process → Stack」の順に並べればプロセスごとのスタック集計、「Stack → Process」の順なら同じスタックを使う全プロセスの集計、と列をドラッグして並べ替えること自体が分析操作です。この一点を理解すると、WPAのすべてのテーブルが同じ読み方になります。
flowchart LR
accTitle: テーブルの黄金律 ── 2本のバーと列の役割
accDescr: 金色バーより左の列の並び順でデータが階層化され、金色と青色のバーの間が表示列、青色バーより右が集計値になる。列をドラッグして並べ替えること自体が分析操作になる
left["金色バーより左: グルーピング列(並び順 = 階層)"] --> gold["金色バー"]
gold --> mid["バーの間: 表示列"]
mid --> blue["青色バー"]
blue --> right["青色バーより右: 集計値(Sum・%など)"]
left -.-> op["列のドラッグ = 分析操作(Process→Stackならプロセス別のスタック集計)"]
- 時間範囲を絞り込む。グラフ上でドラッグして範囲選択し、右クリックから「Zoom」で、その区間だけの集計に切り替わります。性能調査は常に「事象が起きていた区間」だけを見るのが原則です(9章)。
- シンボルを設定する。スタックを関数名で読むには、メニューのTrace > Load Symbolsを実行します。14 既定でMicrosoftの公開シンボルサーバー(msdl.microsoft.com)を参照するため、Windows本体のスタックはインターネット接続があれば解決できます。自社アプリの関数名まで見るには、Trace > Configure Symbol Pathsで自社アプリのPDBのフォルダーを追加します。8 PDBが何者で、なぜリリースビルドでも必ず保存しておくべきかは「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!プロファイル名の形で併用して、CLRのイベントをトレースに含めておきます(手元のWPRが使える組み込みプロファイルはwpr -profilesで確認できます)。そのうえで、ソース行との対応づけのためにビルドで生成される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本体: 公開シンボルサーバー(msdl)"]
load --> own["自社アプリ: Configure Symbol Pathsに追加したビルドPDB"]
load --> ngen[".NET FrameworkのNGenイメージ: WPR生成の.ngenpdb"]
load --> jit["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章: CPU Usage (Sampled)で「誰がどの関数で焼いているか」"]
cpu -->|"低いのに遅い"| core{"1コア・1スレッドの張り付きは?"}
core -->|"ある"| sampled
core -->|"ない"| precise["6章: CPU Usage (Precise)で「何を待っていたか」"]
cpu -->|"ディスクが疑わしい"| disk["7章: Disk Usage / File I/Oで犯人を特定"]
5. CPUが高い場合 ── CPU Usage (Sampled)で「誰がどの関数で焼いているか」
CPUが張り付いているなら、見るのはCPU Usage (Sampled)です。これは約1ミリ秒ごとに全CPUで「いまどのプロセスのどのスタックが実行中か」を記録したサンプリングデータで、サンプル数の比率がそのままCPU時間の内訳になります。6
flowchart LR
accTitle: CPU Usage Sampledの仕組み
accDescr: 約1ミリ秒ごとに全CPUで実行中のスタックを記録し、サンプルを集計した比率がCPU時間の内訳になる。プロセスからスレッド、スタック、関数へ降りて読む。サンプルの合間に終わる短い活動は写らない
tick["約1msごとの割り込み"] --> snap["全CPUで「いま実行中のスタック」を記録"]
snap --> agg["サンプルを集計(比率 = CPU時間の内訳)"]
agg --> drill["Process → Thread → Stack → 関数へ降りる"]
snap -.-> miss["サンプルの合間に終わる短い活動は写らない"]
- 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
1つだけ注意があります。サンプリングである以上、サンプルの合間に終わる短い活動は写りません。6 「合計としてどこがCPUを使ったか」を見る道具であって、1回ごとの正確な実行時間を測る道具ではない、と覚えておいてください。
6. CPUが低いのに遅い場合 ── CPU Usage (Precise)と待ち解析
この記事の核はここです。ただし待ち解析へ進む前に、1つ確かめることがあります。「全体のCPU使用率が低い」は「CPUがボトルネックでない」を意味しません。16コアのPCでは、1コアに張り付いたシリアル処理(UIスレッド1本が全力で回っている状態)も全体では6%程度にしか見えないからです。まず5章のSampled(またはCPU Usage (Precise)のUtilization by CPU)で特定のコア・スレッドの張り付きが無いかを確認し、それも無ければ本章へ ── 処理は動けないのではなく待っているのです。何を待っているかを教えてくれるのがCPU Usage (Precise)です。
Sampledがサンプリングなのに対し、Preciseはコンテキストスイッチ(スレッドの切り替え)の完全な記録です。スレッドが待ちに入り、誰かに起こされ(Ready)、CPUに載る──この往復が1行ずつ残っており、次の列が読めます。74
| 列 | 意味 |
|---|---|
| NewThreadStack | そのスレッドがどのスタックで待ちに入ったか(=何をして止まったか) |
| Waits (us) | 待っていた時間 |
| Ready (us) | 起こされてからCPUに載るまで待たされた時間(CPU の取り合い) |
| ReadyingProcess / ReadyingThreadId | そのスレッドを起こした(待ちを解いた)プロセスとスレッド |
| ReadyThreadStack | 起こした側がどのスタックで起こしたか |
flowchart LR
accTitle: 1回の待ちの往復と各列の対応
accDescr: スレッドはNewThreadStackに残るスタックで待ちに入り、Waitsの時間だけ待つ。誰かに起こされるとReadyingProcessとReadyThreadStackにその相手が残り、Readyの時間だけCPUの取り合いを待ってから再び実行される
run1["実行中"] -->|"待ちに入る(NewThreadStackに残る)"| waitst["待ち(Waits (us))"]
waitst -->|"誰かが起こす(ReadyingProcess / ReadyThreadStack)"| ready["Ready(CPUの取り合い待ち)"]
ready -->|"CPUに載る"| run2["再び実行"]
読み方の型はこうです。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 LR
accTitle: 待ち解析で辿るクリティカルパスの鎖
accDescr: 遅延したスレッドAのNewThreadStackで何をして止まったかを見て、ReadyThreadStackとReadyingProcessで起こした相手Bを特定し、Bも同じ手順で調べて根元のディスクI/Oまで辿る
a["スレッドA(遅延した操作)"] -->|"NewThreadStack: ロック待ちで停止"| b["スレッドB(ロック保持中)"]
b -->|"NewThreadStack: RPC応答待ち"| c["プロセスC"]
c -->|"NewThreadStack: 同期I/O待ち"| d["ディスクI/O(根元)"]
d -.->|"完了がCを起こす"| c
c -.->|"応答がBを起こす"| b
b -.->|"ロック解放がAを起こす(ReadyThreadStackに現れる)"| a
たとえば「マルチスレッド化したのに速くならない」案件では、この手順で1本のロックに全ワーカーが並んでいる様子がそのまま見えます。ロック競合を設計で避ける話は「マルチスレッドの実務ベストプラクティス .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の記録で、重要な列が2つあります。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のプリセットで、どのプロセスが・どのファイルに・どのスタックからI/Oを発行したかを、IO TimeやSizeの大きい順に見ます。15 現場でよく出る答えはこの2つです。
- ウイルス対策ソフトが全ファイルを舐めていた。アプリの起動が遅い時間帯に、対策ソフトのプロセスが大量の読み取りを発行している形で見えます。除外設定の相談材料として、プロセス名・パス・量の証拠がそのまま揃います。
- 別プロセスが大量書き込みをしていた。バックアップ、インデクサー、ログの書き過ぎなど。書き込みがいつディスクに届くかはキャッシュマネージャーが絡むため、「書いた瞬間」と「ディスクが忙しい瞬間」がずれることも含めて「キャッシュマネージャー ── あなたの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と非同期I/O ── OVERLAPPEDの本当の意味」をどうぞ。
flowchart TB
accTitle: File IOとDisk Usageが見る層の違い
accDescr: アプリのファイル操作はファイルシステムとフィルタドライバーを経てOSのIOキューからディスク装置に届く。File IOは上の層の操作を、Disk Usageはディスクに届いたIOを記録し、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 − Disk Service Time = 待ち行列の時間"]
dev -.-> n3["Disk Service Time = 装置の処理時間"]
なお「メモリ不足でスワップしているのでは」という筋は、WPAに進む前にタスクマネージャーとリソースモニターで一次切り分けできます。ただしコミット済みメモリだけ見て否定しないでください ── コミット済みに余裕があっても、物理メモリの逼迫でワーキングセットが削られてハードフォルトが続く状況はあり得ます。利用可能な物理メモリと、リソースモニターの「ハード フォールト/秒」も併せて確認します。読み方は「Windowsの「メモリ使用量」は何を表しているのか」を参照してください。
8. 起動・ログオンが遅い ── ブートトレースの入口
「起動に3分かかる」タイプは、手でwpr -startする前に問題が終わってしまいます。WPRにはブートトレースがあり、次回起動時にOSが自動で記録を開始するよう仕込めます。3
:: 1. 次回起動時の自動記録を仕込む
wpr -boottrace -addboot GeneralProfile -filemode
:: 2. 再起動する(起動の遅さを再現)
:: 3. 起動後、記録を止めて保存(仕込みも解除される)
mkdir C:\temp 2>nul
wpr -boottrace -stopboot C:\temp\boot.etl "起動に3分かかる事象"
flowchart LR
accTitle: ブートトレースの流れ
accDescr: addbootで次回起動時の自動記録を仕込んでから再起動すると、起動時にOSが自動で記録を開始する。ログオン後にstopbootで保存すると仕込みも解除される。やめる場合はcancelbootで解除する
add["wpr -boottrace -addboot で仕込む"] --> rebootpc["再起動(起動の遅さを再現)"]
rebootpc --> auto["起動時にOSが自動で記録開始"]
auto --> stop2["ログオン後に -stopboot で保存(仕込みも解除)"]
add -.-> cancel["やめるなら -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 LR
accTitle: 性能調査の反復ループ
accDescr: 現象の時刻を確定して区間にズームし、CPUか待ちかIOかを分類し、仮説を立てて絞り込み、スタックで裏を取る。裏が取れれば原因確定、外れたら次の仮説で繰り返す
time["現象の時刻を確定"] --> zoomstep["その区間にズーム"]
zoomstep --> triage["CPUか・待ちか・I/Oかを分類"]
triage --> hypo["仮説を立てて絞り込む"]
hypo --> stack["スタックで裏を取る"]
stack -->|"裏が取れた"| fix["原因確定 → 対策へ"]
stack -->|"外れた"| hypo
最後に、採取ファイルの取り扱いです。ETLファイルには、全プロセスの名前、開いたファイルのパス、読み込まれたモジュール、(プロファイルによっては)レジストリのキー名など、システムの内部が広く写っています。GeneralProfileの標準採取に通信内容のようなデータ本体は含まれませんが、カスタムプロバイダーを有効化した場合は、そのイベントのペイロード(アプリが記録した文字列など)がそのまま入ります。有効化したプロバイダーが何を出すかまで確認したうえで、社外に出すには十分に機密なファイルとして扱ってください。パケットキャプチャと同じく、必要最小限の採取・渡す相手との合意・保管期限と削除を手順に組み込んでください。
flowchart LR
accTitle: ETLファイルに写るものと取り扱い
accDescr: ETLには全プロセス名、開いたファイルのパス、モジュール、プロファイルによってはレジストリキー名が写り、カスタムプロバイダーを有効化するとそのペイロードも入る。機密ファイルとして必要最小限の採取、渡す相手との合意、保管期限と削除を手順化する
etl["ETLファイル"] --> a1["全プロセス名・ファイルパス・モジュール"]
etl --> a2["(プロファイルにより)レジストリキー名"]
etl --> a3["カスタムプロバイダーのペイロード(アプリが記録した文字列)"]
a1 -.-> rule["機密として扱う: 必要最小限の採取・相手との合意・保管期限と削除"]
a2 -.-> rule
a3 -.-> rule
10. まとめ
- タスクマネージャーで説明できない「PC全体が重い」は、OS全体のETWトレース──WPRで採り、WPAで読む──で調べます。wpr.exeはWindows 8.1以降のOS標準搭載なので、顧客環境で採ってETLを持ち帰り、手元のWPAで読む分担が成立します。
- 採取は
wpr -start GeneralProfile -filemode→再現→wpr -stop trace.etlの3手順。再現できるならFileモードで数分以内、待ち受けるならMemoryモード(リングバッファ)。長く採るほど良い、ではありません。 - WPAはテーブルの黄金律(金色バーの左=グルーピング)、時間範囲のズーム、シンボル設定(自社アプリはPDB)の3点を押さえれば読み始められます。
- CPUが高いならCPU Usage (Sampled)でプロセス→スタック→関数へ。CPUが低いのに遅いならCPU Usage (Precise)で、NewThreadStack(何をして止まったか)→Waits(どれだけ待ったか)→ReadyingProcess・ReadyThreadStack(誰が起こしたか)の鎖を根元まで辿ります。
- ディスクはDisk UsageのIO TimeとService Timeの差で「待ち行列にいた時間」を見抜き、その原因(装置自体が遅いのか、誰が列を作ったのか)はService Timeとプロセス・パス・スタック別の内訳で特定します。起動の遅さは
wpr -boottraceで採れます。 - 実務の型は、①時刻の確定 ②区間ズーム ③CPUか待ちかI/Oかの分類 ④仮説→ズーム→スタックの反復。ETLは内部情報を含む機密として扱います。
WPAは画面がいかつく、最初の1時間は誰でも迷子になります。しかし「金色バーの左がグルーピング」「Sampledは焼いた場所、Preciseは待った相手」という2つの背骨さえ入れば、あとは同じ操作の反復です。次に「CPUは余っているのに遅いんです」という相談が来たら、タスクマネージャーを閉じて、トレースを採ってみてください。
関連記事
- PerfViewとdotnet-traceで「遅い」を特定する ── .NETパフォーマンス調査の実務入門
- Process Monitor(ProcMon)実践ガイド ── 「設定が読まれない」「ACCESS DENIED」を10分で特定する
- Windowsイベントログ・ETW入門 ── 業務アプリのログをOS標準の仕組みに乗せる
- Windowsの「メモリ使用量」は何を表しているのか ── Working Set・Private Bytes・Commit・ページファイルを正しく読む
- Windowsのプロセッサのスケジュール設定 - バックグラウンドサービスとP/Eコア
- PDB(プログラムデータベース)とは何か ── デバッグ情報・シンボル・Source Linkを理解する
関連する相談領域
合同会社小村ソフトでは、「PC全体が重くなったが原因が分からない」「CPUに余裕があるのにアプリが遅い」「特定の環境だけ起動が極端に遅い」といったシステム全体の性能問題の調査を扱っています。WPR/WPAによる採取設計(どの環境で・どのプロファイルを・どれだけ採るか)からトレース解析、原因となったアプリ側・設定側の修正までを一続きで対応します。
参考リンク
-
Microsoft Learn, Introduction to WPR. WPRがETWベースの性能記録ツールであること、コマンドライン版のWPR.exeがWindows 8.1以降のOSに同梱され追加インストール不要であること、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等のOn/Off遷移の記録について。 ↩ ↩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で読む構成、待ち解析(Wait analysis)で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のマネージドシンボル用PDBをトレースの隣の.ngenpdbフォルダーに生成しWPAが自動参照することについて。 ↩ ↩2 ↩3
-
Microsoft Learn, Built-in Recording Profiles. WPRに組み込まれた記録プロファイルの一覧(CPU usage、Disk I/O activity、File I/O activity、Registry I/O activity、Networking I/O activityほか)と各プロファイルが記録する内容について。 ↩
-
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のエラーコードを読み解く ── Win32エラー・HRESULT・NTSTATUSの三層構造
0x80004005が出たら、検索の前にまず分解。Win32エラー・HRESULT・NTSTATUSの三層構造、0x8007xxxx=Win32エラーの包み直しという最重要パターン、err.exeやPowerShellでの調べ方まで解説します。
Windowsの「メモリ使用量」は何を表しているのか ── Working Set・Private Bytes・Commit・ページファイルを正しく読む
タスクマネージャーのメモリ、Working Set、Private Bytes、Commitは同じ値ではありません。Windowsの仮想メモリと物理メモリの関係、ページファイルの役割、メモリ不足やリーク調査で見るべき指標を解説します。
クリップボードとドラッグ&ドロップの仕組み ── OLEデータ転送を業務アプリで正しく扱う
Excelの表を貼ると崩れる・コピー元を閉じると貼れない――正体は同じ内容を複数形式で置くクリップボードの仕組みです。標準形式・遅延レンダリング・OLEドラッグ&ドロップから履歴とクラウド同期のポリシーまで解説します。
アプリから見たWindowsのシャットダウン ── 終了通知・再起動・電源断を正しく生き延びる
Windows Updateの夜間再起動で計測データが壊れた――そんな事故は設計で防げます。WM_QUERYENDSESSIONやPRESHUTDOWNによる終了通知の受け方、数秒で終える後始末、電源断に耐える保存設計までを一次情報で解説します。
OneDrive「ファイル オンデマンド」と業務アプリ ── プレースホルダーが壊す前提と対策
デスクトップのCSVが読めない、取込処理が「ファイルが見つかりません」で落ちる――原因はOneDriveのKFMとファイル オンデマンドかもしれません。プレースホルダーの仕組みと属性判定、アプリ・情シス双方の対策を解説します。
関連トピック
このテーマと近いトピックページです。記事を起点に、関連するサービスや他の記事へ進めます。
Windows技術トピック
Windows 開発、不具合調査、既存資産活用の技術トピックをまとめた入口です。
このテーマがつながるサービス
この記事は次のサービスページにつながります。近い入口からご覧ください。
Windowsアプリ開発
業務アプリ、装置連携、通信ツールなどの Windows ソフト開発を支援します。
よくある質問
この記事のテーマについて、相談時によくある質問をまとめています。
- WPRとWPAはどこで入手できますか?インストールできない顧客環境でも使えますか?
- 採取ツールのwpr.exe(コマンドライン版)はWindows 8.1以降のOSに標準搭載されており、追加インストールなしで使えます。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ファイルにはプロセス名・ファイルパス・実行ファイルの情報などシステムの内部情報が含まれるため、社外へ持ち出す際の取り扱い(最小化・保管期限・削除)もあらかじめ決めておくべきです。