[PATCH v3 10/18] perf dso: Support BPF programs in dso__read_symbol

Ian Rogers posted 18 patches 11 months ago
There is a newer version of this series
[PATCH v3 10/18] perf dso: Support BPF programs in dso__read_symbol
Posted by Ian Rogers 11 months ago
Set the buffer to the code in the BPF linear info. This enables BPF
JIT code disassembly by LLVM and capstone. Move the disassmble_bpf
calls to disassemble_objdump so that they are only called after
falling back to the objdump option.

Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/util/disasm.c | 12 +++---
 tools/perf/util/dso.c    | 85 +++++++++++++++++++++++++---------------
 2 files changed, 60 insertions(+), 37 deletions(-)

diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c
index a9cc588a3006..99b9c21e02b0 100644
--- a/tools/perf/util/disasm.c
+++ b/tools/perf/util/disasm.c
@@ -1500,6 +1500,12 @@ static int symbol__disassemble_objdump(const char *filename, struct symbol *sym,
 	struct child_process objdump_process;
 	int err;
 
+	if (dso__binary_type(dso) == DSO_BINARY_TYPE__BPF_PROG_INFO)
+		return symbol__disassemble_bpf(sym, args);
+
+	if (dso__binary_type(dso) == DSO_BINARY_TYPE__BPF_IMAGE)
+		return symbol__disassemble_bpf_image(sym, args);
+
 	err = asprintf(&command,
 		 "%s %s%s --start-address=0x%016" PRIx64
 		 " --stop-address=0x%016" PRIx64
@@ -1681,11 +1687,7 @@ int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
 
 	pr_debug("annotating [%p] %30s : [%p] %30s\n", dso, dso__long_name(dso), sym, sym->name);
 
-	if (dso__binary_type(dso) == DSO_BINARY_TYPE__BPF_PROG_INFO) {
-		return symbol__disassemble_bpf(sym, args);
-	} else if (dso__binary_type(dso) == DSO_BINARY_TYPE__BPF_IMAGE) {
-		return symbol__disassemble_bpf_image(sym, args);
-	} else if (dso__binary_type(dso) == DSO_BINARY_TYPE__NOT_FOUND) {
+	if (dso__binary_type(dso) == DSO_BINARY_TYPE__NOT_FOUND) {
 		return SYMBOL_ANNOTATE_ERRNO__COULDNT_DETERMINE_FILE_TYPE;
 	} else if (dso__is_kcore(dso)) {
 		kce.addr = map__rip_2objdump(map, sym->start);
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index 0285904ed26d..a90799bed230 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -1686,48 +1686,69 @@ const u8 *dso__read_symbol(struct dso *dso, const char *symfs_filename,
 			   const struct map *map, const struct symbol *sym,
 			   u8 **out_buf, u64 *out_buf_len, bool *is_64bit)
 {
-	struct nscookie nsc;
 	u64 start = map__rip_2objdump(map, sym->start);
 	u64 end = map__rip_2objdump(map, sym->end);
-	int fd, count;
-	u8 *buf = NULL;
-	size_t len;
-	struct find_file_offset_data data = {
-		.ip = start,
-	};
+	const u8 *buf;
+	size_t len = end - start;
 
 	*out_buf = NULL;
 	*out_buf_len = 0;
 	*is_64bit = false;
 
-	nsinfo__mountns_enter(dso__nsinfo(dso), &nsc);
-	fd = open(symfs_filename, O_RDONLY);
-	nsinfo__mountns_exit(&nsc);
-	if (fd < 0)
+	if (dso__binary_type(dso) == DSO_BINARY_TYPE__BPF_IMAGE) {
+		pr_debug("No BPF image disassembly support\n");
 		return NULL;
+	} else if (dso__binary_type(dso) == DSO_BINARY_TYPE__BPF_PROG_INFO) {
+#ifdef HAVE_LIBBPF_SUPPORT
+		struct bpf_prog_info_node *info_node;
+		struct perf_bpil *info_linear;
+
+		*is_64bit = sizeof(void *) == sizeof(u64);
+		info_node = perf_env__find_bpf_prog_info(dso__bpf_prog(dso)->env,
+							 dso__bpf_prog(dso)->id);
+		if (!info_node) {
+			errno = SYMBOL_ANNOTATE_ERRNO__BPF_MISSING_BTF;
+			return NULL;
+		}
+		info_linear = info_node->info_linear;
+		buf = (const u8 *)(uintptr_t)(info_linear->info.jited_prog_insns);
+		assert(len <= info_linear->info.jited_prog_len);
+#else
+		pr_debug("No BPF program disassembly support\n");
+		return NULL;
+#endif
+	} else {
+		struct nscookie nsc;
+		int fd;
+		ssize_t count;
+		struct find_file_offset_data data = {
+			.ip = start,
+		};
+		u8 *code_buf = NULL;
 
-	if (file__read_maps(fd, /*exe=*/true, find_file_offset, &data, is_64bit) == 0)
-		goto err;
-
-	len = end - start;
-	buf = malloc(len);
-	if (buf == NULL)
-		goto err;
-
-	count = pread(fd, buf, len, data.offset);
-	close(fd);
-	fd = -1;
-
-	if ((u64)count != len)
-		goto err;
+		nsinfo__mountns_enter(dso__nsinfo(dso), &nsc);
+		fd = open(symfs_filename, O_RDONLY);
+		nsinfo__mountns_exit(&nsc);
+		if (fd < 0)
+			return NULL;
 
-	*out_buf = buf;
+		if (file__read_maps(fd, /*exe=*/true, find_file_offset, &data, is_64bit) == 0) {
+			close(fd);
+			return NULL;
+		}
+		buf = code_buf = malloc(len);
+		if (buf == NULL) {
+			close(fd);
+			return NULL;
+		}
+		count = pread(fd, code_buf, len, data.offset);
+		close(fd);
+		if ((u64)count != len) {
+			free(code_buf);
+			return NULL;
+		}
+		*out_buf = code_buf;
+	}
 	*out_buf_len = len;
 	return buf;
-
-err:
-	if (fd >= 0)
-		close(fd);
-	free(buf);
-	return NULL;
 }
-- 
2.48.1.262.g85cc9f2d1e-goog
Re: [PATCH v3 10/18] perf dso: Support BPF programs in dso__read_symbol
Posted by Ian Rogers 8 months, 4 weeks ago
On Wed, Jan 22, 2025 at 9:43 AM Ian Rogers <irogers@google.com> wrote:
>
> Set the buffer to the code in the BPF linear info. This enables BPF
> JIT code disassembly by LLVM and capstone. Move the disassmble_bpf
> calls to disassemble_objdump so that they are only called after
> falling back to the objdump option.

It would be nice to land this series in part because of supporting BPF
disassembly with LLVM and capstone, possible because of the cleanups
and refactorings.

Thanks,
Ian

> Signed-off-by: Ian Rogers <irogers@google.com>
> ---
>  tools/perf/util/disasm.c | 12 +++---
>  tools/perf/util/dso.c    | 85 +++++++++++++++++++++++++---------------
>  2 files changed, 60 insertions(+), 37 deletions(-)
>
> diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c
> index a9cc588a3006..99b9c21e02b0 100644
> --- a/tools/perf/util/disasm.c
> +++ b/tools/perf/util/disasm.c
> @@ -1500,6 +1500,12 @@ static int symbol__disassemble_objdump(const char *filename, struct symbol *sym,
>         struct child_process objdump_process;
>         int err;
>
> +       if (dso__binary_type(dso) == DSO_BINARY_TYPE__BPF_PROG_INFO)
> +               return symbol__disassemble_bpf(sym, args);
> +
> +       if (dso__binary_type(dso) == DSO_BINARY_TYPE__BPF_IMAGE)
> +               return symbol__disassemble_bpf_image(sym, args);
> +
>         err = asprintf(&command,
>                  "%s %s%s --start-address=0x%016" PRIx64
>                  " --stop-address=0x%016" PRIx64
> @@ -1681,11 +1687,7 @@ int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
>
>         pr_debug("annotating [%p] %30s : [%p] %30s\n", dso, dso__long_name(dso), sym, sym->name);
>
> -       if (dso__binary_type(dso) == DSO_BINARY_TYPE__BPF_PROG_INFO) {
> -               return symbol__disassemble_bpf(sym, args);
> -       } else if (dso__binary_type(dso) == DSO_BINARY_TYPE__BPF_IMAGE) {
> -               return symbol__disassemble_bpf_image(sym, args);
> -       } else if (dso__binary_type(dso) == DSO_BINARY_TYPE__NOT_FOUND) {
> +       if (dso__binary_type(dso) == DSO_BINARY_TYPE__NOT_FOUND) {
>                 return SYMBOL_ANNOTATE_ERRNO__COULDNT_DETERMINE_FILE_TYPE;
>         } else if (dso__is_kcore(dso)) {
>                 kce.addr = map__rip_2objdump(map, sym->start);
> diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
> index 0285904ed26d..a90799bed230 100644
> --- a/tools/perf/util/dso.c
> +++ b/tools/perf/util/dso.c
> @@ -1686,48 +1686,69 @@ const u8 *dso__read_symbol(struct dso *dso, const char *symfs_filename,
>                            const struct map *map, const struct symbol *sym,
>                            u8 **out_buf, u64 *out_buf_len, bool *is_64bit)
>  {
> -       struct nscookie nsc;
>         u64 start = map__rip_2objdump(map, sym->start);
>         u64 end = map__rip_2objdump(map, sym->end);
> -       int fd, count;
> -       u8 *buf = NULL;
> -       size_t len;
> -       struct find_file_offset_data data = {
> -               .ip = start,
> -       };
> +       const u8 *buf;
> +       size_t len = end - start;
>
>         *out_buf = NULL;
>         *out_buf_len = 0;
>         *is_64bit = false;
>
> -       nsinfo__mountns_enter(dso__nsinfo(dso), &nsc);
> -       fd = open(symfs_filename, O_RDONLY);
> -       nsinfo__mountns_exit(&nsc);
> -       if (fd < 0)
> +       if (dso__binary_type(dso) == DSO_BINARY_TYPE__BPF_IMAGE) {
> +               pr_debug("No BPF image disassembly support\n");
>                 return NULL;
> +       } else if (dso__binary_type(dso) == DSO_BINARY_TYPE__BPF_PROG_INFO) {
> +#ifdef HAVE_LIBBPF_SUPPORT
> +               struct bpf_prog_info_node *info_node;
> +               struct perf_bpil *info_linear;
> +
> +               *is_64bit = sizeof(void *) == sizeof(u64);
> +               info_node = perf_env__find_bpf_prog_info(dso__bpf_prog(dso)->env,
> +                                                        dso__bpf_prog(dso)->id);
> +               if (!info_node) {
> +                       errno = SYMBOL_ANNOTATE_ERRNO__BPF_MISSING_BTF;
> +                       return NULL;
> +               }
> +               info_linear = info_node->info_linear;
> +               buf = (const u8 *)(uintptr_t)(info_linear->info.jited_prog_insns);
> +               assert(len <= info_linear->info.jited_prog_len);
> +#else
> +               pr_debug("No BPF program disassembly support\n");
> +               return NULL;
> +#endif
> +       } else {
> +               struct nscookie nsc;
> +               int fd;
> +               ssize_t count;
> +               struct find_file_offset_data data = {
> +                       .ip = start,
> +               };
> +               u8 *code_buf = NULL;
>
> -       if (file__read_maps(fd, /*exe=*/true, find_file_offset, &data, is_64bit) == 0)
> -               goto err;
> -
> -       len = end - start;
> -       buf = malloc(len);
> -       if (buf == NULL)
> -               goto err;
> -
> -       count = pread(fd, buf, len, data.offset);
> -       close(fd);
> -       fd = -1;
> -
> -       if ((u64)count != len)
> -               goto err;
> +               nsinfo__mountns_enter(dso__nsinfo(dso), &nsc);
> +               fd = open(symfs_filename, O_RDONLY);
> +               nsinfo__mountns_exit(&nsc);
> +               if (fd < 0)
> +                       return NULL;
>
> -       *out_buf = buf;
> +               if (file__read_maps(fd, /*exe=*/true, find_file_offset, &data, is_64bit) == 0) {
> +                       close(fd);
> +                       return NULL;
> +               }
> +               buf = code_buf = malloc(len);
> +               if (buf == NULL) {
> +                       close(fd);
> +                       return NULL;
> +               }
> +               count = pread(fd, code_buf, len, data.offset);
> +               close(fd);
> +               if ((u64)count != len) {
> +                       free(code_buf);
> +                       return NULL;
> +               }
> +               *out_buf = code_buf;
> +       }
>         *out_buf_len = len;
>         return buf;
> -
> -err:
> -       if (fd >= 0)
> -               close(fd);
> -       free(buf);
> -       return NULL;
>  }
> --
> 2.48.1.262.g85cc9f2d1e-goog
>