【図解】Linux perfの使い方|CPU性能解析・Hot Function・Call Graphを徹底解説

はじめに

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 statPerformance Counterの集計cycles、instructions、cache-missesなど
perf topリアルタイムSamplingCPU上で多くSamplingされているFunction
perf recordSampling 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全体に占める割合は次のようになります。

FunctionSample数割合
Function A5050%
Function B3030%
Function C2020%

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 --version

perf statでCPU処理の特徴を確認する

perf statは、指定したCommandやProcessについてPerformance Counterを集計するために使用します。

Commandを指定して計測する

$ perf stat <command>

たとえばsarを実行して計測した例は以下の通りです。

項目意味見るポイント
task-clockCPU上で実行された時間CPUをどれくらい使用したか
context-switchesContext Switchの回数Process / Threadの切り替え頻度
cpu-migrations実行CPUが別のCPUへ移動した回数CPU間の移動頻度
page-faultsPage Faultの発生回数Memory Mappingの発生状況
cyclesCPU Cycle数CPUが処理に使ったCycle数
instructions実行したInstruction数cyclesと合わせてCPU実行効率を見る
branchesBranch命令の実行回数分岐処理の量
branch-missesBranch 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 10

10秒間の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を確認できます。

項目今回の例意味・見るポイント
Overhead7.20%取得したSample全体のうち、そのSymbolに対応するSampleが占める割合。値が高いFunctionを確認する。
Shared Object[kernel]FunctionがどのObjectに属するか。[kernel]ならKernel。
Symbolclear_page_ermsSampling時に実行されていた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 report

Call 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 30

CPU高負荷が発生している30秒間のSampling Dataを取得します。

⑥ perf reportで原因を深掘りする

# perf report

Overhead、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です。

  1. top / sar:CPU全体の状態を確認する。
  2. pidstat / top -H:CPUを使用しているProcess / Threadを特定する。
  3. perf stat:Performance CounterからCPU処理の特徴を確認する。
  4. perf top:CPU時間が集中しているHot Functionをリアルタイムに確認する。
  5. perf record:障害発生中のSampling DataをEvidenceとして保存する。
  6. perf report:Hotspot、Shared Object、Symbolを解析する。
  7. Call Graph:Functionの呼び出し関係を確認し、CPU負荷につながる処理経路を追う。

CPUトラブルシューティングでは、CPU使用率が高いことを確認するだけでなく、CPU全体 → Process → Thread → Function → Call Graphと段階的に原因を深掘りすることが重要です。

perfを単独で使うのではなく、top、sar、pidstat、Application Logなどで原因を絞り込んだうえで利用することで、CPU高負荷の原因をより効率的に特定できます。

コメント