# Off-CPU Analysis
[[Brendan Gregg]] が体系化した、スレッドが CPU 上で実行していない時間を計測し、その待機理由をスタックトレースと結び付けて調べるパフォーマンス分析メソドロジである。I/O・ロック・タイマー・ページング・スワッピングだけでなく、CPU 需要が高いときの非自発的コンテキストスイッチや割り込みも対象になる。
## on-CPU分析との補完関係
CPU サンプリングは実行中の命令やスタックを観測するため、ブロッキングシステムコールの後にスレッドが待機している区間を捕捉しない。アプリケーション関数を開始・終了時刻で計測するトレーシングは経過時間と CPU 時間を分離できるが、全関数を計測すれば高コストになり、ブロックしそうな関数だけを選べば未知の待機経路を見逃す。
off-CPU トレーシングは、スレッドを CPU から外すカーネルスケジューラのイベントと、待機終了時のユーザー・カーネルスタックを記録する。アプリケーションが MySQL、Apache、Java など何であっても、ブロック箇所をあらかじめ列挙せずに捕捉できる。off-CPU サンプリングは全スレッドを定期的に調べる方式であり、一般的な CPU サンプリングよりスレッド数の影響を強く受ける。
![[_attachments/offcpu-analysis/fig01-thread-states.png]]
(スレッド状態の遷移。on-CPU のユーザー・カーネル状態から、実行可能、スワッピング、ディスク、スリープ、ロック、アイドルなどの off-CPU 状態へ移り、ウェイクアップ後に実行可能状態へ戻る。)
## オーバーヘッド
スケジューライベントは極めて頻繁で、極端な場合は毎秒数百万件に達する。全イベントをユーザー空間へ出力すると、トレースデータの書き込み・シンボル変換・後処理が本番ワークロードへ影響するため、カーネル内でスタックと時間を集約する方式が重要になる。トレーサー自身が発生させたイベントを計測するフィードバックにも注意が要る。
記事の 8 CPU Linux 4.15・高負荷 MySQL 実験では、毎秒 102,000 回のコンテキストスイッチに対して、perf で全スケジューライベントを記録するとスループットが計測中に 9% 低下し、10 秒の `perf.data` は 224 MB、後処理を含む影響は 45 秒に及んだ。eBPF でカーネル内集約すると、計測中の低下は初期化時を除き 6%で、後処理を含む影響は 17 秒だった。60 秒へ延長しても eBPF の後処理は 6 秒から 7 秒への増加にとどまった一方、perf はデータ量に比例して 35 秒から 212 秒へ増加した。
実システムでは、まず 0.1 秒程度の短い計測から始め、CPU 使用率・要求レート・要求レイテンシ・コンテキストスイッチ率を同時に確認して計測時間を伸ばすべきである。
## Linuxでの計測
Linux では eBPF を BCC フロントエンドから利用できる。`cpudist -O` はスレッドがブロックしてから再び実行されるまでの off-CPU 時間をヒストグラム化する。`offcputime` はスケジューラのコンテキストスイッチを利用して、待機時間をユーザー・カーネルスタック別に集計する。記事の実装例は Linux 4.8 以降のスタックトレース取得を前提とする。
```text
on context switch finish:
sleeptime[prev_thread_id] = timestamp
if !sleeptime[thread_id]
return
delta = timestamp - sleeptime[thread_id]
totaltime[pid, execname, user stack, kernel stack] += delta
sleeptime[thread_id] = 0
```
アプリケーションスタックは off-CPU 中には変化しないため、待機の開始時または終了時に一度取得すればよい。待機終了時は待機時間の計算と同じ計測点でスタックを取得できるため実装しやすい。`finish_task_switch()` のような次に実行されるスレッドの文脈で呼ばれるカーネル関数を kprobe し、eBPF map にヒストグラムやスタック別合計を保持する。
`tar` の例では、`io_schedule()` に至る `read` 系スタックがディスク I/O 待ち、`xfs_buf_submit_wait()` に至る `stat` 系スタックがファイルシステムのストレージ待ち、`xfs_buf_lock()` に至る `getdents` 系スタックがロック待ちを示した。ユーザースタックが `[unknown]` になる場合は、フレームポインタ省略コンパイルや JIT シンボル未登録を疑う必要がある。
## 要求と同期したコンテキスト
待機中のワーカースレッドやバックグラウンドスレッドが多いサーバーでは、仕事の到着を待つスリープやタイマーが出力の大半を占める。ゼロ要求の MySQL でも、スリープ・futex・非同期 I/O 完了待ちのスレッドが大量に観測される。
調査対象は、顧客要求やデータベースクエリーの処理中に発生した同期的な待機である。MySQL では `do_command()` から `mysql_execute_command()` へ続く要求処理スタックを目印にできる。後処理で該当関数を含むスタックだけを抽出する方法のほか、要求処理関数を追加計測してその区間の off-CPU 時間だけを記録する方法もある。
## 解釈上の注意
off-CPU 時間には、ブロック解除後に CPU を割り当てられるまでのスケジューラレイテンシも含まれる。CPU 飽和が既知なら、別のトレースイベントでブロック時間とランキュー待ちを分離する価値がある。
ユーザースタックがブロッキング理由を示さない場合は、CPU 飽和による非自発的コンテキストスイッチの可能性がある。Linux では通常、関心のある待機は `TASK_INTERRUPTIBLE` または `TASK_UNINTERRUPTIBLE` であり、`offcputime --state 2` のように `TASK_UNINTERRUPTIBLE` に絞ることで、実行中スレッドがタイムスライスを使い切っただけのイベントを除外できる。
## off-CPUフレームグラフ
大量のスタック出力を理解するには、BCC `offcputime -f` の折りたたみ形式を [[FlameGraph]] の `flamegraph.pl` へ渡す。縦方向はスタック深度、横幅は各スタックの合計 off-CPU 時間を表す。横方向の並び順には時間的意味はなく、クリックによるズームや関数名検索で要求処理へ絞り込める。
![[_attachments/offcpu-analysis/fig02-offcpu-flamegraph.png]]
(MySQL の全 off-CPU スタックを、待機時間の幅で表したフレームグラフ。青色の幅広い経路がスケジューラから I/O・要求処理へ続く。)
要求処理を示す `do_command` を含む行だけを抽出してから再描画すれば、顧客要求のレイテンシに関係する待機経路へズームできる。
![[_attachments/offcpu-analysis/fig03-query-filtered-flamegraph.png]]
(`do_command` を含む MySQL の要求同期スタックに絞ったフレームグラフ。要求処理からファイル読み出し・クエリー実行の待機経路を追える。)
## ウェイクアップと他のOS
ブロックされたスレッドのスタックは「何を待っていたか」を示すが、「誰が起こしたか」は別スレッドのスタックにある場合が多い。`wakeuptime` はウェイクアップ側のスタックを、`offwaketime` はウェイクアップ側と off-CPU 側の関係を分析する。
Solaris では DTrace の `sched:::off-cpu` と `sched:::on-cpu`、FreeBSD では `procstat -ka` によるカーネルスタックサンプリングや DTrace を利用できる。手法自体は特定 OS や特定のブロッキング種別に依存しない。
## 方法論の起源と要約
Gregg は 2005 年頃、DTrace の sched provider と `sched:::off-cpu` probe を調べる過程でこの方法論を使い始め、2007 年に「DTracing Off-CPU Time」として発表した。2017 年時点では eBPF と新しい Linux カーネルによって、従来より本番 Linux で実施しやすくなったと整理している。
off-CPU 分析は、スレッドが他のイベントを待つことで生じるレイテンシを、待機時間とユーザー・カーネルスタックの組み合わせで箇所特定する。CPU プロファイリングが on-CPU 時間を、off-CPU 分析が待機時間を扱うため、両者を組み合わせることでスレッド時間の全体像を把握できる。
## 関連
- エンティティ: [[Brendan Gregg]] / [[BCC]] / [[perf]] / [[FlameGraph]] / [[SystemTap]] / [[MySQL]]
- 概念: [[スレッド状態分析]] / [[フレームグラフ]] / [[eBPF]] / [[コンテキストスイッチ]] / [[スケジューラレイテンシ]] / [[トレーシングオーバーヘッド]] / [[レイテンシ分析]]
## 出典
- 原文: https://www.brendangregg.com/offcpuanalysis.html
- Raw: `.raw/articles/offcpu-analysis-2026-08-30.md`
- 図: `Perf/thread_states.png`、`FlameGraphs/off-mysqld1.png`、`FlameGraphs/off-mysqld2.png`