mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 16:48:26 +02:00 
			
		
		
		
	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:
		
							parent
							
								
									3d65014146
								
							
						
					
					
						commit
						b053b439b7
					
				
					 12 changed files with 516 additions and 25 deletions
				
			
		|  | @ -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 @@ 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 -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 | ||||
|  |  | |||
							
								
								
									
										253
									
								
								tools/lib/bpf/bpf_prog_linfo.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										253
									
								
								tools/lib/bpf/bpf_prog_linfo.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -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; | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Martin KaFai Lau
						Martin KaFai Lau