有位 pwru 的用户在 GitHub 上提了一个 issue,说 --filter-trace-tc
不能正常工作。
1
2
3
4
5
6
7
8
9
|
......
; event.addr = bpf_get_func_ip(ctx);
26: (bf) r1 = r6
27: R0=invP1 R1_w=ctx(id=0,off=0,imm=0) R6=ctx(id=0,off=0,imm=0) R7=ptr_sk_buff(id=0,off=0,imm=0) R10=fp0 fp-8=????mmmm fp-16=00000000 fp-24=00000000 fp-32=0mm0mmmm fp-40=mmmmmmmm fp-48=mmmmmmmm fp-56=mmmmmmmm fp-64=mmmmmmmm fp-72=00000000 fp-80=00000000 fp-88=00000000 fp-96=00000000 fp-104=mmmmmmmm fp-112_w=ptr_ fp-120=00000000 fp-128=0000mmmm
27: (85) call bpf_get_func_ip#173
func bpf_get_func_ip#173 not supported for program type 3
processed 437 insns (limit 1000000) max_states_per_insn 1 total_states 28 peak_states 28 mark_read 28
program fentry_tc: load program: operation not supported: func bpf_get_func_ip#173 not supported for program type 3 (1020 line(s) omitted)
|
这个 issue 的原因是 --filter-trace-tc
选项使用了 bpf_get_func_ip()
,而 bpf_get_func_ip()
在 5.15 版本的内核中才支持。
噢,--filter-trace-tc
是我给 pwru
添加的一个功能。
所以,我得想个办法避免使用 bpf_get_func_ip()
的情况下去支持 --filter-trace-tc
。
bpf_get_func_ip() helper
bpf_get_func_ip()
是 5.15 内核中新增的一个 helper,它的作用是获取当前被跟踪函数/bpf prog 的地址。
1
2
3
4
5
|
/* Implement bpf_get_func_ip inline. */
if (prog_type == BPF_PROG_TYPE_TRACING &&
insn->imm == BPF_FUNC_get_func_ip) {
/* Load IP address from ctx - 8 */
insn_buf[0] = BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, -8);
|
不过,先不深究该 helper 函数的实现,看下如何在不使用 bpf_get_func_ip()
的情况下实现 --filter-trace-tc
。
bpf prog 在 /proc/kallsyms 中的信息
在 /proc/kallsyms
中,bpf prog 的信息是这样子的:
1
2
3
4
5
|
# tail -n 4 /proc/kallsyms
ffffffffc0024020 t bpf_prog_a04f5eef06a7f555_dummy [bpf]
ffffffffc0024094 t bpf_prog_a04f5eef06a7f555_dummy [bpf]
ffffffffc002ac4c t bpf_prog_0b3a2ce7164b50f3_fentry_tc [bpf]
ffffffffc002add0 t bpf_prog_0ca2b1941f84fe9b_fexit_tc [bpf]
|
大概是这个格式:<addr> t bpf_prog_<tag>_<name>\t[bpf]
.
何以见得?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
// ${KERNEL}/kernel/bpf/core.c
static void
bpf_prog_ksym_set_name(struct bpf_prog *prog)
{
// ...
sym += snprintf(sym, KSYM_NAME_LEN, "bpf_prog_");
sym = bin2hex(sym, prog->tag, sizeof(prog->tag));
/* prog->aux->name will be ignored if full btf name is available */
if (prog->aux->func_info_cnt) {
type = btf_type_by_id(prog->aux->btf,
prog->aux->func_info[prog->aux->func_idx].type_id);
func_name = btf_name_by_offset(prog->aux->btf, type->name_off);
snprintf(sym, (size_t)(end - sym), "_%s", func_name);
return;
}
if (prog->aux->name[0])
snprintf(sym, (size_t)(end - sym), "_%s", prog->aux->name);
else
*sym = 0;
}
|
所以,确定了 bpf prog 在 /proc/kallsyms
中的信息后,就可以通过 /proc/kallsyms
来获取 bpf prog 的地址了。
tag -> addr -> name
原先使用 bpf_get_func_ip()
的目的是获取当前被跟踪 bpf prog 的地址,然后通过该地址去获取被跟踪 bpf prog 的名字。
既然避免使用 bpf_get_func_ip()
,那么就可以通过 /proc/kallsyms
来获取被跟踪 bpf prog 的 tag -> addr 信息。
然后,因为 bpf prog 的 ProgramInfo 中有 tag 信息,就可以通过 tag 来获取 bpf prog 的地址。
最终,将 bpf prog 的地址通过 spec.RewriteConstants()
注入到 bpf prog 中,从而替换了 bpf_get_func_ip()
的作用。
因为在打印事件信息的时候,已经有了 addr -> name 的信息,所以就不需要额外去获取被跟踪 bpf prog 的名字了。
小结
bpf_get_func_ip()
是 5.15 内核中新增的一个 helper,它的作用是获取当前被跟踪函数/bpf prog 的地址。
当 bpf_get_func_ip()
不可用时,可以通过 /proc/kallsyms
来获取被跟踪函数/bpf prog 的地址。