0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

eBPFについて学ぶ③ - eBPFツール

0
Last updated at Posted at 2026-02-24

前回→eBPFについて学ぶ② - eBPFマップ
次回→eBPFについて学ぶ④ - eBPF検証器

(https://qiita.com/yamada3/items/f16f7f0fc54b9d7a338a)
前回の続きなので、参照するとわかりやすいです。
eBPF Beginner Skill Pathを日本語訳しながら再度まとめているため、同時にサイトを見ながら進めるとわかりやすいです。
公式GitHubリポジトリ:GitHub - dorkamotorka/ebpf-hello-world: eBPF Hello World Program using ebpf-go framework

シリーズ
#1→eBPFについて学ぶ① - 基本
#2→eBPFについて学ぶ② - eBPFマップ
#3→[いまここ]
#4→eBPFについて学ぶ④ - eBPF検証器


eBPFツールの「bpftool」と「bpftop」を使用し、eBPFプログラムを検査・分析・監視を行い、カーネル内部での動作やパフォーマンスを可視化する。

ざっくり
bpftool: カーネル内のeBPFプログラムとマップを検査する
bpftop: eBPFプログラムをリアルタイムでモニタリングする

前回からhello.c, main.goに変更はありません。同様にアプリケーションを構築・実行します

$ go generate
$ go build
$ sudo ./lab3

bpftool

プログラムとマップがカーネルに正しくロードされているか、プログラムがいつ、誰によってロードされたかを確認できる。また、マップの内部状態を可視化できる。

eBPFプログラムのリストと検査

以下のコマンドで、カーネルに現在ロードされているすべてのeBPFプログラムを表示できる。

$ sudo bpftool prog list

出力は以下の通り

15: cgroup_skb  name sd_fw_ingress  tag 6deef7357e7b4530  gpl
        loaded_at 2026-02-24T00:01:01+0000  uid 0
        xlated 64B  jited 58B  memlock 4096B
17: tracepoint  name handle_execve_tp  tag 8236b54ceef5a3ce  gpl
        loaded_at 2026-02-24T00:04:25+0000  uid 0
        xlated 560B  jited 379B  memlock 4096B  map_ids 6,8
        btf_id 28

※loadedとattachedの違い
loaded: プログラムが検証済みでカーネルに受け入れられたが、アクティブではない。
attached: プログラムがアクティブで、特定のフックに紐づいている。

eBPFプログラムには一意のidが割り当てられている。このidを利用して詳細を表示できる。
以下を実行すると、eBPFプログラムの詳細を表示できる。

$ sudo bpftool prog show id 17 --pretty

出力は以下の通り

{
    "id": 17,
    "type": "tracepoint",
    "name": "handle_execve_tp",
    "tag": "8236b54ceef5a3ce",
    "gpl_compatible": true,
    "loaded_at": 1771891465,
    "uid": 0,
    "orphaned": false,
    "bytes_xlated": 560,
    "jited": true,
    "bytes_jited": 379,
    "bytes_memlock": 4096,
    "map_ids": [6,8
    ],
    "btf_id": 28
}

カーネルにロードされたeBPFバイトコードを検査することもできる。

$ sudo bpftool prog dump xlated id 17

出力は以下の通り

int handle_execve_tp(struct trace_event_raw_sys_enter * ctx):
; const char *filename = (const char *)ctx->args[0];
   0: (79) r3 = *(u64 *)(r1 +16)
   1: (b7) r1 = 0
; struct path_key key = {};
   2: (7b) *(u64 *)(r10 -8) = r1
   3: (7b) *(u64 *)(r10 -16) = r1
   4: (7b) *(u64 *)(r10 -24) = r1
   5: (7b) *(u64 *)(r10 -32) = r1
   6: (7b) *(u64 *)(r10 -40) = r1
   7: (7b) *(u64 *)(r10 -48) = r1
   8: (7b) *(u64 *)(r10 -56) = r1
   9: (7b) *(u64 *)(r10 -64) = r1
  10: (7b) *(u64 *)(r10 -72) = r1
  11: (7b) *(u64 *)(r10 -80) = r1
  12: (7b) *(u64 *)(r10 -88) = r1
  13: (7b) *(u64 *)(r10 -96) = r1
  14: (7b) *(u64 *)(r10 -104) = r1
  15: (7b) *(u64 *)(r10 -112) = r1
  16: (7b) *(u64 *)(r10 -120) = r1
  17: (7b) *(u64 *)(r10 -128) = r1
  18: (7b) *(u64 *)(r10 -136) = r1
  19: (7b) *(u64 *)(r10 -144) = r1
  20: (7b) *(u64 *)(r10 -152) = r1
  21: (7b) *(u64 *)(r10 -160) = r1
  22: (7b) *(u64 *)(r10 -168) = r1
  23: (7b) *(u64 *)(r10 -176) = r1
  24: (7b) *(u64 *)(r10 -184) = r1
  25: (7b) *(u64 *)(r10 -192) = r1
  26: (7b) *(u64 *)(r10 -200) = r1
  27: (7b) *(u64 *)(r10 -208) = r1
  28: (7b) *(u64 *)(r10 -216) = r1
  29: (7b) *(u64 *)(r10 -224) = r1
  30: (7b) *(u64 *)(r10 -232) = r1
  31: (7b) *(u64 *)(r10 -240) = r1
  32: (7b) *(u64 *)(r10 -248) = r1
  33: (7b) *(u64 *)(r10 -256) = r1
  34: (bf) r1 = r10
; 
  35: (07) r1 += -256
; long n = bpf_probe_read_user_str(key.path, sizeof(key.path), filename);
  36: (b7) r2 = 256
  37: (85) call bpf_probe_read_user_str#-72624
  38: (b7) r1 = 1
; if (n <= 0) {
  39: (6d) if r1 s> r0 goto pc+28
  40: (bf) r2 = r10
; __u64 *val = bpf_map_lookup_elem(&exec_count, &key);
  41: (07) r2 += -256
  42: (18) r1 = map[id:6]
  44: (85) call __htab_map_lookup_elem#191824
  45: (15) if r0 == 0x0 goto pc+1
  46: (07) r0 += 304
; if (val) {
  47: (15) if r0 == 0x0 goto pc+4
; *val += 1;
  48: (79) r1 = *(u64 *)(r0 +0)
  49: (07) r1 += 1
  50: (7b) *(u64 *)(r0 +0) = r1
  51: (05) goto pc+10
  52: (b7) r1 = 1
; __u64 init = 1;
  53: (7b) *(u64 *)(r10 -264) = r1
  54: (bf) r2 = r10
  55: (07) r2 += -256
  56: (bf) r3 = r10
  57: (07) r3 += -264
; bpf_map_update_elem(&exec_count, &key, &init, BPF_NOEXIST);
  58: (18) r1 = map[id:6]
  60: (b7) r4 = 1
  61: (85) call htab_map_update_elem#202992
  62: (bf) r3 = r10
; bpf_printk("execve: %s\n", key.path);
  63: (07) r3 += -256
  64: (18) r1 = map[id:8][0]+0
  66: (b7) r2 = 12
  67: (85) call bpf_trace_printk#-64800
; }
  68: (b7) r0 = 0
  69: (95) exit

また、JITコンパイルされたマシンコードも確認できる。

$ sudo bpftool prog dump jited id 17

出力は以下の通り

int handle_execve_tp(struct trace_event_raw_sys_enter * ctx):
bpf_prog_8236b54ceef5a3ce_handle_execve_tp:
; const char *filename = (const char *)ctx->args[0];
   0:   nopl   0x0(%rax,%rax,1)
   5:   xchg   %ax,%ax
   7:   push   %rbp
   8:   mov    %rsp,%rbp
   b:   sub    $0x108,%rsp
  12:   mov    0x10(%rdi),%rdx
  16:   xor    %edi,%edi
; struct path_key key = {};
  18:   mov    %rdi,-0x8(%rbp)
  1c:   mov    %rdi,-0x10(%rbp)
  20:   mov    %rdi,-0x18(%rbp)
  24:   mov    %rdi,-0x20(%rbp)
  28:   mov    %rdi,-0x28(%rbp)
  2c:   mov    %rdi,-0x30(%rbp)
  30:   mov    %rdi,-0x38(%rbp)
  34:   mov    %rdi,-0x40(%rbp)
  38:   mov    %rdi,-0x48(%rbp)
  3c:   mov    %rdi,-0x50(%rbp)
  40:   mov    %rdi,-0x58(%rbp)
  44:   mov    %rdi,-0x60(%rbp)
  48:   mov    %rdi,-0x68(%rbp)
  4c:   mov    %rdi,-0x70(%rbp)
  50:   mov    %rdi,-0x78(%rbp)
  54:   mov    %rdi,-0x80(%rbp)
  58:   mov    %rdi,-0x88(%rbp)
  5f:   mov    %rdi,-0x90(%rbp)
  66:   mov    %rdi,-0x98(%rbp)
  6d:   mov    %rdi,-0xa0(%rbp)
  74:   mov    %rdi,-0xa8(%rbp)
  7b:   mov    %rdi,-0xb0(%rbp)
  82:   mov    %rdi,-0xb8(%rbp)
  89:   mov    %rdi,-0xc0(%rbp)
  90:   mov    %rdi,-0xc8(%rbp)
  97:   mov    %rdi,-0xd0(%rbp)
  9e:   mov    %rdi,-0xd8(%rbp)
  a5:   mov    %rdi,-0xe0(%rbp)
  ac:   mov    %rdi,-0xe8(%rbp)
  b3:   mov    %rdi,-0xf0(%rbp)
  ba:   mov    %rdi,-0xf8(%rbp)
  c1:   mov    %rdi,-0x100(%rbp)
  c8:   mov    %rbp,%rdi
; 
  cb:   add    $0xffffffffffffff00,%rdi
; long n = bpf_probe_read_user_str(key.path, sizeof(key.path), filename);
  d2:   mov    $0x100,%esi
  d7:   call   0xffffffff811c26c0
  dc:   mov    $0x1,%edi
; if (n <= 0) {
  e1:   cmp    %rax,%rdi
  e4:   jg     0xffffffffc0012a97
  ea:   mov    %rbp,%rsi
; __u64 *val = bpf_map_lookup_elem(&exec_count, &key);
  ed:   add    $0xffffffffffffff00,%rsi
  f4:   movabs $0xffff88810222cc00,%rdi
  fe:   call   0xffffffff81202fc0
 103:   test   %rax,%rax
 106:   je     0xffffffffc0012a32
 108:   add    $0x130,%rax
; if (val) {
 10e:   test   %rax,%rax
 111:   je     0xffffffffc0012a45
; *val += 1;
 113:   mov    0x0(%rax),%rdi
 117:   add    $0x1,%rdi
 11b:   mov    %rdi,0x0(%rax)
 11f:   jmp    0xffffffffc0012a79
 121:   mov    $0x1,%edi
; __u64 init = 1;
 126:   mov    %rdi,-0x108(%rbp)
 12d:   mov    %rbp,%rsi
 130:   add    $0xffffffffffffff00,%rsi
 137:   mov    %rbp,%rdx
 13a:   add    $0xfffffffffffffef8,%rdx
; bpf_map_update_elem(&exec_count, &key, &init, BPF_NOEXIST);
 141:   movabs $0xffff88810222cc00,%rdi
 14b:   mov    $0x1,%ecx
 150:   call   0xffffffff81205b60
 155:   mov    %rbp,%rdx
; bpf_printk("execve: %s\n", key.path);
 158:   add    $0xffffffffffffff00,%rdx
 15f:   movabs $0xffffc9000066a000,%rdi
 169:   mov    $0xc,%esi
 16e:   call   0xffffffff811c4550
; }
 173:   xor    %eax,%eax
 175:   leave
 176:   jmp    0xffffffff82003480

元のC言語コード→eBPF命令→ネイティブCPU命令に変換されていることが分かる

eBPFマップのリストと管理

以下のコマンドでeBPFマップを一覧表示できる。

$ sudo bpftool map list

出力は以下の通り

2: array  name iterator.rodata  flags 0x480
        key 4B  value 98B  max_entries 1  memlock 4096B
        btf_id 2  frozen
6: hash  name exec_count  flags 0x0
        key 256B  value 8B  max_entries 16384  memlock 4325376B
        btf_id 26

idを知っていればダンプできる。

$ sudo bpftool map dump id 6

出力は以下の通り

また、以下の方法でeBPFマップの検索、キーの更新、削除もできる
検索

keyhex=$(python3 - <<'PY'
s=b"/bin/bash\0".ljust(256, b"\x00")
print(" ".join(f"{b:02x}" for b in s))
PY
)
sudo bpftool map lookup id 5 key hex $keyhex
{
    "key": {
        "path": "/bin/bash"
    },
    "value": 1
}

s=b"/bin/bash\0": 探したいファイル名
.ljust(256, b"\x00"): 256バイトに足りない部分を0で埋める
print(..): /bin/bashを16進数に変換

キーの更新

keyhex=$(python3 - <<'PY'
s=b"/bin/bash\0".ljust(256, b"\x00")
print(" ".join(f"{b:02x}" for b in s))
PY
)
sudo bpftool map update id 5 key hex $keyhex value hex 2a 00 00 00 00 00 00 00
{
    "key": {
        "path": "/bin/bash"
    },
    "value": 42
}

削除

keyhex=$(python3 - <<'PY'
s=b"/bin/bash\0".ljust(256, b"\x00")
print(" ".join(f"{b:02x}" for b in s))
PY
)
sudo bpftool map delete id 5 key hex $keyhex
$ sudo bpftool map lookup id 6 key hex $keyhex
key:
2f 62 69 6e 2f 62 61 73  68 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00

Not found

デバッグとトレース

今までは

$ sudo cat /sys/kernel/debug/tracing/trace_pipe

でログをprintしていたが、以下でも実現できる

$ sudo bpftool prog trace

vmlinux.hファイルの生成

ざっくりいうとvmlinux.hは現在のカーネル内のデータ構造を1つのファイルにまとめたもの。めっちゃでかいヘッダーファイル。

$ bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h

利用可能なeBPF機能をリストアップする

$ sudo bpftool feature probe kernel
Scanning eBPF helper functions...
eBPF helpers supported for program type socket_filter:
        - bpf_map_lookup_elem
        - bpf_map_update_elem
        - bpf_map_delete_elem
        - bpf_ktime_get_ns
        - bpf_get_prandom_u32
        - bpf_get_smp_processor_id
        - bpf_tail_call
        - bpf_perf_event_output
        - bpf_skb_load_bytes
        - bpf_get_current_task
          ...

bpftop

eBPFプログラムをリアルタイムでモニタリングできる。

$ sudo bpftop

Pasted image 20260224091323.png
↑カーネル内で実行されているすべてのeBPFプログラム

Pasted image 20260224091306.png

↑統計情報
左上(プログラム情報): プログラムID、タイプ、名前
右上(総CPU%): CPU使用率
左下(1秒当たりのイベント数)
右下(ナノ秒単位の平均ランタイム):

次回→eBPFについて学ぶ④ - eBPF検証器

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?