動機

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
          • libbpf的寫法十分神奇
    • security, network
      • 可以鎖syscall,在到network stack之前就過濾封包
  • bpf程式架構
    • map
      • 類似mmap, kmap的map,大塊的可以與userspace溝通的記憶體,拿來放一些資料結構
    • helper
      • bpf不放行直接碰kernel的變數,所以要用這個,當成syscall
    • perf的ring buf
      • bpf把資料傳回userspace的手段
    • bytecode
    • probe+action

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

完整工具列表

bcc tracing tools

bcc github上的工具列表

bpftrace

  • probe[,probe,...] /filter/ { action; ... }
    • probe用:
      • systemtap用.
    • filter是optional
      • systemtap是用if自己做
  • probe: 括號是縮寫
    • BEGID, END
    • dynamic tracing
      • kernel space
        • kprobe(k)
          • kprobe:function_name[+offset]
            • 會有args: arg0, arg1, …
              • 如果arg都放在stack
                • sarg0, sarg1, …
        • kretprobe(kr)
          • kretprobe:function_name
            • retval
        • kfunc & kretfunc
          • kfunc: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, …
        • uretprobe(ur)
          • uretprobe:library_name:function_name
            • retval
    • static tracing
      • kernel space
        • tracepoint(t)
          • tracepoint:name
      • user space
        • usdt(U)
          • usdt:binary_path:probe_name
          • usdt:binary_path:[probe_namespace]:probe_name
          • usdt:library_path:probe_name
          • usdt:library_path:[probe_namespace]:probe_name
    • else
      • profile(p): perf的抽樣
        • profile:hz:rate
          • 單位: hz, s, ms, us
          • rate: 次數
      • software(s): perf的software事件 man perf_event_open
        • software:event_name:count or software:event_name
        • event
          • cpu-clock(cpu)
          • task-clock
          • page-faults(faults)
          • context-switches(cs)
          • cpu-migrations
          • minor-faults
          • major-faults
          • alignment-faults
          • emulation-faults
          • dummy
          • bpf-output
      • hardware(h): perf的hardware事件 man perf_event_open
        • hardware:event_name:count or hardware:event_name
        • event
          • cpu-cycles(cycles)
          • instructions
          • cache-references
          • cache-misses
          • branch-instructions(branchs)
          • branch-misses
          • bus-cycles
          • frontend-stalls
          • backend-stalls
          • ref-cycles
      • interval(i): timer
        • interval: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: //, /**/
  • var
    • local: $name
    • global: @name
      • 這個其實就是map,拿來放置大物件的
    • buildin: name
    • 沒有struct?
      • 自己寫
      • include(同c)
      • 生BTF
    • cast(同c)
  • data structrue
    • array(hash)
      • @associative_array_name[key_name, key_name2, ...]
    • tuple
      • $t = (expr1, expr2, ...)
      • $t.1; $t.2;

libbpf

參考資料在?

範例在兩個地方

  1. libbpf-bootstrap
  2. bcc的libbpf-tools

有說明文

主角,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用量

載入到執行到移除

  1. bootstrap_bpf__open
  • 把bpf的code讀進來
  • 拿到struct bootstrap_bpf
    • 可以改裡面的參數
  1. bootstrap_bpf__load
  • 開始創map,把code丟到verifier
  1. bootstrap_bpf__attach
  • 開始跑
  • 之後就是看userspace要做什麼
  1. 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");
  1. 有關的type在bpf.h
  • 但struct要放什麼就要去找範例了
  1. 要加SEC(".maps")

probe

SEC("tp/sched/sched_process_exec")
int handle_exec(struct trace_event_raw_sched_process_exec *ctx)
{
  //...
	return 0;
}
  1. SEC放probe
  2. 不同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