はじめに
LinuxでCPU高負荷が発生した場合、topやsarを使うことでCPU全体の状態を確認し、pidstatやtop -Hを使うことでCPUを多く使用しているProcessやThreadを特定できます。
しかし、ProcessやThreadを特定できても、「そのProcessが具体的にどの処理でCPUを使っているのか」までは分からないことがあります。
たとえば、Java ProcessがCPUを90%使用していることが分かっても、それだけではApplicationのどのFunctionがCPUを消費しているのか、User SpaceとKernel Spaceのどちらで処理時間を使っているのかまでは判断できません。
このような場合に利用できるのがperfです。perfを利用すると、CPU上で実行されている処理をSamplingし、CPU上で多くSamplingされているFunctionやCall Stackを確認できます。
本記事では、Linuxトラブルシューティング連載の第6回として、perf stat、perf top、perf record、perf reportを使い、CPU高負荷をProcess / ThreadからFunctionまで深掘りする方法を解説します。
perfとは
perfは、Linux Kernelが提供するPerformance Monitoring機能を利用して、CPUやSoftware Eventなどを観測するPerformance Analysis Toolです。
topやpidstatが「どのProcess / ThreadがCPUを使用しているか」を確認するのに対して、perfでは「そのProcess / ThreadがどのFunctionを実行しているか」まで深掘りできます。

perfでは、CPU CycleやInstructionなどのHardware Event、Context SwitchやPage FaultなどのSoftware Event、Function単位のSampling、Call Graphなどを確認できます。
| perf機能 | 主な用途 | 見るポイント |
|---|---|---|
| perf stat | Performance Counterの集計 | cycles、instructions、cache-missesなど |
| perf top | リアルタイムSampling | CPU上で多くSamplingされているFunction |
| perf record | Sampling Dataを保存 | 障害発生中のEvidence取得 |
| perf report | 保存したDataを解析 | Hotspot、Symbol、Call Graph |
perfのSamplingの仕組み
perfを理解するうえで重要なのが、Samplingという考え方です。
perfはCPUのすべての処理を常時記録するのではなく、一定のEventやFrequencyを基準にCPUの実行状態をSamplingします。
Samplingの契機には、cpu-clockのような時間ベースのEventや、cyclesなどのHardware Eventを利用できます。

Sampling時に何を取得するのか
Samplingが発生すると、perfはその時点でCPUが実行していた命令の位置であるInstruction Pointerなどの情報を記録します。
Instruction PointerはMemory Addressなので、そのままではどのFunctionを実行していたのか分かりません。perfはKernelやApplication、LibraryなどのSymbol情報を利用して、AddressをFunction名へ変換します。
この処理を繰り返すことで、「どのFunctionを実行しているときにSampleが多く取得されたか」を確認できます。
Overheadはどのように計算されるのか
たとえば、perfがCPUを100回Samplingし、次のような結果になったとします。
全Sample:100
Function A:50 Sample
Function B:30 Sample
Function C:20 Sampleこの場合、Sample全体に占める割合は次のようになります。
| Function | Sample数 | 割合 |
|---|---|---|
| Function A | 50 | 50% |
| Function B | 30 | 30% |
| Function C | 20 | 20% |
perf topなどに表示されるOverheadは、このようにSample全体のうち、そのSymbolに対応するSampleがどの程度を占めているかを見るための値です。
たとえばOverheadが7.2%の場合、「CPU使用率が7.2%」という意味ではなく、取得したSampleのうち約7.2%がそのSymbolに対応していたと理解します。
Call GraphではCall Stackも取得する
perf record -gを使用すると、Sampling時のFunctionだけでなくCall Stackも記録し、Functionの呼び出し経路を解析できます。
Sample
↓
Instruction Pointer
+
Call Stack
↓
main
└─ Function A
└─ Function B
└─ Hot Functionこれにより、Hot Functionを特定するだけでなく、「どの処理から呼び出されて、そのFunctionに到達したのか」まで確認できます。
つまりperfによるCPU解析では、Sampling → Instruction Pointerの取得 → Symbolの解決 → Sampleの集計によってHot Functionを探し、必要に応じてCall Stackから処理経路を深掘りします。
perfによる性能解析の全体像
perfは、CPU高負荷調査の最初に実行するToolではありません。まずtop、sar、pidstatなどでCPU高負荷のProcess / Threadを特定し、さらに深掘りが必要な場合にperfを利用します。

重要なのは、perf単体で原因を決めるのではなく、top、pidstat、Application Logなどで得た情報と組み合わせて仮説を検証することです。
perfをインストールする
RHEL / AlmaLinux 9系では、perf Packageをインストールして利用します。
# dnf install perfインストール後、Versionを確認します。
# perf --versionperf statでCPU処理の特徴を確認する
perf statは、指定したCommandやProcessについてPerformance Counterを集計するために使用します。
Commandを指定して計測する
$ perf stat <command>たとえばsarを実行して計測した例は以下の通りです。

| 項目 | 意味 | 見るポイント |
|---|---|---|
| task-clock | CPU上で実行された時間 | CPUをどれくらい使用したか |
| context-switches | Context Switchの回数 | Process / Threadの切り替え頻度 |
| cpu-migrations | 実行CPUが別のCPUへ移動した回数 | CPU間の移動頻度 |
| page-faults | Page Faultの発生回数 | Memory Mappingの発生状況 |
| cycles | CPU Cycle数 | CPUが処理に使ったCycle数 |
| instructions | 実行したInstruction数 | cyclesと合わせてCPU実行効率を見る |
| branches | Branch命令の実行回数 | 分岐処理の量 |
| branch-misses | Branch Predictionに失敗した回数 | Branch Missの多さを確認 |
これらの値には「この値を超えたら異常」という共通のThresholdがあるわけではありません。CPU ArchitectureやApplicationのWorkloadによって大きく異なるため、通常時との比較や同一処理間の比較が重要です。
また、Virtual MachineなどではHardware Performance Counterが利用できない、または一部Eventを取得できない場合があります。その場合は取得可能なSoftware Eventや他のToolを組み合わせて調査します。
既存Processを計測する
すでに稼働しているProcessを一定時間計測する場合は、PIDを指定します。
# perf stat -p <PID> -- sleep 1010秒間のPerformance Counterを取得し、対象ProcessのCPU処理の特徴を確認できます。
perf topでHot Functionを確認する
perf topは、CPU上で実行されているFunctionをSamplingし、CPU上で多くSamplingされているFunctionをリアルタイムで確認するために使用します。
# perf top特定Processに絞る場合はPIDを指定します。
# perf top -p <PID>
perfコマンドの表示例:

これはperf topのリアルタイム表示です。CPU上でどのFunction(Symbol)がSamplingされたかを集計し、Sampleの割合が高いFunctionを確認できます。
| 項目 | 今回の例 | 意味・見るポイント |
|---|---|---|
Overhead | 7.20% | 取得したSample全体のうち、そのSymbolに対応するSampleが占める割合。値が高いFunctionを確認する。 |
Shared Object | [kernel] | FunctionがどのObjectに属するか。[kernel]ならKernel。 |
Symbol | clear_page_erms | Sampling時に実行されていたFunction名。[k]はKernel側、[.]はUser Space側のFunctionを示す。 |
Overheadが高いFunctionはCPU Hotspotの候補ですが、Overheadだけで障害原因と断定することはできません。Applicationの仕様やCall Graph、通常時との違いを確認して原因を判断します。
perf recordでSampling Dataを取得する
perf topはリアルタイム確認に向いていますが、障害解析ではEvidenceとしてSampling Dataを保存し、後から解析したい場合があります。その場合はperf recordを使用します。
Command実行全体を取得する
特定Commandの実行を最初から最後まで計測する場合は、Commandを直接指定します。
# perf record -g <command>本番障害では、CPU高負荷が再現している短い時間帯に取得し、障害時のEvidenceとして残す使い方が有効です。
特定Processを一定時間取得する
# perf record -p <PID> -g -- sleep 30上記では対象Processを30秒間Samplingし、Call Graph情報も取得します。取得結果は通常perf.dataとして保存されます。

perf reportでHotspotを解析する
perf recordで取得したperf.dataは、perf reportで解析します。
# perf report
perf reportでは、どのCommand、Shared Object、SymbolにSampleが集中しているかを確認します。
Call Graphから処理の流れを追う
Hot Functionが分かっても、そのFunctionがなぜ頻繁に呼ばれているのか分からないことがあります。その場合はCall Graphを確認します。
# perf record -g -p <PID> -- sleep 30
# perf reportCall Graphを確認することで、Function単体だけでなく、どの処理から呼び出されているのかを追跡できます。

perf reportで対象SymbolのCall Graphを展開すると、Hot Function単体だけでなく、Functionの呼び出し経路を確認できます。上記例では、futex待機に関連するSystem Callから、Kernelのfutex_wait、scheduleなどへ至るCall Chainを確認できます。
perfを使ったCPU高負荷のトラブルシューティング例
ここでは、Web ApplicationのResponse Timeが悪化し、Linux ServerのCPU使用率が高騰したケースを例に、perfを使った調査の流れを整理します。
① CPU高負荷を確認する
まずtopやsarでCPU全体の状態を確認します。
# top
# sar -u 1%userが大きく上昇しており、Application処理によるCPU高負荷が疑われたとします。
② CPUを使用しているProcessを特定する
# pidstat -u 1
# top調査の結果、Application ProcessがCPUを多く使用していることを確認します。
③ Threadまで絞り込む
# top -H -p <PID>
# pidstat -t -u -p <PID> 1特定ThreadにCPU使用率が集中していることを確認します。
④ perf topでHot Functionを確認する
# perf top -p <PID>ここで特定のFunctionにSampleが集中していることを確認します。
⑤ perf recordで障害時のEvidenceを取得する
# perf record -p <PID> -g -- sleep 30CPU高負荷が発生している30秒間のSampling Dataを取得します。
⑥ perf reportで原因を深掘りする
# perf reportOverhead、Symbol、Call Graphを確認し、どの処理経路にCPU Sampleが集中しているかを確認します。
このように、perfはtopやpidstatの代わりではなく、Process / Threadを特定した後にFunction Levelまで原因を掘り下げるために利用します。
perfを本番環境で使用する際の注意点
perfは非常に強力なToolですが、本番環境で利用する場合は負荷やSecurityへの影響を考慮する必要があります。
取得時間を限定する
perf recordを長時間実行すると、Sampling処理によるOverheadやperf.dataの容量増加につながります。障害が発生している時間帯を狙い、まずは短時間で取得します。
Sampling Frequencyに注意する
Sampling Frequencyを高くすると詳細なDataを取得しやすくなる一方、SystemへのOverheadも増加します。本番環境では必要以上に高いFrequencyを設定しないようにします。
perf.dataの容量を確認する
取得時間やEvent数、Sampling Frequencyによってはperf.dataが大きくなることがあります。Filesystemの空き容量を確認してから取得します。
Symbol情報を確認する
Symbol情報が不足していると、perf reportでFunction名を十分に解決できません。必要に応じてdebuginfoやApplication BinaryのBuild条件を確認します。
Virtual MachineではPMUが利用できない場合がある
Virtual Machineでは、Hypervisor設定やVirtual CPUの構成によってHardware PMUがGuest OSへ公開されていない場合があります。その場合、cyclesやinstructionsなど一部Hardware Eventを取得できないことがあります。
Hardware Eventが取得できない場合でも、利用可能なSoftware EventやTracepointを確認し、top、pidstat、strace、Application Profilerなど他のToolと組み合わせて調査します。
Evidence取得前に対象を絞る
System全体を無条件に長時間Samplingするのではなく、topやpidstatでProcess / Threadを絞り込んでからperfを利用すると、取得Dataと負荷を抑えやすくなります。
まとめ
perfは、LinuxのCPU高負荷をFunction Levelまで深掘りするためのPerformance Analysis Toolです。
- top / sar:CPU全体の状態を確認する。
- pidstat / top -H:CPUを使用しているProcess / Threadを特定する。
- perf stat:Performance CounterからCPU処理の特徴を確認する。
- perf top:CPU時間が集中しているHot Functionをリアルタイムに確認する。
- perf record:障害発生中のSampling DataをEvidenceとして保存する。
- perf report:Hotspot、Shared Object、Symbolを解析する。
- Call Graph:Functionの呼び出し関係を確認し、CPU負荷につながる処理経路を追う。
CPUトラブルシューティングでは、CPU使用率が高いことを確認するだけでなく、CPU全体 → Process → Thread → Function → Call Graphと段階的に原因を深掘りすることが重要です。
perfを単独で使うのではなく、top、sar、pidstat、Application Logなどで原因を絞り込んだうえで利用することで、CPU高負荷の原因をより効率的に特定できます。


コメント