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
CPU / スケジューラ
| ツール | 目的 | コマンド例 |
|---|
execsnoop | 新しいプロセス実行の追跡 | sudo execsnoop |
runqlat | CPU 実行キュー待ち時間の分布 | sudo runqlat 1 |
runqlen | CPU 実行キューの長さ | sudo runqlen 1 |
cpudist | オンCPU時間の分布 | sudo cpudist |
offcputime | オフCPU時間のスタックトレース | sudo offcputime 10 |
profile | CPU プロファイリング | 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 |
oomkill | OOM Killer 発動の追跡 | sudo oomkill |
drsnoop | Direct Reclaim のトレース | sudo drsnoop |
vmscan | VM スキャン・ページ回収 | 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 |
bitesize | I/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 |
statsnoop | stat() システムコール | sudo statsnoop |
vfsstat | VFS 操作統計 | sudo vfsstat 1 |
vfscount | VFS 関数の呼び出し回数 | sudo vfscount |
cachestat | ページキャッシュヒット率 | sudo cachestat 1 |
cachetop | プロセス別キャッシュ状況 | sudo cachetop 1 |
ext4slower | ext4 の遅い操作を検出 | sudo ext4slower 10 |
xfsslower | XFS の遅い操作を検出 | sudo xfsslower 10 |
ネットワーク
| ツール | 目的 | コマンド例 |
|---|
tcpconnect | TCP 接続開始の追跡 | sudo tcpconnect |
tcpaccept | TCP 接続受け入れの追跡 | sudo tcpaccept |
tcpretrans | TCPリトランスミッション | sudo tcpretrans |
tcplife | TCP セッション詳細ログ | sudo tcplife |
tcptop | TCP 送受信量の上位 | sudo tcptop 1 |
tcptracer | TCP 状態変化の追跡 | sudo tcptracer |
udpconnect | UDP 接続の追跡 | 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
言語別ツール
| 言語 | ツール | 用途 |
|---|
| Java | javacalls | メソッド呼び出しの追跡 |
| Python | pythoncalls | Python 関数呼び出しの追跡 |
| Node.js | nodegc | GC イベントの追跡 |
| Ruby | rubycalls | Ruby メソッドの追跡 |
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機能の確認