bpftool: Translate prog_id to its bpf prog_name
authorMartin KaFai Lau <kafai@fb.com>
Wed, 18 Mar 2020 17:16:50 +0000 (10:16 -0700)
committerDaniel Borkmann <daniel@iogearbox.net>
Fri, 20 Mar 2020 14:51:35 +0000 (15:51 +0100)
The kernel struct_ops obj has kernel's func ptrs implemented by bpf_progs.
The bpf prog_id is stored as the value of the func ptr for introspection
purpose.  In the latter patch, a struct_ops dump subcmd will be added
to introspect these func ptrs.  It is desired to print the actual bpf
prog_name instead of only printing the prog_id.

Since struct_ops is the only usecase storing prog_id in the func ptr,
this patch adds a prog_id_as_func_ptr bool (default is false) to
"struct btf_dumper" in order not to mis-interpret the ptr value
for the other existing use-cases.

While printing a func_ptr as a bpf prog_name,
this patch also prefix the bpf prog_name with the ptr's func_proto.
[ Note that it is the ptr's func_proto instead of the bpf prog's
  func_proto ]
It reuses the current btf_dump_func() to obtain the ptr's func_proto
string.

Here is an example from the bpf_cubic.c:
"void (struct sock *, u32, u32) bictcp_cong_avoid/prog_id:140"

Signed-off-by: Martin KaFai Lau <kafai@fb.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Acked-by: Quentin Monnet <quentin@isovalent.com>
Link: https://lore.kernel.org/bpf/20200318171650.129252-1-kafai@fb.com
tools/bpf/bpftool/btf_dumper.c
tools/bpf/bpftool/main.h

index e0cff4d..497807b 100644 (file)
@@ -4,11 +4,13 @@
 #include <ctype.h>
 #include <stdio.h> /* for (FILE *) used by json_writer */
 #include <string.h>
+#include <unistd.h>
 #include <asm/byteorder.h>
 #include <linux/bitops.h>
 #include <linux/btf.h>
 #include <linux/err.h>
 #include <bpf/btf.h>
+#include <bpf/bpf.h>
 
 #include "json_writer.h"
 #include "main.h"
 static int btf_dumper_do_type(const struct btf_dumper *d, __u32 type_id,
                              __u8 bit_offset, const void *data);
 
-static void btf_dumper_ptr(const void *data, json_writer_t *jw,
-                          bool is_plain_text)
+static int btf_dump_func(const struct btf *btf, char *func_sig,
+                        const struct btf_type *func_proto,
+                        const struct btf_type *func, int pos, int size);
+
+static int dump_prog_id_as_func_ptr(const struct btf_dumper *d,
+                                   const struct btf_type *func_proto,
+                                   __u32 prog_id)
 {
-       if (is_plain_text)
-               jsonw_printf(jw, "%p", *(void **)data);
+       struct bpf_prog_info_linear *prog_info = NULL;
+       const struct btf_type *func_type;
+       const char *prog_name = NULL;
+       struct bpf_func_info *finfo;
+       struct btf *prog_btf = NULL;
+       struct bpf_prog_info *info;
+       int prog_fd, func_sig_len;
+       char prog_str[1024];
+
+       /* Get the ptr's func_proto */
+       func_sig_len = btf_dump_func(d->btf, prog_str, func_proto, NULL, 0,
+                                    sizeof(prog_str));
+       if (func_sig_len == -1)
+               return -1;
+
+       if (!prog_id)
+               goto print;
+
+       /* Get the bpf_prog's name.  Obtain from func_info. */
+       prog_fd = bpf_prog_get_fd_by_id(prog_id);
+       if (prog_fd == -1)
+               goto print;
+
+       prog_info = bpf_program__get_prog_info_linear(prog_fd,
+                                               1UL << BPF_PROG_INFO_FUNC_INFO);
+       close(prog_fd);
+       if (IS_ERR(prog_info)) {
+               prog_info = NULL;
+               goto print;
+       }
+       info = &prog_info->info;
+
+       if (!info->btf_id || !info->nr_func_info ||
+           btf__get_from_id(info->btf_id, &prog_btf))
+               goto print;
+       finfo = (struct bpf_func_info *)info->func_info;
+       func_type = btf__type_by_id(prog_btf, finfo->type_id);
+       if (!func_type || !btf_is_func(func_type))
+               goto print;
+
+       prog_name = btf__name_by_offset(prog_btf, func_type->name_off);
+
+print:
+       if (!prog_id)
+               snprintf(&prog_str[func_sig_len],
+                        sizeof(prog_str) - func_sig_len, " 0");
+       else if (prog_name)
+               snprintf(&prog_str[func_sig_len],
+                        sizeof(prog_str) - func_sig_len,
+                        " %s/prog_id:%u", prog_name, prog_id);
        else
-               jsonw_printf(jw, "%lu", *(unsigned long *)data);
+               snprintf(&prog_str[func_sig_len],
+                        sizeof(prog_str) - func_sig_len,
+                        " <unknown_prog_name>/prog_id:%u", prog_id);
+
+       prog_str[sizeof(prog_str) - 1] = '\0';
+       jsonw_string(d->jw, prog_str);
+       btf__free(prog_btf);
+       free(prog_info);
+       return 0;
+}
+
+static void btf_dumper_ptr(const struct btf_dumper *d,
+                          const struct btf_type *t,
+                          const void *data)
+{
+       unsigned long value = *(unsigned long *)data;
+       const struct btf_type *ptr_type;
+       __s32 ptr_type_id;
+
+       if (!d->prog_id_as_func_ptr || value > UINT32_MAX)
+               goto print_ptr_value;
+
+       ptr_type_id = btf__resolve_type(d->btf, t->type);
+       if (ptr_type_id < 0)
+               goto print_ptr_value;
+       ptr_type = btf__type_by_id(d->btf, ptr_type_id);
+       if (!ptr_type || !btf_is_func_proto(ptr_type))
+               goto print_ptr_value;
+
+       if (!dump_prog_id_as_func_ptr(d, ptr_type, value))
+               return;
+
+print_ptr_value:
+       if (d->is_plain_text)
+               jsonw_printf(d->jw, "%p", (void *)value);
+       else
+               jsonw_printf(d->jw, "%lu", value);
 }
 
 static int btf_dumper_modifier(const struct btf_dumper *d, __u32 type_id,
@@ -442,7 +533,7 @@ static int btf_dumper_do_type(const struct btf_dumper *d, __u32 type_id,
        case BTF_KIND_ENUM:
                return btf_dumper_enum(d, t, data);
        case BTF_KIND_PTR:
-               btf_dumper_ptr(data, d->jw, d->is_plain_text);
+               btf_dumper_ptr(d, t, data);
                return 0;
        case BTF_KIND_UNKN:
                jsonw_printf(d->jw, "(unknown)");
@@ -487,10 +578,6 @@ int btf_dumper_type(const struct btf_dumper *d, __u32 type_id,
                        return -1;                                      \
        } while (0)
 
-static int btf_dump_func(const struct btf *btf, char *func_sig,
-                        const struct btf_type *func_proto,
-                        const struct btf_type *func, int pos, int size);
-
 static int __btf_dumper_type_only(const struct btf *btf, __u32 type_id,
                                  char *func_sig, int pos, int size)
 {
@@ -599,8 +686,15 @@ static int btf_dump_func(const struct btf *btf, char *func_sig,
                        BTF_PRINT_ARG(", ");
                if (arg->type) {
                        BTF_PRINT_TYPE(arg->type);
-                       BTF_PRINT_ARG("%s",
-                                     btf__name_by_offset(btf, arg->name_off));
+                       if (arg->name_off)
+                               BTF_PRINT_ARG("%s",
+                                             btf__name_by_offset(btf, arg->name_off));
+                       else if (pos && func_sig[pos - 1] == ' ')
+                               /* Remove unnecessary space for
+                                * FUNC_PROTO that does not have
+                                * arg->name_off
+                                */
+                               func_sig[--pos] = '\0';
                } else {
                        BTF_PRINT_ARG("...");
                }
index 5f6dccd..6db2398 100644 (file)
@@ -205,6 +205,7 @@ struct btf_dumper {
        const struct btf *btf;
        json_writer_t *jw;
        bool is_plain_text;
+       bool prog_id_as_func_ptr;
 };
 
 /* btf_dumper_type - print data along with type information