bpf: libbpf: bpftool: Print bpf_line_info during prog dump
authorMartin KaFai Lau <kafai@fb.com>
Sat, 8 Dec 2018 00:42:32 +0000 (16:42 -0800)
committerAlexei Starovoitov <ast@kernel.org>
Sun, 9 Dec 2018 21:54:38 +0000 (13:54 -0800)
This patch adds print bpf_line_info function in 'prog dump jitted'
and 'prog dump xlated':

[root@arch-fb-vm1 bpf]# ~/devshare/fb-kernel/linux/tools/bpf/bpftool/bpftool prog dump jited pinned /sys/fs/bpf/test_btf_haskv
[...]
int test_long_fname_2(struct dummy_tracepoint_args * arg):
bpf_prog_44a040bf25481309_test_long_fname_2:
; static int test_long_fname_2(struct dummy_tracepoint_args *arg)
   0: push   %rbp
   1: mov    %rsp,%rbp
   4: sub    $0x30,%rsp
   b: sub    $0x28,%rbp
   f: mov    %rbx,0x0(%rbp)
  13: mov    %r13,0x8(%rbp)
  17: mov    %r14,0x10(%rbp)
  1b: mov    %r15,0x18(%rbp)
  1f: xor    %eax,%eax
  21: mov    %rax,0x20(%rbp)
  25: xor    %esi,%esi
; int key = 0;
  27: mov    %esi,-0x4(%rbp)
; if (!arg->sock)
  2a: mov    0x8(%rdi),%rdi
; if (!arg->sock)
  2e: cmp    $0x0,%rdi
  32: je     0x0000000000000070
  34: mov    %rbp,%rsi
; counts = bpf_map_lookup_elem(&btf_map, &key);
  37: add    $0xfffffffffffffffc,%rsi
  3b: movabs $0xffff8881139d7480,%rdi
  45: add    $0x110,%rdi
  4c: mov    0x0(%rsi),%eax
  4f: cmp    $0x4,%rax
  53: jae    0x000000000000005e
  55: shl    $0x3,%rax
  59: add    %rdi,%rax
  5c: jmp    0x0000000000000060
  5e: xor    %eax,%eax
; if (!counts)
  60: cmp    $0x0,%rax
  64: je     0x0000000000000070
; counts->v6++;
  66: mov    0x4(%rax),%edi
  69: add    $0x1,%rdi
  6d: mov    %edi,0x4(%rax)
  70: mov    0x0(%rbp),%rbx
  74: mov    0x8(%rbp),%r13
  78: mov    0x10(%rbp),%r14
  7c: mov    0x18(%rbp),%r15
  80: add    $0x28,%rbp
  84: leaveq
  85: retq
[...]

With linum:
[root@arch-fb-vm1 bpf]# ~/devshare/fb-kernel/linux/tools/bpf/bpftool/bpftool prog dump jited pinned /sys/fs/bpf/test_btf_haskv linum
int _dummy_tracepoint(struct dummy_tracepoint_args * arg):
bpf_prog_b07ccb89267cf242__dummy_tracepoint:
; return test_long_fname_1(arg); [file:/data/users/kafai/fb-kernel/linux/tools/testing/selftests/bpf/test_btf_haskv.c line_num:54 line_col:9]
   0: push   %rbp
   1: mov    %rsp,%rbp
   4: sub    $0x28,%rsp
   b: sub    $0x28,%rbp
   f: mov    %rbx,0x0(%rbp)
  13: mov    %r13,0x8(%rbp)
  17: mov    %r14,0x10(%rbp)
  1b: mov    %r15,0x18(%rbp)
  1f: xor    %eax,%eax
  21: mov    %rax,0x20(%rbp)
  25: callq  0x000000000000851e
; return test_long_fname_1(arg); [file:/data/users/kafai/fb-kernel/linux/tools/testing/selftests/bpf/test_btf_haskv.c line_num:54 line_col:2]
  2a: xor    %eax,%eax
  2c: mov    0x0(%rbp),%rbx
  30: mov    0x8(%rbp),%r13
  34: mov    0x10(%rbp),%r14
  38: mov    0x18(%rbp),%r15
  3c: add    $0x28,%rbp
  40: leaveq
  41: retq
[...]

Signed-off-by: Martin KaFai Lau <kafai@fb.com>
Acked-by: Yonghong Song <yhs@fb.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
12 files changed:
tools/bpf/bpftool/Documentation/bpftool-prog.rst
tools/bpf/bpftool/bash-completion/bpftool
tools/bpf/bpftool/btf_dumper.c
tools/bpf/bpftool/jit_disasm.c
tools/bpf/bpftool/main.h
tools/bpf/bpftool/prog.c
tools/bpf/bpftool/xlated_dumper.c
tools/bpf/bpftool/xlated_dumper.h
tools/lib/bpf/Build
tools/lib/bpf/bpf_prog_linfo.c [new file with mode: 0644]
tools/lib/bpf/libbpf.h
tools/lib/bpf/libbpf.map

index 5524b6dccd856468668a6234a903ac0bb07a318f..7c30731a9b734da78960f2f2dd5c95c0ba0c1ca8 100644 (file)
@@ -22,8 +22,8 @@ MAP COMMANDS
 =============
 
 |      **bpftool** **prog { show | list }** [*PROG*]
-|      **bpftool** **prog dump xlated** *PROG* [{**file** *FILE* | **opcodes** | **visual**}]
-|      **bpftool** **prog dump jited**  *PROG* [{**file** *FILE* | **opcodes**}]
+|      **bpftool** **prog dump xlated** *PROG* [{**file** *FILE* | **opcodes** | **visual** | **linum**}]
+|      **bpftool** **prog dump jited**  *PROG* [{**file** *FILE* | **opcodes** | **linum**}]
 |      **bpftool** **prog pin** *PROG* *FILE*
 |      **bpftool** **prog { load | loadall }** *OBJ* *PATH* [**type** *TYPE*] [**map** {**idx** *IDX* | **name** *NAME*} *MAP*] [**dev** *NAME*]
 |      **bpftool** **prog attach** *PROG* *ATTACH_TYPE* [*MAP*]
@@ -56,7 +56,7 @@ DESCRIPTION
                  Output will start with program ID followed by program type and
                  zero or more named attributes (depending on kernel version).
 
-       **bpftool prog dump xlated** *PROG* [{ **file** *FILE* | **opcodes** | **visual** }]
+       **bpftool prog dump xlated** *PROG* [{ **file** *FILE* | **opcodes** | **visual** | **linum** }]
                  Dump eBPF instructions of the program from the kernel. By
                  default, eBPF will be disassembled and printed to standard
                  output in human-readable format. In this case, **opcodes**
@@ -69,13 +69,21 @@ DESCRIPTION
                  built instead, and eBPF instructions will be presented with
                  CFG in DOT format, on standard output.
 
-       **bpftool prog dump jited**  *PROG* [{ **file** *FILE* | **opcodes** }]
+                 If the prog has line_info available, the source line will
+                 be displayed by default.  If **linum** is specified,
+                 the filename, line number and line column will also be
+                 displayed on top of the source line.
+       **bpftool prog dump jited**  *PROG* [{ **file** *FILE* | **opcodes** | **linum** }]
                  Dump jited image (host machine code) of the program.
                  If *FILE* is specified image will be written to a file,
                  otherwise it will be disassembled and printed to stdout.
 
                  **opcodes** controls if raw opcodes will be printed.
 
+                 If the prog has line_info available, the source line will
+                 be displayed by default.  If **linum** is specified,
+                 the filename, line number and line column will also be
+                 displayed on top of the source line.
        **bpftool prog pin** *PROG* *FILE*
                  Pin program *PROG* as *FILE*.
 
index 44c189ba072a3bf3e1cfee218ac6fc89c0200d05..a57febd6abb1648bf2a0999838eb331e36edd7fe 100644 (file)
@@ -191,7 +191,7 @@ _bpftool()
 
     # Deal with simplest keywords
     case $prev in
-        help|hex|opcodes|visual)
+        help|hex|opcodes|visual|linum)
             return 0
             ;;
         tag)
@@ -278,10 +278,10 @@ _bpftool()
                     *)
                         _bpftool_once_attr 'file'
                         if _bpftool_search_list 'xlated'; then
-                            COMPREPLY+=( $( compgen -W 'opcodes visual' -- \
+                            COMPREPLY+=( $( compgen -W 'opcodes visual linum' -- \
                                 "$cur" ) )
                         else
-                            COMPREPLY+=( $( compgen -W 'opcodes' -- \
+                            COMPREPLY+=( $( compgen -W 'opcodes linum' -- \
                                 "$cur" ) )
                         fi
                         return 0
index c3fd3a7cb78764d30cca3496809a8bdf0f916d8e..dbbf6ece676022dcb1c8e61ab187a0ad1c2c3b5e 100644 (file)
@@ -385,3 +385,67 @@ void btf_dumper_type_only(const struct btf *btf, __u32 type_id, char *func_sig,
        if (err < 0)
                func_sig[0] = '\0';
 }
+
+static const char *ltrim(const char *s)
+{
+       while (isspace(*s))
+               s++;
+
+       return s;
+}
+
+void btf_dump_linfo_plain(const struct btf *btf,
+                         const struct bpf_line_info *linfo,
+                         const char *prefix, bool linum)
+{
+       const char *line = btf__name_by_offset(btf, linfo->line_off);
+
+       if (!line)
+               return;
+       line = ltrim(line);
+
+       if (!prefix)
+               prefix = "";
+
+       if (linum) {
+               const char *file = btf__name_by_offset(btf, linfo->file_name_off);
+
+               /* More forgiving on file because linum option is
+                * expected to provide more info than the already
+                * available src line.
+                */
+               if (!file)
+                       file = "";
+
+               printf("%s%s [file:%s line_num:%u line_col:%u]\n",
+                      prefix, line, file,
+                      BPF_LINE_INFO_LINE_NUM(linfo->line_col),
+                      BPF_LINE_INFO_LINE_COL(linfo->line_col));
+       } else {
+               printf("%s%s\n", prefix, line);
+       }
+}
+
+void btf_dump_linfo_json(const struct btf *btf,
+                        const struct bpf_line_info *linfo, bool linum)
+{
+       const char *line = btf__name_by_offset(btf, linfo->line_off);
+
+       if (line)
+               jsonw_string_field(json_wtr, "src", ltrim(line));
+
+       if (linum) {
+               const char *file = btf__name_by_offset(btf, linfo->file_name_off);
+
+               if (file)
+                       jsonw_string_field(json_wtr, "file", file);
+
+               if (BPF_LINE_INFO_LINE_NUM(linfo->line_col))
+                       jsonw_int_field(json_wtr, "line_num",
+                                       BPF_LINE_INFO_LINE_NUM(linfo->line_col));
+
+               if (BPF_LINE_INFO_LINE_COL(linfo->line_col))
+                       jsonw_int_field(json_wtr, "line_col",
+                                       BPF_LINE_INFO_LINE_COL(linfo->line_col));
+       }
+}
index 545a92471c3365540694059310e2d2cb5d436c34..f381f8628ce99c7cf89cc45d818341d6e95ec753 100644 (file)
@@ -21,6 +21,7 @@
 #include <dis-asm.h>
 #include <sys/stat.h>
 #include <limits.h>
+#include <libbpf.h>
 
 #include "json_writer.h"
 #include "main.h"
@@ -68,10 +69,16 @@ static int fprintf_json(void *out, const char *fmt, ...)
 }
 
 void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
-                      const char *arch, const char *disassembler_options)
+                      const char *arch, const char *disassembler_options,
+                      const struct btf *btf,
+                      const struct bpf_prog_linfo *prog_linfo,
+                      __u64 func_ksym, unsigned int func_idx,
+                      bool linum)
 {
+       const struct bpf_line_info *linfo = NULL;
        disassembler_ftype disassemble;
        struct disassemble_info info;
+       unsigned int nr_skip = 0;
        int count, i, pc = 0;
        char tpath[PATH_MAX];
        bfd *bfdf;
@@ -127,12 +134,26 @@ void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
        if (json_output)
                jsonw_start_array(json_wtr);
        do {
+               if (prog_linfo) {
+                       linfo = bpf_prog_linfo__lfind_addr_func(prog_linfo,
+                                                               func_ksym + pc,
+                                                               func_idx,
+                                                               nr_skip);
+                       if (linfo)
+                               nr_skip++;
+               }
+
                if (json_output) {
                        jsonw_start_object(json_wtr);
                        oper_count = 0;
+                       if (linfo)
+                               btf_dump_linfo_json(btf, linfo, linum);
                        jsonw_name(json_wtr, "pc");
                        jsonw_printf(json_wtr, "\"0x%x\"", pc);
                } else {
+                       if (linfo)
+                               btf_dump_linfo_plain(btf, linfo, "; ",
+                                                    linum);
                        printf("%4x:\t", pc);
                }
 
index 0be0dd8f467ff5c053bc26cdd85c7e3600bbb147..d9393abdba7826d74c58713499f1427413235a3d 100644 (file)
@@ -138,6 +138,9 @@ struct pinned_obj {
        struct hlist_node hash;
 };
 
+struct btf;
+struct bpf_line_info;
+
 int build_pinned_obj_table(struct pinned_obj_table *table,
                           enum bpf_obj_type type);
 void delete_pinned_obj_table(struct pinned_obj_table *tab);
@@ -175,13 +178,23 @@ int map_parse_fd(int *argc, char ***argv);
 int map_parse_fd_and_info(int *argc, char ***argv, void *info, __u32 *info_len);
 
 #ifdef HAVE_LIBBFD_SUPPORT
+struct bpf_prog_linfo;
 void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
-                      const char *arch, const char *disassembler_options);
+                      const char *arch, const char *disassembler_options,
+                      const struct btf *btf,
+                      const struct bpf_prog_linfo *prog_linfo,
+                      __u64 func_ksym, unsigned int func_idx,
+                      bool linum);
 int disasm_init(void);
 #else
 static inline
 void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
-                      const char *arch, const char *disassembler_options)
+                      const char *arch, const char *disassembler_options,
+                      const struct btf *btf,
+                      const struct bpf_prog_linfo *prog_linfo,
+                      __u64 func_ksym, unsigned int func_idx,
+                      bool linum)
+
 {
 }
 static inline int disasm_init(void)
@@ -217,6 +230,12 @@ int btf_dumper_type(const struct btf_dumper *d, __u32 type_id,
 void btf_dumper_type_only(const struct btf *btf, __u32 func_type_id,
                          char *func_only, int size);
 
+void btf_dump_linfo_plain(const struct btf *btf,
+                         const struct bpf_line_info *linfo,
+                         const char *prefix, bool linum);
+void btf_dump_linfo_json(const struct btf *btf,
+                        const struct bpf_line_info *linfo, bool linum);
+
 struct nlattr;
 struct ifinfomsg;
 struct tcmsg;
index a9a51123454c3e4fb8f1d0338881383b3eb6e9a9..65b921ffd10a13d159fc1d9260a3aa95a1e31c29 100644 (file)
@@ -423,24 +423,26 @@ static int do_show(int argc, char **argv)
 
 static int do_dump(int argc, char **argv)
 {
+       unsigned int finfo_rec_size, linfo_rec_size, jited_linfo_rec_size;
+       void *func_info = NULL, *linfo = NULL, *jited_linfo = NULL;
+       unsigned int finfo_cnt, linfo_cnt = 0, jited_linfo_cnt = 0;
+       struct bpf_prog_linfo *prog_linfo = NULL;
        unsigned long *func_ksyms = NULL;
        struct bpf_prog_info info = {};
        unsigned int *func_lens = NULL;
        const char *disasm_opt = NULL;
-       unsigned int finfo_rec_size;
        unsigned int nr_func_ksyms;
        unsigned int nr_func_lens;
        struct dump_data dd = {};
        __u32 len = sizeof(info);
        struct btf *btf = NULL;
-       void *func_info = NULL;
-       unsigned int finfo_cnt;
        unsigned int buf_size;
        char *filepath = NULL;
        bool opcodes = false;
        bool visual = false;
        char func_sig[1024];
        unsigned char *buf;
+       bool linum = false;
        __u32 *member_len;
        __u64 *member_ptr;
        ssize_t n;
@@ -484,6 +486,9 @@ static int do_dump(int argc, char **argv)
        } else if (is_prefix(*argv, "visual")) {
                visual = true;
                NEXT_ARG();
+       } else if (is_prefix(*argv, "linum")) {
+               linum = true;
+               NEXT_ARG();
        }
 
        if (argc) {
@@ -543,6 +548,32 @@ static int do_dump(int argc, char **argv)
                }
        }
 
+       linfo_rec_size = info.line_info_rec_size;
+       if (info.line_info_cnt && linfo_rec_size && info.btf_id) {
+               linfo_cnt = info.line_info_cnt;
+               linfo = malloc(linfo_cnt * linfo_rec_size);
+               if (!linfo) {
+                       p_err("mem alloc failed");
+                       close(fd);
+                       goto err_free;
+               }
+       }
+
+       jited_linfo_rec_size = info.jited_line_info_rec_size;
+       if (info.jited_line_info_cnt &&
+           jited_linfo_rec_size &&
+           info.nr_jited_ksyms &&
+           info.nr_jited_func_lens &&
+           info.btf_id) {
+               jited_linfo_cnt = info.jited_line_info_cnt;
+               jited_linfo = malloc(jited_linfo_cnt * jited_linfo_rec_size);
+               if (!jited_linfo) {
+                       p_err("mem alloc failed");
+                       close(fd);
+                       goto err_free;
+               }
+       }
+
        memset(&info, 0, sizeof(info));
 
        *member_ptr = ptr_to_u64(buf);
@@ -554,6 +585,13 @@ static int do_dump(int argc, char **argv)
        info.func_info_cnt = finfo_cnt;
        info.func_info_rec_size = finfo_rec_size;
        info.func_info = ptr_to_u64(func_info);
+       info.line_info_cnt = linfo_cnt;
+       info.line_info_rec_size = linfo_rec_size;
+       info.line_info = ptr_to_u64(linfo);
+       info.jited_line_info_cnt = jited_linfo_cnt;
+       info.jited_line_info_rec_size = jited_linfo_rec_size;
+       info.jited_line_info = ptr_to_u64(jited_linfo);
+
 
        err = bpf_obj_get_info_by_fd(fd, &info, &len);
        close(fd);
@@ -596,6 +634,30 @@ static int do_dump(int argc, char **argv)
                finfo_cnt = 0;
        }
 
+       if (linfo && info.line_info_cnt != linfo_cnt) {
+               p_err("incorrect line_info_cnt %u vs. expected %u",
+                     info.line_info_cnt, linfo_cnt);
+               goto err_free;
+       }
+
+       if (info.line_info_rec_size != linfo_rec_size) {
+               p_err("incorrect line_info_rec_size %u vs. expected %u",
+                     info.line_info_rec_size, linfo_rec_size);
+               goto err_free;
+       }
+
+       if (jited_linfo && info.jited_line_info_cnt != jited_linfo_cnt) {
+               p_err("incorrect jited_line_info_cnt %u vs. expected %u",
+                     info.jited_line_info_cnt, jited_linfo_cnt);
+               goto err_free;
+       }
+
+       if (info.jited_line_info_rec_size != jited_linfo_rec_size) {
+               p_err("incorrect jited_line_info_rec_size %u vs. expected %u",
+                     info.jited_line_info_rec_size, jited_linfo_rec_size);
+               goto err_free;
+       }
+
        if ((member_len == &info.jited_prog_len &&
             info.jited_prog_insns == 0) ||
            (member_len == &info.xlated_prog_len &&
@@ -609,6 +671,12 @@ static int do_dump(int argc, char **argv)
                goto err_free;
        }
 
+       if (linfo_cnt) {
+               prog_linfo = bpf_prog_linfo__new(&info);
+               if (!prog_linfo)
+                       p_err("error in processing bpf_line_info.  continue without it.");
+       }
+
        if (filepath) {
                fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600);
                if (fd < 0) {
@@ -690,8 +758,11 @@ static int do_dump(int argc, char **argv)
                                        printf("%s:\n", sym_name);
                                }
 
-                               disasm_print_insn(img, lens[i], opcodes, name,
-                                                 disasm_opt);
+                               disasm_print_insn(img, lens[i], opcodes,
+                                                 name, disasm_opt, btf,
+                                                 prog_linfo, ksyms[i], i,
+                                                 linum);
+
                                img += lens[i];
 
                                if (json_output)
@@ -704,7 +775,7 @@ static int do_dump(int argc, char **argv)
                                jsonw_end_array(json_wtr);
                } else {
                        disasm_print_insn(buf, *member_len, opcodes, name,
-                                         disasm_opt);
+                                         disasm_opt, btf, NULL, 0, 0, false);
                }
        } else if (visual) {
                if (json_output)
@@ -718,11 +789,14 @@ static int do_dump(int argc, char **argv)
                dd.btf = btf;
                dd.func_info = func_info;
                dd.finfo_rec_size = finfo_rec_size;
+               dd.prog_linfo = prog_linfo;
 
                if (json_output)
-                       dump_xlated_json(&dd, buf, *member_len, opcodes);
+                       dump_xlated_json(&dd, buf, *member_len, opcodes,
+                                        linum);
                else
-                       dump_xlated_plain(&dd, buf, *member_len, opcodes);
+                       dump_xlated_plain(&dd, buf, *member_len, opcodes,
+                                         linum);
                kernel_syms_destroy(&dd);
        }
 
@@ -730,6 +804,9 @@ static int do_dump(int argc, char **argv)
        free(func_ksyms);
        free(func_lens);
        free(func_info);
+       free(linfo);
+       free(jited_linfo);
+       bpf_prog_linfo__free(prog_linfo);
        return 0;
 
 err_free:
@@ -737,6 +814,9 @@ err_free:
        free(func_ksyms);
        free(func_lens);
        free(func_info);
+       free(linfo);
+       free(jited_linfo);
+       bpf_prog_linfo__free(prog_linfo);
        return -1;
 }
 
@@ -1138,8 +1218,8 @@ static int do_help(int argc, char **argv)
 
        fprintf(stderr,
                "Usage: %s %s { show | list } [PROG]\n"
-               "       %s %s dump xlated PROG [{ file FILE | opcodes | visual }]\n"
-               "       %s %s dump jited  PROG [{ file FILE | opcodes }]\n"
+               "       %s %s dump xlated PROG [{ file FILE | opcodes | visual | linum }]\n"
+               "       %s %s dump jited  PROG [{ file FILE | opcodes | linum }]\n"
                "       %s %s pin   PROG FILE\n"
                "       %s %s { load | loadall } OBJ  PATH \\\n"
                "                         [type TYPE] [dev NAME] \\\n"
index 131ecd175533046f3ae026ff54b59dadd1f8137f..aef628dcccb6a793077ae936699e667802f69939 100644 (file)
@@ -41,6 +41,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
+#include <libbpf.h>
 
 #include "disasm.h"
 #include "json_writer.h"
@@ -234,8 +235,9 @@ static const char *print_imm(void *private_data,
 }
 
 void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len,
-                     bool opcodes)
+                     bool opcodes, bool linum)
 {
+       const struct bpf_prog_linfo *prog_linfo = dd->prog_linfo;
        const struct bpf_insn_cbs cbs = {
                .cb_print       = print_insn_json,
                .cb_call        = print_call,
@@ -246,6 +248,7 @@ void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len,
        struct bpf_insn *insn = buf;
        struct btf *btf = dd->btf;
        bool double_insn = false;
+       unsigned int nr_skip = 0;
        char func_sig[1024];
        unsigned int i;
 
@@ -273,6 +276,16 @@ void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len,
                        }
                }
 
+               if (prog_linfo) {
+                       const struct bpf_line_info *linfo;
+
+                       linfo = bpf_prog_linfo__lfind(prog_linfo, i, nr_skip);
+                       if (linfo) {
+                               btf_dump_linfo_json(btf, linfo, linum);
+                               nr_skip++;
+                       }
+               }
+
                jsonw_name(json_wtr, "disasm");
                print_bpf_insn(&cbs, insn + i, true);
 
@@ -307,8 +320,9 @@ void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len,
 }
 
 void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len,
-                      bool opcodes)
+                      bool opcodes, bool linum)
 {
+       const struct bpf_prog_linfo *prog_linfo = dd->prog_linfo;
        const struct bpf_insn_cbs cbs = {
                .cb_print       = print_insn,
                .cb_call        = print_call,
@@ -318,6 +332,7 @@ void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len,
        struct bpf_func_info *record;
        struct bpf_insn *insn = buf;
        struct btf *btf = dd->btf;
+       unsigned int nr_skip = 0;
        bool double_insn = false;
        char func_sig[1024];
        unsigned int i;
@@ -340,6 +355,17 @@ void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len,
                        }
                }
 
+               if (prog_linfo) {
+                       const struct bpf_line_info *linfo;
+
+                       linfo = bpf_prog_linfo__lfind(prog_linfo, i, nr_skip);
+                       if (linfo) {
+                               btf_dump_linfo_plain(btf, linfo, "; ",
+                                                    linum);
+                               nr_skip++;
+                       }
+               }
+
                double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
 
                printf("% 4d: ", i);
index aec31723e1e5dd357c81bf109bd14c209102149d..a24f89df8cb207f7159a0edc26c2ca5070a47148 100644 (file)
@@ -40,6 +40,8 @@
 
 #define SYM_MAX_NAME   256
 
+struct bpf_prog_linfo;
+
 struct kernel_sym {
        unsigned long address;
        char name[SYM_MAX_NAME];
@@ -54,6 +56,7 @@ struct dump_data {
        struct btf *btf;
        void *func_info;
        __u32 finfo_rec_size;
+       const struct bpf_prog_linfo *prog_linfo;
        char scratch_buff[SYM_MAX_NAME + 8];
 };
 
@@ -61,9 +64,9 @@ void kernel_syms_load(struct dump_data *dd);
 void kernel_syms_destroy(struct dump_data *dd);
 struct kernel_sym *kernel_syms_search(struct dump_data *dd, unsigned long key);
 void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len,
-                     bool opcodes);
+                      bool opcodes, bool linum);
 void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len,
-                      bool opcodes);
+                      bool opcodes, bool linum);
 void dump_xlated_for_graph(struct dump_data *dd, void *buf, void *buf_end,
                           unsigned int start_index);
 
index 7bc31c90501846cc045f956c87e8035b140e0be2..197b40f5b5c64b1926b3be667d920b8f2a2fee3c 100644 (file)
@@ -1 +1 @@
-libbpf-y := libbpf.o bpf.o nlattr.o btf.o libbpf_errno.o str_error.o netlink.o
+libbpf-y := libbpf.o bpf.o nlattr.o btf.o libbpf_errno.o str_error.o netlink.o bpf_prog_linfo.o
diff --git a/tools/lib/bpf/bpf_prog_linfo.c b/tools/lib/bpf/bpf_prog_linfo.c
new file mode 100644 (file)
index 0000000..b8af651
--- /dev/null
@@ -0,0 +1,253 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+/* Copyright (c) 2018 Facebook */
+
+#include <string.h>
+#include <stdlib.h>
+#include <linux/err.h>
+#include <linux/bpf.h>
+#include "libbpf.h"
+
+#ifndef min
+#define min(x, y) ((x) < (y) ? (x) : (y))
+#endif
+
+struct bpf_prog_linfo {
+       void *raw_linfo;
+       void *raw_jited_linfo;
+       __u32 *nr_jited_linfo_per_func;
+       __u32 *jited_linfo_func_idx;
+       __u32 nr_linfo;
+       __u32 nr_jited_func;
+       __u32 rec_size;
+       __u32 jited_rec_size;
+};
+
+static int dissect_jited_func(struct bpf_prog_linfo *prog_linfo,
+                             const __u64 *ksym_func, const __u32 *ksym_len)
+{
+       __u32 nr_jited_func, nr_linfo;
+       const void *raw_jited_linfo;
+       const __u64 *jited_linfo;
+       __u64 last_jited_linfo;
+       /*
+        * Index to raw_jited_linfo:
+        *      i: Index for searching the next ksym_func
+        * prev_i: Index to the last found ksym_func
+        */
+       __u32 i, prev_i;
+       __u32 f; /* Index to ksym_func */
+
+       raw_jited_linfo = prog_linfo->raw_jited_linfo;
+       jited_linfo = raw_jited_linfo;
+       if (ksym_func[0] != *jited_linfo)
+               goto errout;
+
+       prog_linfo->jited_linfo_func_idx[0] = 0;
+       nr_jited_func = prog_linfo->nr_jited_func;
+       nr_linfo = prog_linfo->nr_linfo;
+
+       for (prev_i = 0, i = 1, f = 1;
+            i < nr_linfo && f < nr_jited_func;
+            i++) {
+               raw_jited_linfo += prog_linfo->jited_rec_size;
+               last_jited_linfo = *jited_linfo;
+               jited_linfo = raw_jited_linfo;
+
+               if (ksym_func[f] == *jited_linfo) {
+                       prog_linfo->jited_linfo_func_idx[f] = i;
+
+                       /* Sanity check */
+                       if (last_jited_linfo - ksym_func[f - 1] + 1 >
+                           ksym_len[f - 1])
+                               goto errout;
+
+                       prog_linfo->nr_jited_linfo_per_func[f - 1] =
+                               i - prev_i;
+                       prev_i = i;
+
+                       /*
+                        * The ksym_func[f] is found in jited_linfo.
+                        * Look for the next one.
+                        */
+                       f++;
+               } else if (*jited_linfo <= last_jited_linfo) {
+                       /* Ensure the addr is increasing _within_ a func */
+                       goto errout;
+               }
+       }
+
+       if (f != nr_jited_func)
+               goto errout;
+
+       prog_linfo->nr_jited_linfo_per_func[nr_jited_func - 1] =
+               nr_linfo - prev_i;
+
+       return 0;
+
+errout:
+       return -EINVAL;
+}
+
+void bpf_prog_linfo__free(struct bpf_prog_linfo *prog_linfo)
+{
+       if (!prog_linfo)
+               return;
+
+       free(prog_linfo->raw_linfo);
+       free(prog_linfo->raw_jited_linfo);
+       free(prog_linfo->nr_jited_linfo_per_func);
+       free(prog_linfo->jited_linfo_func_idx);
+       free(prog_linfo);
+}
+
+struct bpf_prog_linfo *bpf_prog_linfo__new(const struct bpf_prog_info *info)
+{
+       struct bpf_prog_linfo *prog_linfo;
+       __u32 nr_linfo, nr_jited_func;
+
+       nr_linfo = info->line_info_cnt;
+
+       /*
+        * Test !info->line_info because the kernel may NULL
+        * the ptr if kernel.kptr_restrict is set.
+        */
+       if (!nr_linfo || !info->line_info)
+               return NULL;
+
+       /*
+        * The min size that bpf_prog_linfo has to access for
+        * searching purpose.
+        */
+       if (info->line_info_rec_size <
+           offsetof(struct bpf_line_info, file_name_off))
+               return NULL;
+
+       prog_linfo = calloc(1, sizeof(*prog_linfo));
+       if (!prog_linfo)
+               return NULL;
+
+       /* Copy xlated line_info */
+       prog_linfo->nr_linfo = nr_linfo;
+       prog_linfo->rec_size = info->line_info_rec_size;
+       prog_linfo->raw_linfo = malloc(nr_linfo * prog_linfo->rec_size);
+       if (!prog_linfo->raw_linfo)
+               goto err_free;
+       memcpy(prog_linfo->raw_linfo, (void *)(long)info->line_info,
+              nr_linfo * prog_linfo->rec_size);
+
+       nr_jited_func = info->nr_jited_ksyms;
+       if (!nr_jited_func ||
+           !info->jited_line_info ||
+           info->jited_line_info_cnt != nr_linfo ||
+           info->jited_line_info_rec_size < sizeof(__u64) ||
+           info->nr_jited_func_lens != nr_jited_func ||
+           !info->jited_ksyms ||
+           !info->jited_func_lens)
+               /* Not enough info to provide jited_line_info */
+               return prog_linfo;
+
+       /* Copy jited_line_info */
+       prog_linfo->nr_jited_func = nr_jited_func;
+       prog_linfo->jited_rec_size = info->jited_line_info_rec_size;
+       prog_linfo->raw_jited_linfo = malloc(nr_linfo *
+                                            prog_linfo->jited_rec_size);
+       if (!prog_linfo->raw_jited_linfo)
+               goto err_free;
+       memcpy(prog_linfo->raw_jited_linfo,
+              (void *)(long)info->jited_line_info,
+              nr_linfo * prog_linfo->jited_rec_size);
+
+       /* Number of jited_line_info per jited func */
+       prog_linfo->nr_jited_linfo_per_func = malloc(nr_jited_func *
+                                                    sizeof(__u32));
+       if (!prog_linfo->nr_jited_linfo_per_func)
+               goto err_free;
+
+       /*
+        * For each jited func,
+        * the start idx to the "linfo" and "jited_linfo" array,
+        */
+       prog_linfo->jited_linfo_func_idx = malloc(nr_jited_func *
+                                                 sizeof(__u32));
+       if (!prog_linfo->jited_linfo_func_idx)
+               goto err_free;
+
+       if (dissect_jited_func(prog_linfo,
+                              (__u64 *)(long)info->jited_ksyms,
+                              (__u32 *)(long)info->jited_func_lens))
+               goto err_free;
+
+       return prog_linfo;
+
+err_free:
+       bpf_prog_linfo__free(prog_linfo);
+       return NULL;
+}
+
+const struct bpf_line_info *
+bpf_prog_linfo__lfind_addr_func(const struct bpf_prog_linfo *prog_linfo,
+                               __u64 addr, __u32 func_idx, __u32 nr_skip)
+{
+       __u32 jited_rec_size, rec_size, nr_linfo, start, i;
+       const void *raw_jited_linfo, *raw_linfo;
+       const __u64 *jited_linfo;
+
+       if (func_idx >= prog_linfo->nr_jited_func)
+               return NULL;
+
+       nr_linfo = prog_linfo->nr_jited_linfo_per_func[func_idx];
+       if (nr_skip >= nr_linfo)
+               return NULL;
+
+       start = prog_linfo->jited_linfo_func_idx[func_idx] + nr_skip;
+       jited_rec_size = prog_linfo->jited_rec_size;
+       raw_jited_linfo = prog_linfo->raw_jited_linfo +
+               (start * jited_rec_size);
+       jited_linfo = raw_jited_linfo;
+       if (addr < *jited_linfo)
+               return NULL;
+
+       nr_linfo -= nr_skip;
+       rec_size = prog_linfo->rec_size;
+       raw_linfo = prog_linfo->raw_linfo + (start * rec_size);
+       for (i = 0; i < nr_linfo; i++) {
+               if (addr < *jited_linfo)
+                       break;
+
+               raw_linfo += rec_size;
+               raw_jited_linfo += jited_rec_size;
+               jited_linfo = raw_jited_linfo;
+       }
+
+       return raw_linfo - rec_size;
+}
+
+const struct bpf_line_info *
+bpf_prog_linfo__lfind(const struct bpf_prog_linfo *prog_linfo,
+                     __u32 insn_off, __u32 nr_skip)
+{
+       const struct bpf_line_info *linfo;
+       __u32 rec_size, nr_linfo, i;
+       const void *raw_linfo;
+
+       nr_linfo = prog_linfo->nr_linfo;
+       if (nr_skip >= nr_linfo)
+               return NULL;
+
+       rec_size = prog_linfo->rec_size;
+       raw_linfo = prog_linfo->raw_linfo + (nr_skip * rec_size);
+       linfo = raw_linfo;
+       if (insn_off < linfo->insn_off)
+               return NULL;
+
+       nr_linfo -= nr_skip;
+       for (i = 0; i < nr_linfo; i++) {
+               if (insn_off < linfo->insn_off)
+                       break;
+
+               raw_linfo += rec_size;
+               linfo = raw_linfo;
+       }
+
+       return raw_linfo - rec_size;
+}
index f30c3d07bb7db9da44de922e157b85579f0338f2..5f68d7b75215c1e3eb99c4a2b69e280c7239f697 100644 (file)
@@ -342,6 +342,19 @@ int libbpf_nl_get_qdisc(int sock, unsigned int nl_pid, int ifindex,
 int libbpf_nl_get_filter(int sock, unsigned int nl_pid, int ifindex, int handle,
                         libbpf_dump_nlmsg_t dump_filter_nlmsg, void *cookie);
 
+struct bpf_prog_linfo;
+struct bpf_prog_info;
+
+LIBBPF_API void bpf_prog_linfo__free(struct bpf_prog_linfo *prog_linfo);
+LIBBPF_API struct bpf_prog_linfo *
+bpf_prog_linfo__new(const struct bpf_prog_info *info);
+LIBBPF_API const struct bpf_line_info *
+bpf_prog_linfo__lfind_addr_func(const struct bpf_prog_linfo *prog_linfo,
+                               __u64 addr, __u32 func_idx, __u32 nr_skip);
+LIBBPF_API const struct bpf_line_info *
+bpf_prog_linfo__lfind(const struct bpf_prog_linfo *prog_linfo,
+                     __u32 insn_off, __u32 nr_skip);
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif
index 8deff22d61bba2a52989ec8845bf4846b210b258..cd02cd4e2cc3508f2d2edc58d680a6be7360a41b 100644 (file)
@@ -99,6 +99,10 @@ LIBBPF_0.0.1 {
                bpf_program__unload;
                bpf_program__unpin;
                bpf_program__unpin_instance;
+               bpf_prog_linfo__free;
+               bpf_prog_linfo__new;
+               bpf_prog_linfo__lfind_addr_func;
+               bpf_prog_linfo__lfind;
                bpf_raw_tracepoint_open;
                bpf_set_link_xdp_fd;
                bpf_task_fd_query;