drgn[1] 是 Linux 系统可编程的调试器,基于 Python 脚本简单、便利地读取内核数据结构和变量。
理所当然地,drgn
可以用来学习 bpf,用来 inspect bpf map、bpf program、bpf link 等对象。
P.S. 以下内容基于 drgn PR[2] 提供的功能。
inspect bpf map
类似 bpftool map list
,可以使用 drgn
来查看 bpf map 的信息。
# drgn ./contrib/bpf_inspect.py m
2: BPF_MAP_TYPE_PROG_ARRAY hid_jmp_table
4207: BPF_MAP_TYPE_PROG_ARRAY jmp_table
4208: BPF_MAP_TYPE_ARRAY tailcall.bss
4247: BPF_MAP_TYPE_PROG_ARRAY jmp_table
4248: BPF_MAP_TYPE_ARRAY tailcall.bss
5092: BPF_MAP_TYPE_ARRAY .rodata
# drgn ./contrib/bpf_inspect.py m --show-details
2: BPF_MAP_TYPE_PROG_ARRAY hid_jmp_table
owner: BPF_PROG_TYPE_TRACING JITed
4207: BPF_MAP_TYPE_PROG_ARRAY jmp_table
owner: BPF_PROG_TYPE_SCHED_CLS JITed
poke: 2295: BPF_PROG_TYPE_EXT entry tail_call_reachable
4208: BPF_MAP_TYPE_ARRAY tailcall.bss
4247: BPF_MAP_TYPE_PROG_ARRAY jmp_table
owner: BPF_PROG_TYPE_SCHED_CLS JITed
poke: 2400: BPF_PROG_TYPE_EXT entry tail_call_reachable
4248: BPF_MAP_TYPE_ARRAY tailcall.bss
5092: BPF_MAP_TYPE_ARRAY .rodata
如果想要 inspect 具体的 bpf map 呢?
# drgn ./contrib/bpf_inspect.py i
For help, type help(drgn).
>>> import drgn
>>> from drgn import NULL, Object, cast, container_of, execscript, offsetof, reinterpret, sizeof, stack_trace
>>> from drgn.helpers.common import *
>>> from drgn.helpers.linux import *
>>> list_bpf_maps()
2: BPF_MAP_TYPE_PROG_ARRAY hid_jmp_table
4207: BPF_MAP_TYPE_PROG_ARRAY jmp_table
4208: BPF_MAP_TYPE_ARRAY tailcall.bss
4247: BPF_MAP_TYPE_PROG_ARRAY jmp_table
4248: BPF_MAP_TYPE_ARRAY tailcall.bss
5092: BPF_MAP_TYPE_ARRAY .rodata
>>> m = get_bpf_map_by_id(4247)
>>> print(m.map.format_())
*(struct bpf_map *)0xffff990290259400 = {
.ops = (const struct bpf_map_ops *)prog_array_map_ops+0x0 = 0xffffffff8583cc20,
.inner_map_meta = (struct bpf_map *)0x0,
.security = (void *)0x0,
.map_type = (enum bpf_map_type)BPF_MAP_TYPE_PROG_ARRAY,
.key_size = (u32)4,
.value_size = (u32)4,
.max_entries = (u32)1,
.map_extra = (u64)0,
.map_flags = (u32)0,
.id = (u32)4247,
.record = (struct btf_record *)0x0,
.numa_node = (int)-1,
.btf_key_type_id = (u32)0,
.btf_value_type_id = (u32)0,
.btf_vmlinux_value_type_id = (u32)0,
.btf = (struct btf *)0x0,
.objcg = (struct obj_cgroup *)0xffff990290f48540,
.name = (char [16])"jmp_table",
// ...
}
>>> arr = BpfProgArrayMap(m.map)
>>> print(arr.prog_array.format_())
*(struct bpf_array *)0xffff990290259400 = {
.map = (struct bpf_map){
.ops = (const struct bpf_map_ops *)prog_array_map_ops+0x0 = 0xffffffff8583cc20,
// ...
},
.elem_size = (u32)8,
.index_mask = (u32)0,
.aux = (struct bpf_array_aux *)0xffff9902885134e0,
.__empty_value = (struct <anonymous>){},
.value = (char []){},
.__empty_ptrs = (struct <anonymous>){},
.ptrs = (void *[]){},
.__empty_pptrs = (struct <anonymous>){},
.pptrs = (void *[]){},
}
inspect bpf program
类似 bpftool prog list
,可以使用 drgn
来查看 bpf program 的信息。
# drgn ./contrib/bpf_inspect.py p
2: BPF_PROG_TYPE_TRACING hid_tail_call
2295: BPF_PROG_TYPE_EXT entry tail_call_reachable
2400: BPF_PROG_TYPE_EXT entry tail_call_reachable
4235: BPF_PROG_TYPE_SCHED_CLS entry1
# drgn ./contrib/bpf_inspect.py p --show-details
2: BPF_PROG_TYPE_TRACING hid_tail_call
used map: 2: BPF_MAP_TYPE_PROG_ARRAY hid_jmp_table
ksym: bpf_prog_7cc47bbf07148bfe_hid_tail_call 0x00ffffffffc009e948 105 bytes
2295: BPF_PROG_TYPE_EXT entry tail_call_reachable
used map: 4208: BPF_MAP_TYPE_ARRAY tailcall.bss
used map: 4207: BPF_MAP_TYPE_PROG_ARRAY jmp_table
ksym: bpf_prog_0c0f4c2413ef19b1_entry 0x00ffffffffc00b4720 74 bytes
func[ 1]: 0: BPF_PROG_TYPE_EXT subprog_tail
funcksym: bpf_prog_3a140cef239a4b4f_subprog_tail 0x00ffffffffc00b4788 76 bytes
2400: BPF_PROG_TYPE_EXT entry tail_call_reachable
used map: 4248: BPF_MAP_TYPE_ARRAY tailcall.bss
used map: 4247: BPF_MAP_TYPE_PROG_ARRAY jmp_table
ksym: bpf_prog_0c0f4c2413ef19b1_entry 0x00ffffffffc00af864 74 bytes
func[ 1]: 0: BPF_PROG_TYPE_EXT subprog_tail
funcksym: bpf_prog_3a140cef239a4b4f_subprog_tail 0x00ffffffffc00b3fdc 76 bytes
4235: BPF_PROG_TYPE_SCHED_CLS entry1
used map: 5092: BPF_MAP_TYPE_ARRAY .rodata
ksym: bpf_prog_e20a685998fd41c8_entry1 0x00ffffffffc009ece0 26 bytes
func[ 1]: 0: BPF_PROG_TYPE_SCHED_CLS subprog1
funcksym: bpf_prog_9e6e84b2270d0b1f_subprog1 0x00ffffffffc009ed0c 36 bytes
func[ 2]: 0: BPF_PROG_TYPE_SCHED_CLS subprog2
funcksym: bpf_prog_9e6e84b2270d0b1f_subprog2 0x00ffffffffc009ed48 36 bytes
如果想要 inspect 具体的 bpf program 呢?
# drgn ./contrib/bpf_inspect.py i
For help, type help(drgn).
>>> import drgn
>>> from drgn import NULL, Object, cast, container_of, execscript, offsetof, reinterpret, sizeof, stack_trace
>>> from drgn.helpers.common import *
>>> from drgn.helpers.linux import *
>>> list_bpf_progs()
2: BPF_PROG_TYPE_TRACING hid_tail_call
2295: BPF_PROG_TYPE_EXT entry tail_call_reachable
2400: BPF_PROG_TYPE_EXT entry tail_call_reachable
4235: BPF_PROG_TYPE_SCHED_CLS entry1
>>> p = get_bpf_prog_by_id(2400)
>>> print(p.prog.format_())
*(struct bpf_prog *)0xffffb2e08075a000 = {
.pages = (u16)1,
.jited = (u16)1,
.jit_requested = (u16)1,
.gpl_compatible = (u16)1,
.cb_access = (u16)1,
.dst_needed = (u16)0,
.blinding_requested = (u16)0,
.blinded = (u16)0,
.is_func = (u16)0,
.kprobe_override = (u16)0,
.has_callchain_buf = (u16)0,
.enforce_expected_attach_type = (u16)0,
.call_get_stack = (u16)0,
.call_get_func_ip = (u16)0,
.tstamp_type_access = (u16)0,
.type = (enum bpf_prog_type)BPF_PROG_TYPE_EXT,
.expected_attach_type = (enum bpf_attach_type)BPF_CGROUP_INET_INGRESS,
.len = (u32)20,
.jited_len = (u32)74,
.tag = (u8 [8]){ 12, 15, 76, 36, 19, 239, 25, 177 },
.stats = (struct bpf_prog_stats *)0x4a590,
.active = (int *)0x4a24c,
.bpf_func = (unsigned int (*)(const void *, const struct bpf_insn *))0xffffffffc00af864,
.aux = (struct bpf_prog_aux *)0xffff9902bbdbb800,
.orig_prog = (struct sock_fprog_kern *)0x0,
.__empty_insns = (struct <anonymous>){},
.insns = (struct sock_filter []){},
.__empty_insnsi = (struct <anonymous>){},
.insnsi = (struct bpf_insn []){},
}
>>> print(p.prog.aux.format_())
*(struct bpf_prog_aux *)0xffff9902bbdbb800 = {
.refcnt = (atomic64_t){
.counter = (s64)1,
},
.used_map_cnt = (u32)2,
// ...
.stack_depth = (u32)0,
.id = (u32)2400,
// ...
.name = (char [16])"entry",
// ...
}
inspect bpf link
类似 bpftool link list
,可以使用 drgn
来查看 bpf link 的信息。
# drgn ./contrib/bpf_inspect.py l --show-details
1: BPF_LINK_TYPE_TRACING
prog: 2: BPF_PROG_TYPE_TRACING hid_tail_call
attach: __hid_bpf_tail_call
如果想要 inspect 具体的 bpf link 呢?
# drgn ./contrib/bpf_inspect.py i
For help, type help(drgn).
>>> import drgn
>>> from drgn import NULL, Object, cast, container_of, execscript, offsetof, reinterpret, sizeof, stack_trace
>>> from drgn.helpers.common import *
>>> from drgn.helpers.linux import *
>>> list_bpf_links()
1: BPF_LINK_TYPE_TRACING
prog: 2: BPF_PROG_TYPE_TRACING hid_tail_call
attach: __hid_bpf_tail_call
>>> l = get_bpf_link_by_id(1)
>>> print(l.link.format_())
*(struct bpf_link *)0xffff990282c31200 = {
.refcnt = (atomic64_t){
.counter = (s64)1,
},
.id = (u32)1,
.type = (enum bpf_link_type)BPF_LINK_TYPE_TRACING,
.ops = (const struct bpf_link_ops *)bpf_tracing_link_lops+0x0 = 0xffffffff85839000,
.prog = (struct bpf_prog *)0xffffb2e080075000,
// ...
}
>>> t = BpfTracingLink(l.link)
>>> print(t.tracing.format_())
*(struct bpf_tracing_link *)0xffff990282c31200 = {
.link = (struct bpf_tramp_link){
.link = (struct bpf_link){
.refcnt = (atomic64_t){
.counter = (s64)1,
},
.id = (u32)1,
.type = (enum bpf_link_type)BPF_LINK_TYPE_TRACING,
.ops = (const struct bpf_link_ops *)bpf_tracing_link_lops+0x0 = 0xffffffff85839000,
.prog = (struct bpf_prog *)0xffffb2e080075000,
// ...
},
.tramp_hlist = (struct hlist_node){
.next = (struct hlist_node *)0x0,
.pprev = (struct hlist_node **)0xffff990282c30ed0,
},
.cookie = (u64)0,
},
.attach_type = (enum bpf_attach_type)BPF_MODIFY_RETURN,
.trampoline = (struct bpf_trampoline *)0xffff990282c30e40,
.tgt_prog = (struct bpf_prog *)0x0,
}
总结
drgn
是一个强大的调试工具,可以用来 inspect bpf map、bpf program、bpf link 等对象。
drgn: https://github.com/osandov/drgn
[2]drgn PR: https://github.com/osandov/drgn/pull/409