2024-01-18 09:55 UTC+0000 ~ Jiri Olsa <jolsa@xxxxxxxxxx> > Displaying cookies for kprobe multi link, in plain mode: > > # bpftool link > ... > 1397: kprobe_multi prog 47532 > kretprobe.multi func_cnt 3 > addr cookie func [module] > ffffffff82b370c0 3 bpf_fentry_test1 > ffffffff82b39780 1 bpf_fentry_test2 > ffffffff82b397a0 2 bpf_fentry_test3 > > And in json mode: > > # bpftool link -j | jq > ... > { > "id": 1397, > "type": "kprobe_multi", > "prog_id": 47532, > "retprobe": true, > "func_cnt": 3, > "missed": 0, > "funcs": [ > { > "addr": 18446744071607382208, > "func": "bpf_fentry_test1", > "module": null, > "cookie": 3 > }, > { > "addr": 18446744071607392128, > "func": "bpf_fentry_test2", > "module": null, > "cookie": 1 > }, > { > "addr": 18446744071607392160, > "func": "bpf_fentry_test3", > "module": null, > "cookie": 2 > } > ] > } > > Cookie is attached to specific address, and because we sort addresses > before printing, we need to sort cookies the same way, hence adding > the struct addr_cookie to keep and sort them together. > > Signed-off-by: Jiri Olsa <jolsa@xxxxxxxxxx> > --- > tools/bpf/bpftool/link.c | 71 ++++++++++++++++++++++++++++++++-------- > 1 file changed, 57 insertions(+), 14 deletions(-) > > diff --git a/tools/bpf/bpftool/link.c b/tools/bpf/bpftool/link.c > index b66a1598b87c..fd862afe6c6f 100644 > --- a/tools/bpf/bpftool/link.c > +++ b/tools/bpf/bpftool/link.c > @@ -249,18 +249,44 @@ static int get_prog_info(int prog_id, struct bpf_prog_info *info) > return err; > } > > -static int cmp_u64(const void *A, const void *B) > +struct addr_cookie { > + __u64 addr; > + __u64 cookie; > +}; > + > +static int cmp_addr_cookie(const void *A, const void *B) > +{ > + const struct addr_cookie *a = A, *b = B; > + > + if (a->addr == b->addr) > + return 0; > + return a->addr < b->addr ? -1 : 1; > +} > + > +static struct addr_cookie * > +get_addr_cookie_array(__u64 *addrs, __u64 *cookies, __u32 count) > { > - const __u64 *a = A, *b = B; > + struct addr_cookie *data; > + __u32 i; > > - return *a - *b; > + data = calloc(count, sizeof(data[0])); > + if (!data) { > + p_err("mem alloc failed"); > + return NULL; > + } > + for (i = 0; i < count; i++) { > + data[i].addr = addrs[i]; > + data[i].cookie = cookies[i]; > + } > + qsort(data, count, sizeof(data[0]), cmp_addr_cookie); > + return data; > } > > static void > show_kprobe_multi_json(struct bpf_link_info *info, json_writer_t *wtr) > { > + struct addr_cookie *data; > __u32 i, j = 0; > - __u64 *addrs; > > jsonw_bool_field(json_wtr, "retprobe", > info->kprobe_multi.flags & BPF_F_KPROBE_MULTI_RETURN); > @@ -268,14 +294,17 @@ show_kprobe_multi_json(struct bpf_link_info *info, json_writer_t *wtr) > jsonw_uint_field(json_wtr, "missed", info->kprobe_multi.missed); > jsonw_name(json_wtr, "funcs"); > jsonw_start_array(json_wtr); > - addrs = u64_to_ptr(info->kprobe_multi.addrs); > - qsort(addrs, info->kprobe_multi.count, sizeof(addrs[0]), cmp_u64); > + data = get_addr_cookie_array(u64_to_ptr(info->kprobe_multi.addrs), > + u64_to_ptr(info->kprobe_multi.cookies), > + info->kprobe_multi.count); > + if (!data) > + return; > > /* Load it once for all. */ > if (!dd.sym_count) > kernel_syms_load(&dd); > for (i = 0; i < dd.sym_count; i++) { > - if (dd.sym_mapping[i].address != addrs[j]) > + if (dd.sym_mapping[i].address != data[j].addr) > continue; > jsonw_start_object(json_wtr); > jsonw_uint_field(json_wtr, "addr", dd.sym_mapping[i].address); > @@ -287,11 +316,13 @@ show_kprobe_multi_json(struct bpf_link_info *info, json_writer_t *wtr) > } else { > jsonw_string_field(json_wtr, "module", dd.sym_mapping[i].module); > } > + jsonw_uint_field(json_wtr, "cookie", data[j].cookie); > jsonw_end_object(json_wtr); > if (j++ == info->kprobe_multi.count) > break; > } > jsonw_end_array(json_wtr); > + free(data); > } > > static __u64 *u64_to_arr(__u64 val) > @@ -675,8 +706,8 @@ void netfilter_dump_plain(const struct bpf_link_info *info) > > static void show_kprobe_multi_plain(struct bpf_link_info *info) > { > + struct addr_cookie *data; > __u32 i, j = 0; > - __u64 *addrs; > > if (!info->kprobe_multi.count) > return; > @@ -688,8 +719,11 @@ static void show_kprobe_multi_plain(struct bpf_link_info *info) > printf("func_cnt %u ", info->kprobe_multi.count); > if (info->kprobe_multi.missed) > printf("missed %llu ", info->kprobe_multi.missed); > - addrs = (__u64 *)u64_to_ptr(info->kprobe_multi.addrs); > - qsort(addrs, info->kprobe_multi.count, sizeof(__u64), cmp_u64); > + data = get_addr_cookie_array(u64_to_ptr(info->kprobe_multi.addrs), > + u64_to_ptr(info->kprobe_multi.cookies), > + info->kprobe_multi.count); > + if (!data) > + return; > > /* Load it once for all. */ > if (!dd.sym_count) > @@ -697,12 +731,12 @@ static void show_kprobe_multi_plain(struct bpf_link_info *info) > if (!dd.sym_count) > return; Don't we need to free(data) if we return here? > > - printf("\n\t%-16s %s", "addr", "func [module]"); > + printf("\n\t%-16s %-16s %s", "addr", "cookie", "func [module]"); > for (i = 0; i < dd.sym_count; i++) { > - if (dd.sym_mapping[i].address != addrs[j]) > + if (dd.sym_mapping[i].address != data[j].addr) > continue; > - printf("\n\t%016lx %s", > - dd.sym_mapping[i].address, dd.sym_mapping[i].name); > + printf("\n\t%016lx %-16llx %s", > + dd.sym_mapping[i].address, data[j].cookie, dd.sym_mapping[i].name); > if (dd.sym_mapping[i].module[0] != '\0') > printf(" [%s] ", dd.sym_mapping[i].module); > else > @@ -711,6 +745,7 @@ static void show_kprobe_multi_plain(struct bpf_link_info *info) > if (j++ == info->kprobe_multi.count) > break; > } > + free(data); > } > > static void show_uprobe_multi_plain(struct bpf_link_info *info) > @@ -966,6 +1001,14 @@ static int do_show_link(int fd) > return -ENOMEM; > } > info.kprobe_multi.addrs = ptr_to_u64(addrs); > + cookies = calloc(count, sizeof(__u64)); > + if (!cookies) { > + p_err("mem alloc failed"); > + free(addrs); > + close(fd); > + return -ENOMEM; > + } > + info.kprobe_multi.cookies = ptr_to_u64(cookies); > goto again; > } > }