概念 #システムパフォーマンス #Brendan Gregg #Linux #SRE #読書ノート #BPF #eBPF #bpftrace 📚 詳解 システム・パフォーマンス

詳解 システム・パフォーマンス - 第15章:BPF/eBPF

BPF(Berkeley Packet Filter)の仕組み・BCC tools完全リファレンス・bpftraceワンライナー集。次世代カーネルトレーシングの実践ガイド。

BPF/eBPF とは

**eBPF(extended Berkeley Packet Filter)**は Linux カーネル内でサンドボックス化されたプログラムを安全に実行する仕組み。

特徴

特徴説明
安全カーネルクラッシュを引き起こさない(検証器が事前チェック)
高性能カーネル内で実行されるため JIT コンパイル後は非常に高速
低オーバーヘッドデータ収集をカーネル内で完結(ユーザー空間への転送を最小化)
動的再起動なしでトレースポイントを追加・削除できる

必要な Linux バージョン

  • BCC tools: Linux 4.1 以降
  • bpftrace: Linux 4.9 以降
  • 多くの機能: Linux 5.x 以降で安定
# カーネルバージョン確認
uname -r

# BPF 機能の確認
bpftool feature probe

インストール

# Ubuntu/Debian
sudo apt-get install bpfcc-tools bpftrace linux-headers-$(uname -r)

# CentOS/RHEL 8
sudo yum install bcc-tools bpftrace

# 確認
ls /usr/share/bcc/tools/
bpftrace --version

BCC tools 完全リファレンス

CPU / スケジューラ

ツール目的コマンド例
execsnoop新しいプロセス実行の追跡sudo execsnoop
runqlatCPU 実行キュー待ち時間の分布sudo runqlat 1
runqlenCPU 実行キューの長さsudo runqlen 1
cpudistオンCPU時間の分布sudo cpudist
offcputimeオフCPU時間のスタックトレースsudo offcputime 10
profileCPU プロファイリングsudo profile -F 99 10
# CPU実行キューの待ち時間(スケジューリングレイテンシ)
sudo runqlat 1
# usecs   : count  distribution
#   0 -> 1 : 9000  |*********  |
#   2 -> 3 : 1000  |*          |
# 64 -> 127:    5  |           | ← 遅いスケジューリング

# CPU オフタイム(何を待っているか)
sudo offcputime 10 | ./FlameGraph/flamegraph.pl --colors=blue > offcpu.svg

メモリ

ツール目的コマンド例
memleakメモリリーク検出sudo memleak -p <PID>
slabratetopカーネルスラブ割り当てsudo slabratetop
oomkillOOM Killer 発動の追跡sudo oomkill
drsnoopDirect Reclaim のトレースsudo drsnoop
vmscanVM スキャン・ページ回収sudo vmscan
# メモリリーク検出(30秒追跡)
sudo memleak -p <PID> -a 10 -t 30
# 出力:割り当てスタックごとの未解放バイト数

# OOM Killer 監視(リアルタイム)
sudo oomkill
# TIME   PID    UID  PCOMM        MEM  SCORE  OCOMM
# 12:00  1234     0  java       512MB   900  java   ← 殺されたプロセス

ディスク I/O

ツール目的コマンド例
biolatencyブロックI/Oレイテンシ分布sudo biolatency 1
biosnoopブロックI/Oイベントのトレースsudo biosnoop
biotopディスクI/OのTOP表示sudo biotop 1
bitesizeI/Oサイズ分布sudo bitesize
seeksizeディスクシーク距離sudo seeksize
filetopファイル別I/Oの上位sudo filetop 1
filelifeファイルの生存時間sudo filelife
# ディスクI/Oレイテンシ分布(5秒間)
sudo biolatency 5
# usecs   : count  distribution
#   8->15  : 500   |**********   |
#  16->31  : 200   |****         |
# 512->1023:   5   |             | ← 外れ値

# I/Oが多いファイルTop10
sudo filetop 1
# PID  COMM   READS  WRITES  R_Kb  W_Kb FILE
# 1234 mysql   100     50   400  200  ibdata1

ファイルシステム

ツール目的コマンド例
opensnoopファイルオープンの追跡sudo opensnoop
statsnoopstat() システムコールsudo statsnoop
vfsstatVFS 操作統計sudo vfsstat 1
vfscountVFS 関数の呼び出し回数sudo vfscount
cachestatページキャッシュヒット率sudo cachestat 1
cachetopプロセス別キャッシュ状況sudo cachetop 1
ext4slowerext4 の遅い操作を検出sudo ext4slower 10
xfsslowerXFS の遅い操作を検出sudo xfsslower 10

ネットワーク

ツール目的コマンド例
tcpconnectTCP 接続開始の追跡sudo tcpconnect
tcpacceptTCP 接続受け入れの追跡sudo tcpaccept
tcpretransTCPリトランスミッションsudo tcpretrans
tcplifeTCP セッション詳細ログsudo tcplife
tcptopTCP 送受信量の上位sudo tcptop 1
tcptracerTCP 状態変化の追跡sudo tcptracer
udpconnectUDP 接続の追跡sudo udpconnect
# TCP セッションの詳細ログ(接続時間・データ量)
sudo tcplife
# PID  COMM    LADDR      LPORT RADDR       RPORT TX_KB RX_KB MS
# 1234 nginx   0.0.0.0    80    10.0.0.5    54321   0.5  10.2 25.3

# リトランスミッション検出(パケットロスの証拠)
sudo tcpretrans
# TIME     PID  IP  LADDR:LPORT    RADDR:RPORT    STATE   TYPE
# 12:00:01 1234  4  10.0.0.1:8080  10.0.0.5:12345 ESTAB   R

言語別ツール

言語ツール用途
Javajavacallsメソッド呼び出しの追跡
PythonpythoncallsPython 関数呼び出しの追跡
Node.jsnodegcGC イベントの追跡
RubyrubycallsRuby メソッドの追跡

bpftrace:ワンライナーとスクリプト

基本文法

bpftrace -e 'probe { action }'

probe:
  kprobe:関数名          → カーネル関数の入口
  kretprobe:関数名        → カーネル関数の出口
  tracepoint:カテゴリ:名前 → 静的トレースポイント
  uprobe:パス:関数名      → ユーザー空間関数
  profile:hz:N           → タイマーサンプリング(N Hz)
  software:イベント:N    → ソフトウェアイベント

変数:
  pid / tid / uid         → プロセスID / スレッドID / ユーザーID
  comm                    → コマンド名
  nsecs                   → ナノ秒タイムスタンプ
  kstack / ustack         → カーネル/ユーザースタックトレース
  args.引数名             → トレースポイントの引数

アクション:
  printf()                → 出力
  @map[key] = count()     → カウンタ
  @map[key] = sum(val)    → 合計
  @map[key] = hist(val)   → ヒストグラム
  @map[key] = lhist(val, min, max, step) → 線形ヒストグラム

ワンライナー集

# ── プロセス・syscall ──

# ファイルオープンを追跡(プロセス名とパス)
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_openat { printf("%-6d %-16s %s\n", pid, comm, str(args.filename)); }'

# syscall の呼び出し回数を集計(10秒)
sudo bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @calls[comm] = count(); }' -c "sleep 10"

# exec の追跡(何が起動しているか)
sudo bpftrace -e 'tracepoint:sched:sched_process_exec { printf("%-6d %s\n", pid, str(args.filename)); }'

# ── CPU ──

# CPU スタックサンプリング(99Hz、10秒)
sudo bpftrace -e 'profile:hz:99 { @[kstack] = count(); }' -c "sleep 10" | head -30

# オフCPU時間(I/O・ロック待ち)のスタック
sudo bpftrace -e '
  tracepoint:sched:sched_switch { @start[tid] = nsecs; }
  tracepoint:sched:sched_switch /@start[tid]/ {
    @offcpu[kstack] = sum(nsecs - @start[tid]);
    @start[tid] = 0;
  }
' -c "sleep 10"

# ── メモリ ──

# malloc 呼び出しのサイズ分布
sudo bpftrace -e 'uprobe:/lib/x86_64-linux-gnu/libc.so.6:malloc { @sizes = hist(arg0); }'

# ── ディスク I/O ──

# ブロックI/Oのサイズ分布
sudo bpftrace -e 'tracepoint:block:block_rq_issue { @bytes = hist(args.bytes); }'

# ブロックI/Oのレイテンシ(usec単位)
sudo bpftrace -e '
  tracepoint:block:block_rq_issue { @start[args.dev, args.sector] = nsecs; }
  tracepoint:block:block_rq_complete /@start[args.dev, args.sector]/ {
    @latency_us = hist((nsecs - @start[args.dev, args.sector]) / 1000);
    delete(@start[args.dev, args.sector]);
  }
'

# ── ネットワーク ──

# TCP 接続先を集計(10秒)
sudo bpftrace -e '
  tracepoint:syscalls:sys_enter_connect {
    @connections[pid, comm] = count();
  }
' -c "sleep 10"

# ── レイテンシ計測 ──

# read() システムコールのレイテンシ分布
sudo bpftrace -e '
  tracepoint:syscalls:sys_enter_read { @start[tid] = nsecs; }
  tracepoint:syscalls:sys_exit_read /@start[tid]/ {
    @read_latency_us = hist((nsecs - @start[tid]) / 1000);
    delete(@start[tid]);
  }
'

# 特定プロセスの関数レイテンシ(uprobe)
sudo bpftrace -e '
  uprobe:/path/to/myapp:my_function { @start[tid] = nsecs; }
  uretprobe:/path/to/myapp:my_function /@start[tid]/ {
    @latency = hist(nsecs - @start[tid]);
    delete(@start[tid]);
  }
'

bpftrace スクリプトの例

複数イベントを組み合わせた高度なトレース。

# ディスクI/Oのレイテンシを 10ms 以上のもののみ表示
cat > /tmp/slowdisk.bt << 'EOF'
tracepoint:block:block_rq_issue
{
    @start[args.dev, args.sector] = nsecs;
}

tracepoint:block:block_rq_complete
/@start[args.dev, args.sector]/
{
    $lat = (nsecs - @start[args.dev, args.sector]) / 1000000; // ms
    if ($lat > 10) {
        printf("SLOW I/O: dev=%d sector=%d lat=%dms rw=%d size=%d\n",
            args.dev, args.sector, $lat, args.rwbs, args.bytes);
    }
    delete(@start[args.dev, args.sector]);
}
EOF

sudo bpftrace /tmp/slowdisk.bt

BPF パフォーマンスツールのエコシステム

bpftrace
├── ワンライナー(即時実行)
├── スクリプト(複雑なロジック)
└── .bt ファイル

BCC(BPF Compiler Collection)
├── Python + C でのツール開発
├── /usr/share/bcc/tools/ の既製ツール群
└── libbcc で独自ツール開発

bpftool(管理・デバッグ)
├── bpftool prog list → 実行中のBPFプログラム一覧
├── bpftool map list → BPF マップ一覧
└── bpftool feature probe → BPF機能の確認

出典: 詳解 システム・パフォーマンス 第2版 Brendan Gregg著