動機
systemtap看完後就是bpf了
ebpf
- 放一台vm在kernel中當內線
- 跑verfier與自己的bytecode
- 所以會有自己的IR,過llvm轉成bytecode
- 詳細看BPF Internals (eBPF)
- 用處
- trace, profile, observe, monitor
- 這裡就與systemtap很像
- 但是現在bpf的code其實不好上手
- bpftrace整體與systemtap很像,但
- 不能透過probe去找對應的source code
- 沒有embed c, context var
- bcc是幫助開發者寫bpf的工具,但
- 現在在從bcc c/python轉移到libbpf
- 十分年輕,看commit時間,一年多一點點開始
- 但libbpf有
- BPF CO-RE: bpf的目標,邊一次到處跑
- BTF: 進化的DWARF,同時也有bpf的執行檔資訊
- libbpf: 很像compiler+linker+bootstraper
- 生vmlinux.h: 不用裝linux-header了,也不用include了
- 配合BTF與bpf的ELF設定需要的object與load bpf程式
- 提供處理kernel版本差異的方法
- extern Kconfig:
extern u32 CONFIG_HZ __kconfig; - struct flavors: 讓relocate時用本機的struct
- extern Kconfig:
- BPF CO-RE: bpf的目標,邊一次到處跑
- libbpf的寫法十分神奇
- 現在在從bcc c/python轉移到libbpf
- bpftrace整體與systemtap很像,但
- security, network
- 可以鎖syscall,在到network stack之前就過濾封包
- trace, profile, observe, monitor
- bpf程式架構
- map
- 類似mmap, kmap的map,大塊的可以與userspace溝通的記憶體,拿來放一些資料結構
- helper
- bpf不放行直接碰kernel的變數,所以要用這個,當成syscall
- perf的ring buf
- bpf把資料傳回userspace的手段
- bytecode
- probe+action
- map
how to use
- 我只想做monitor
- bcc提供的工具
- bpftrace去跑script
- 我想開發bpf
- 看完前面兩個,再看libbpf
bcc的工具
- 出事的60秒quick check
- uptime
- dmesg -T | tail
- vmstat 1
- mpstat -P ALL 1
- pidstat 1
- iostat -xz 1
- free -m
- sar -n DEV 1
- sar -n TCP,ETCP 1
- top
- 用bcc的工具
- execsnoop: 看exec的syscal,會列ret
- ext4slower: 把file io太慢的proc列出來
- biolatency: block io in hist
- gethostlatency: call getaddrinfo/gethostbyname的延遲,抓DNS延遲
- runqlat: scheduler latency in hist
- biosnoop: 列proc使用block io的狀況,會列latency
- cachestat: 列cache成功與失敗的比率,每秒一筆
- tcpconnect: 列用tcp connect的程式(不用煩惱netstat有沒有裝了)
- tcpaccept: 列用tcp accept的程式
- profile: perf
- opensnoop: 看open的syscall,會列err
- 實際上的demo
完整工具列表

bpftrace
probe[,probe,...] /filter/ { action; ... }- probe用
:- systemtap用
.
- systemtap用
- filter是optional
- systemtap是用if自己做
- probe用
- probe: 括號是縮寫
BEGID,END- dynamic tracing
- kernel space
kprobe(k)kprobe:function_name[+offset]- 會有args:
arg0,arg1, …- 如果arg都放在stack
sarg0,sarg1, …
- 如果arg都放在stack
- 會有args:
kretprobe(kr)kretprobe:function_nameretval
kfunc&kretfunckfunc:function&kretfunc:function- 會有args:
args->name - kretfunc會多
retval - 可以用
bpftrace -lv去看有什麼參數與函數
- user space
uprobe(u)uprobe:library_name:function_name[+offset]uprobe:library_name:address- 會有args:
arg0,arg1, …
- 會有args:
uretprobe(ur)uretprobe:library_name:function_nameretval
- kernel space
- static tracing
- kernel space
tracepoint(t)tracepoint:name
- user space
usdt(U)usdt:binary_path:probe_nameusdt:binary_path:[probe_namespace]:probe_nameusdt:library_path:probe_nameusdt:library_path:[probe_namespace]:probe_name
- kernel space
- else
profile(p): perf的抽樣profile:hz:rate- 單位: hz, s, ms, us
- rate: 次數
software(s): perf的software事件man perf_event_opensoftware:event_name:countorsoftware:event_name- event
cpu-clock(cpu)task-clockpage-faults(faults)context-switches(cs)cpu-migrationsminor-faultsmajor-faultsalignment-faultsemulation-faultsdummybpf-output
hardware(h): perf的hardware事件man perf_event_openhardware:event_name:countorhardware:event_name- event
cpu-cycles(cycles)instructionscache-referencescache-missesbranch-instructions(branchs)branch-missesbus-cyclesfrontend-stallsbackend-stallsref-cycles
interval(i): timerinterval:ms:rate- 單位: hz, s, ms, us
- rate: 次數
- 可以在某些probe用wildcard,
k:vfs_* - 還有看mem與probe的iterator在這裡
- action
- buildin function
- printf
- signal
- …
- 直接看這裡
- map function
- 語法真的很神奇
@name = map-function(val)- 我是這樣想:
@name = map-function(@name, val)- map function會去讀map與val產生新的map
- 我是這樣想:
- count
- hist
- min, max
- 剩下這裡
- 語法真的很神奇
- buildin var
- pid, tid, uid, gid, cpid(child pid)
- kstack, ustack: kernel, user stack
$1,$2…: bpftrace的參數- rand: 就是rand
- cpu, cgroup: cpu id, cgroup id
- comm: proc name
- func: function name
- probe: probe name
- nssec, elapsed: timestamp in ns, bpftrace的運行時間
- controll structrue
if (expr) {} else {}(expr) ? expr1 : expr2;
unroll (expr) {}(loop)- comment:
//,/**/
- buildin function
- var
- local:
$name - global:
@name- 這個其實就是map,拿來放置大物件的
- buildin:
name - 沒有struct?
- 自己寫
- include(同c)
- 生BTF
- cast(同c)
- local:
- data structrue
- array(hash)
@associative_array_name[key_name, key_name2, ...]
- tuple
$t = (expr1, expr2, ...)$t.1; $t.2;
- array(hash)
libbpf
參考資料在?
範例在兩個地方
有說明文
- BCC to libbpf conversion guide
- 雖說是BCC到libbpf,但裡面涵蓋寫libbpf需要注意的點
- Tips and Tricks for Writing Linux BPF Applications with libbpf
- 誠如標題所示,就是tip與trick
- Building BPF applications with libbpf-bootstrap
- libbpf-bootstrap講解
主角,libbpf的code,在這裡
建議從libbpf-bootstrap開始看,下面用examples/c/krpobe來看,看看每個用libbpf都會有的東西
兩個code: userspace & bpf
現在程式分成userspace的kprobe.c與bpf的kprobe.bpf.c
kprobe.c: 會做跑bpf的前置工作與等bpf完成
kprobe.bpf.c: 實際跑bpf probe的地方
userspace
header有#include "kprobe.skel.h"
libbpf生的header,裡面有與bpf溝通需要的東西與libbpf的API
libbpf_print_fn 與 bump_memlock_rlimit
libbpf_print_fn就是printk之類的,可以在裡面設定需要多燒level才print
bump_memlock_rlimit是設定bpf的mem用量
載入到執行到移除
- bootstrap_bpf__open
- 把bpf的code讀進來
- 拿到
struct bootstrap_bpf- 可以改裡面的參數
- bootstrap_bpf__load
- 開始創map,把code丟到verifier
- bootstrap_bpf__attach
- 開始跑
- 之後就是看userspace要做什麼
- bootstrap_bpf__destroy
- 把bpf停掉
bpf
herder
#include "vmlinux.h" /* all kernel types */
#include <bpf/bpf_helpers.h> /* most used helpers: SEC, __always_inline, etc */
#include <bpf/bpf_core_read.h> /* for BPF CO-RE helpers */
#include <bpf/bpf_tracing.h> /* for getting kprobe arguments */
map
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 8192);
__type(key, pid_t);
__type(value, u64);
} exec_start SEC(".maps");
- 有關的type在bpf.h
- 但struct要放什麼就要去找範例了
- 要加
SEC(".maps")
probe
SEC("tp/sched/sched_process_exec")
int handle_exec(struct trace_event_raw_sched_process_exec *ctx)
{
//...
return 0;
}
- 在
SEC放probe - 不同probe的函數宣告方式不一樣,像kprobe要
BPF_KPROBE(...)這macro
- 找範例去對,現在還沒看到像樣的文件
helper
task = (struct task_struct *)bpf_get_current_task();
bpf_get_current_comm(&e->comm, sizeof(e->comm));
bpf_probe_read_str(&e->filename, sizeof(e->filename), (void *)ctx + fname_off);
這些helper都在bpf_helper_defs.h
Ref
bpftrace Cheat Sheet bpftrace Reference Guide BPF binaries: BTF, CO-RE, and the future of BPF perf tools BPF Internals (eBPF) BPF Portability and CO-RE