色々あって確認をしたので書く。
attached_probes
アタッチしたprobeの数。通常のフォーマットで「Attaching 1 probe...」と出る内容。
$ sudo bpftrace -B full -f json -e 'kprobe:vfs_read { printf("Hello, world\n"); }' {"type": "attached_probes", "data": {"probes": 1}} ...
printf
printf
関数での出力結果のデータ。
$ sudo bpftrace -B full -f json -e 'kprobe:vfs_read { printf("[%ld] Hello, world\n", nsecs); }' | head -5 {"type": "attached_probes", "data": {"probes": 1}} {"type": "printf", "data": "[38374324977911] Hello, world\n"} {"type": "printf", "data": "[38374325064127] Hello, world\n"} {"type": "printf", "data": "[38374325364310] Hello, world\n"} {"type": "printf", "data": "[38374325581025] Hello, world\n"}
map
count()
など値が一つの集計関数で渡ってくる構造。
$ sudo bpftrace -B full -f json -e 'kprobe:vfs_read { @c[comm] = count() } interval:s:5 { exit(); }' {"type": "attached_probes", "data": {"probes": 2}} {"type": "map", "data": {"@c": {"systemd-journal": 1, "gmain": 2, "vminfo": 7, "docker-containe": 54, "dockerd": 54, "redis-server": 96}}}
jq にかけるとこう。変数ごとにさらにマップがある感じ。
{ "type": "map", "data": { "@c": { "systemd-journal": 1, "gmain": 2, "vminfo": 7, "docker-containe": 54, "dockerd": 54, "redis-server": 96 } } }
hist
hist()
で渡ってくる構造。
$ sudo bpftrace -B full -f json -e 'kretprobe:vfs_read { @c[comm] = hist(retval) } interval:s:5 { exit(); }' {"type": "attached_probes", "data": {"probes": 2}}
でかいので整形後のものだけ。
{ "type": "hist", "data": { "@c": { "systemd-journal": [ { "min": 8, "max": 15, "count": 1 } ], "gmain": [ { "max": -1, "count": 1 }, //... ], "redis-server": [ { "max": -1, "count": 49 }, { "min": 0, "max": 0, "count": 0 }, { "min": 1, "max": 1, "count": 0 }, { "min": 2, "max": 3, "count": 0 }, { "min": 4, "max": 7, "count": 0 }, { "min": 8, "max": 15, "count": 0 }, { "min": 16, "max": 31, "count": 0 }, { "min": 32, "max": 63, "count": 0 }, { "min": 64, "max": 127, "count": 0 }, { "min": 128, "max": 255, "count": 0 }, { "min": 256, "max": 511, "count": 49 } ] } } }
変数→comm→レンジごとのcountの配列。
ちなみに lhist
でも同じ構造を得られる。
~$ sudo bpftrace -B full -f json -e 'kretprobe:vfs_read { @c[comm] = lhist(retval, 0, 1000, 100) } interval:s:5 { exit(); }' {"type": "attached_probes", "data": {"probes": 2}} ...
{ "type": "hist", "data": { "@c": { "systemd-journal": [ { "min": 0, "max": 99, "count": 1 } ], //... "redis-server": [ { "max": -1, "count": 48 }, { "min": 0, "max": 99, "count": 0 }, { "min": 100, "max": 199, "count": 0 }, { "min": 200, "max": 299, "count": 0 }, { "min": 300, "max": 399, "count": 48 } ] } } }
stats
stats()
で渡ってくる構造。
$ sudo bpftrace -B full -f json -e 'kretprobe:vfs_read { @c[comm] = stats(retval) } interval:s:5 { exit(); }' {"type": "attached_probes", "data": {"probes": 2}}
{ "type": "stats", "data": { "@c": { "systemd-journal": { "count": 1, "average": 8, "total": 8 }, "docker-containe": { "count": 46, "average": 10, "total": 484 }, "dockerd": { "count": 47, "average": 10, "total": 512 }, "redis-server": { "count": 96, "average": 152, "total": 14640 }, "vminfo": { "count": 7, "average": 329, "total": 2304 } } } }
ネストしない場合
@c[comm]
などとせず @c
で受けた場合ネストが一段解除されるようで、 type
を見ながら構造を動的に判定していい感じに扱う必要があるかもしれない。
特別な key の扱い
kstack()
みたいなものは改行入りのつながった文字列のキーになる。配列のキーにはならない...。
{ "type": "map", "data": { "@c": { "\n kretprobe_trampoline+0\n __x64_sys_read+26\n do_syscall_64+90\n entry_SYSCALL_64_after_hwframe+68\n": 209 } } }
[pid, comm]
みたいにすると普通に ,
で結合された一つの文字列になる。
{ "type": "map", "data": { "@c": { "systemd-journal,490": 1, "gmain,1031": 2, "vminfo,1611": 7, "docker-containe,2658": 50, "dockerd,1070": 52, "redis-server,1080": 98 } } }
bpftrace で追いつかないような複雑なフォーマットで出力したい場合は BCC を使うべき、ということになっているが、そうは言っても10徳ナイフ的な bpftrace が便利なのと、JSONをうまく取り扱えば割と要件を満たせるパターンが多そうに思われた。何かに使えればいいですね。