bpf: libbpf: bpftool: Print bpf_line_info during prog dump
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>
This commit is contained in:
Родитель
3d65014146
Коммит
b053b439b7
|
@ -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*.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче