mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v2 0/7] perf annotate: Add elfutils libasm disassembler support
@ 2026-06-09  7:07 Ian Rogers
  2026-06-09  7:07 ` [PATCH v2 1/7] tools build: Add feature check for elfutils libasm Ian Rogers
                   ` (7 more replies)
  0 siblings, 8 replies; 16+ messages in thread
From: Ian Rogers @ 2026-06-09  7:07 UTC (permalink / raw)
  To: Serhei Makarov, mark, Peter Zijlstra, Ingo Molnar,
	Arnaldo Carvalho de Melo, Namhyung Kim, Jiri Olsa, Ian Rogers,
	Adrian Hunter, James Clark, Nick Terrell, David Sterba,
	Nathan Chancellor, Tomas Glozar, Blake Jones, Dmitrii Dolgov,
	Alexandre Chartre, Costa Shulyupin, Yuzhuo Jing, Michael Jeanson,
	Leo Yan, Tianyou Li, Zecheng Li, Rong Bao, linux-kernel,
	linux-perf-users, bpf

This series adds a new `libasm` disassembly backend to `perf annotate`,
leveraging the `elfutils` libasm library. This allows perf to use the
widely available elfutils for disassembly alongside capstone, libLLVM, and objdump.

v2 changes:
- Patch 1: Added a note to the commit message acknowledging that older elfutils static builds lacking `-lebl` dependencies in pkg-config are explicitly unsupported, while intentionally retaining the feature check and API dependencies.
- Patch 2: Fixed `NO_LIBELF=1` build breakages by implicitly disabling `libasm` and removing the unconditional `#warning` in favor of silent fallbacks or standard warnings.
- Patch 3: Fixed `libasm` block boundary tracking and corrected callback error/return values, preventing memory leaks and infinite loops in the disassembly extraction loop.
- Patch 5: Fixed script robustness in `tests/shell/annotate.sh` by ensuring cleanup functions run reliably, `set -e` doesn't abort prematurely on grep failures, and configs are properly restored.
- Patch 6: Refactored `jit_write_elf` to accept the `e_machine` parameter directly instead of post-patching the generated ELF file with `libelf`. The target architecture is now extracted dynamically and passed into `jit_write_elf`, simplifying the interaction with BPF and making it fully compatible with all disassemblers.
- Patch 7: Fixed a TOCTOU race condition when creating the symlink in `tests/shell/annotate.sh` by placing `perf.data` inside an isolated temporary directory.
- General: The cover letter now lists capstone, libLLVM, and objdump as the existing disassemblers instead of just objdump.

Ian Rogers (7):
  tools build: Add feature check for elfutils libasm
  perf build: Add build support and capability for elfutils libasm
  perf annotate: Implement elfutils libasm disassembler backend
  perf annotate: Add --disassembler command-line option
  perf test: Enhance annotate test coverage and isolate config
  perf annotate: Support BPF JIT disassembly via genelf
  perf test: Add BPF JIT annotation test coverage for all disassemblers

 tools/build/Makefile.feature       |   2 +
 tools/build/feature/Makefile       |   9 ++
 tools/build/feature/test-libasm.c  |  19 +++
 tools/perf/Makefile.config         |  22 ++++
 tools/perf/builtin-annotate.c      |  10 ++
 tools/perf/builtin-check.c         |   1 +
 tools/perf/tests/genelf.c          |   2 +-
 tools/perf/tests/shell/annotate.sh | 133 +++++++++++++++++++++
 tools/perf/util/Build              |   1 +
 tools/perf/util/annotate.c         |   8 +-
 tools/perf/util/annotate.h         |   3 +
 tools/perf/util/disasm.c           | 114 +++++++++++++++++-
 tools/perf/util/disasm.h           |   1 +
 tools/perf/util/genelf.c           |   4 +-
 tools/perf/util/genelf.h           |   2 +-
 tools/perf/util/jitdump.c          |   2 +-
 tools/perf/util/libasm.c           | 184 +++++++++++++++++++++++++++++
 tools/perf/util/libasm.h           |  27 +++++
 18 files changed, 531 insertions(+), 13 deletions(-)
 create mode 100644 tools/build/feature/test-libasm.c
 create mode 100644 tools/perf/util/libasm.c
 create mode 100644 tools/perf/util/libasm.h

--
2.54.0.1064.gd145956f57-goog

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v2 1/7] tools build: Add feature check for elfutils libasm
  2026-06-09  7:07 [PATCH v2 0/7] perf annotate: Add elfutils libasm disassembler support Ian Rogers
@ 2026-06-09  7:07 ` Ian Rogers
  2026-06-09  7:07 ` [PATCH v2 2/7] perf build: Add build support and capability " Ian Rogers
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 16+ messages in thread
From: Ian Rogers @ 2026-06-09  7:07 UTC (permalink / raw)
  To: Serhei Makarov, mark, Peter Zijlstra, Ingo Molnar,
	Arnaldo Carvalho de Melo, Namhyung Kim, Jiri Olsa, Ian Rogers,
	Adrian Hunter, James Clark, Nick Terrell, David Sterba,
	Nathan Chancellor, Tomas Glozar, Blake Jones, Dmitrii Dolgov,
	Alexandre Chartre, Costa Shulyupin, Yuzhuo Jing, Michael Jeanson,
	Leo Yan, Tianyou Li, Zecheng Li, Rong Bao, linux-kernel,
	linux-perf-users, bpf

This commit introduces a build-time feature test for the elfutils libasm
library in the shared tools/build system. It adds the test program and
updates the Makefiles to attempt building it to detect support.

Note on static linking: pkg-config for libdw/libelf on modern systems
does not list -lebl as a dependency. Older versions of elfutils that
require explicitly linking -lebl statically are unsupported by this
feature check.
---
 tools/build/Makefile.feature      |  2 ++
 tools/build/feature/Makefile      |  9 +++++++++
 tools/build/feature/test-libasm.c | 19 +++++++++++++++++++
 3 files changed, 30 insertions(+)
 create mode 100644 tools/build/feature/test-libasm.c

diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature
index ed1374af31c1..422329dbc20a 100644
--- a/tools/build/Makefile.feature
+++ b/tools/build/Makefile.feature
@@ -118,6 +118,7 @@ FEATURE_TESTS_EXTRA :=                  \
          hello                          \
          babeltrace2-ctf-writer         \
          libcapstone                    \
+         libasm                         \
          libcheck                       \
          libbfd-liberty                 \
          libbfd-liberty-z               \
@@ -150,6 +151,7 @@ FEATURE_DISPLAY ?=              \
          numa_num_possible_cpus \
          libpython              \
          libcapstone            \
+         libasm                 \
          llvm-perf              \
          zlib                   \
          lzma                   \
diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile
index 62909a9c799d..f0f39a2ab203 100644
--- a/tools/build/feature/Makefile
+++ b/tools/build/feature/Makefile
@@ -47,6 +47,7 @@ FILES=                                          \
          test-timerfd.bin                       \
          test-babeltrace2-ctf-writer.bin        \
          test-libcapstone.bin			\
+         test-libasm.bin			\
          test-libcheck.bin			\
          test-compile-32.bin                    \
          test-compile-x32.bin                   \
@@ -184,6 +185,11 @@ ifeq ($(findstring -static,${LDFLAGS}),-static)
   DWARFLIBS += -ldl
 endif
 
+ASMLIBS := -lasm -ldw -lelf
+ifeq ($(findstring -static,${LDFLAGS}),-static)
+  ASMLIBS += -lz -llzma -lbz2 -lzstd
+endif
+
 $(OUTPUT)test-libdw.bin:
 	$(BUILD) $(DWLIBS)
 
@@ -311,6 +317,9 @@ $(OUTPUT)test-babeltrace2-ctf-writer.bin:
 $(OUTPUT)test-libcapstone.bin:
 	$(BUILD) # -lcapstone provided by $(FEATURE_CHECK_LDFLAGS-libcapstone)
 
+$(OUTPUT)test-libasm.bin:
+	$(BUILD) $(ASMLIBS)
+
 $(OUTPUT)test-libcheck.bin:
 	$(BUILD) # -lcheck is provided by $(FEATURE_CHECK_LDFLAGS-libcheck)
 
diff --git a/tools/build/feature/test-libasm.c b/tools/build/feature/test-libasm.c
new file mode 100644
index 000000000000..f3598ad1cc9e
--- /dev/null
+++ b/tools/build/feature/test-libasm.c
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <stddef.h>
+#include <elfutils/libasm.h>
+#include <gelf.h>
+
+typedef struct ebl Ebl;
+extern Ebl *ebl_openbackend (Elf *elf);
+extern void ebl_closebackend (Ebl *ebl);
+
+int main(void)
+{
+	Elf *elf = elf_begin(0, ELF_C_READ, NULL);
+	Ebl *ebl = ebl_openbackend(elf);
+	DisasmCtx_t *ctx = disasm_begin(ebl, elf, NULL);
+	disasm_end(ctx);
+	ebl_closebackend(ebl);
+	elf_end(elf);
+	return 0;
+}
-- 
2.54.0.1064.gd145956f57-goog


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v2 2/7] perf build: Add build support and capability for elfutils libasm
  2026-06-09  7:07 [PATCH v2 0/7] perf annotate: Add elfutils libasm disassembler support Ian Rogers
  2026-06-09  7:07 ` [PATCH v2 1/7] tools build: Add feature check for elfutils libasm Ian Rogers
@ 2026-06-09  7:07 ` Ian Rogers
  2026-06-09  7:07 ` [PATCH v2 3/7] perf annotate: Implement elfutils libasm disassembler backend Ian Rogers
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 16+ messages in thread
From: Ian Rogers @ 2026-06-09  7:07 UTC (permalink / raw)
  To: Serhei Makarov, mark, Peter Zijlstra, Ingo Molnar,
	Arnaldo Carvalho de Melo, Namhyung Kim, Jiri Olsa, Ian Rogers,
	Adrian Hunter, James Clark, Nick Terrell, David Sterba,
	Nathan Chancellor, Tomas Glozar, Blake Jones, Dmitrii Dolgov,
	Alexandre Chartre, Costa Shulyupin, Yuzhuo Jing, Michael Jeanson,
	Leo Yan, Tianyou Li, Zecheng Li, Rong Bao, linux-kernel,
	linux-perf-users, bpf

Configure perf build system to utilize the libasm feature check
(supporting both static and dynamic builds) and export the config flags.
Add "libasm" capability detection to perf check.

Assisted-by: Antigravity:Google Gemini 3.5-flash
Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/Makefile.config | 22 ++++++++++++++++++++++
 tools/perf/builtin-check.c |  1 +
 2 files changed, 23 insertions(+)

diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config
index 6e7b15fab2ec..8527d20f498f 100644
--- a/tools/perf/Makefile.config
+++ b/tools/perf/Makefile.config
@@ -199,6 +199,13 @@ endif
 FEATURE_CHECK_CFLAGS-libcapstone := $(LIBCAPSTONE_CFLAGS)
 FEATURE_CHECK_LDFLAGS-libcapstone := $(LIBCAPSTONE_LDFLAGS) -lcapstone
 
+ifdef LIBASM_DIR
+  LIBASM_CFLAGS  := -I$(LIBASM_DIR)/include
+  LIBASM_LDFLAGS := -L$(LIBASM_DIR)/lib
+endif
+FEATURE_CHECK_CFLAGS-libasm := $(LIBASM_CFLAGS)
+FEATURE_CHECK_LDFLAGS-libasm := $(LIBASM_LDFLAGS) -lasm -ldw -lelf
+
 ifdef LIBZSTD_DIR
   LIBZSTD_CFLAGS  := -I$(LIBZSTD_DIR)/lib
   LIBZSTD_LDFLAGS := -L$(LIBZSTD_DIR)/lib
@@ -432,6 +439,7 @@ ifeq ($(feature-file-handle), 1)
 endif
 
 ifdef NO_LIBELF
+  NO_LIBASM := 1
   NO_LIBDW := 1
   NO_LIBUNWIND := 1
   NO_LIBBPF := 1
@@ -1060,6 +1068,20 @@ ifndef NO_CAPSTONE
   endif
 endif
 
+ifndef NO_LIBASM
+  $(call feature_check,libasm)
+  ifeq ($(feature-libasm), 1)
+    CFLAGS += -DHAVE_LIBASM_SUPPORT $(LIBASM_CFLAGS)
+    ifdef LIBASM_DLOPEN
+      CFLAGS += -DLIBASM_DLOPEN
+    else
+      LDFLAGS += $(LIBASM_LDFLAGS)
+      EXTLIBS += -lasm -ldw -lelf
+    endif
+    $(call detected,CONFIG_LIBASM)
+  endif
+endif
+
 ifdef EXTRA_TESTS
     $(call detected,CONFIG_EXTRA_TESTS)
     CFLAGS += -DHAVE_EXTRA_TESTS
diff --git a/tools/perf/builtin-check.c b/tools/perf/builtin-check.c
index 60437650c50f..d2ffea4d9b8b 100644
--- a/tools/perf/builtin-check.c
+++ b/tools/perf/builtin-check.c
@@ -46,6 +46,7 @@ struct feature_status supported_features[] = {
 	FEATURE_STATUS("babeltrace2-ctf-writer", HAVE_BABELTRACE2_CTF_WRITER_SUPPORT),
 	FEATURE_STATUS("libbpf-strings", HAVE_LIBBPF_STRINGS_SUPPORT),
 	FEATURE_STATUS("libcapstone", HAVE_LIBCAPSTONE_SUPPORT),
+	FEATURE_STATUS("libasm", HAVE_LIBASM_SUPPORT),
 	FEATURE_STATUS("libdw-dwarf-unwind", HAVE_LIBDW_SUPPORT),
 	FEATURE_STATUS("libelf", HAVE_LIBELF_SUPPORT),
 	FEATURE_STATUS("libLLVM", HAVE_LIBLLVM_SUPPORT),
-- 
2.54.0.1064.gd145956f57-goog


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v2 3/7] perf annotate: Implement elfutils libasm disassembler backend
  2026-06-09  7:07 [PATCH v2 0/7] perf annotate: Add elfutils libasm disassembler support Ian Rogers
  2026-06-09  7:07 ` [PATCH v2 1/7] tools build: Add feature check for elfutils libasm Ian Rogers
  2026-06-09  7:07 ` [PATCH v2 2/7] perf build: Add build support and capability " Ian Rogers
@ 2026-06-09  7:07 ` Ian Rogers
  2026-06-09  7:07 ` [PATCH v2 4/7] perf annotate: Add --disassembler command-line option Ian Rogers
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 16+ messages in thread
From: Ian Rogers @ 2026-06-09  7:07 UTC (permalink / raw)
  To: Serhei Makarov, mark, Peter Zijlstra, Ingo Molnar,
	Arnaldo Carvalho de Melo, Namhyung Kim, Jiri Olsa, Ian Rogers,
	Adrian Hunter, James Clark, Nick Terrell, David Sterba,
	Nathan Chancellor, Tomas Glozar, Blake Jones, Dmitrii Dolgov,
	Alexandre Chartre, Costa Shulyupin, Yuzhuo Jing, Michael Jeanson,
	Leo Yan, Tianyou Li, Zecheng Li, Rong Bao, linux-kernel,
	linux-perf-users, bpf

Implement the core disassembler backend in libasm.c utilizing elfutils'
libasm API. Hook the backend into perf annotate disassembler routing.
Configure libasm as the first-choice disassembler backend by default
(ahead of LLVM/capstone) when available.
Also, expose annotation_options__add_disassemblers_str to allow
custom disassembler selection.

Assisted-by: Antigravity:Google Gemini 3.5-flash
Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/util/Build      |   1 +
 tools/perf/util/annotate.c |   8 +-
 tools/perf/util/annotate.h |   3 +
 tools/perf/util/disasm.c   |   5 +
 tools/perf/util/libasm.c   | 184 +++++++++++++++++++++++++++++++++++++
 tools/perf/util/libasm.h   |  27 ++++++
 6 files changed, 226 insertions(+), 2 deletions(-)
 create mode 100644 tools/perf/util/libasm.c
 create mode 100644 tools/perf/util/libasm.h

diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index b22cdc24082a..f1d5ebb2edfa 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -12,6 +12,7 @@ perf-util-y += block-range.o
 perf-util-y += build-id.o
 perf-util-y += cacheline.o
 perf-util-$(CONFIG_LIBCAPSTONE) += capstone.o
+perf-util-$(CONFIG_LIBASM) += libasm.o
 perf-util-y += config.o
 perf-util-y += copyfile.o
 perf-util-y += ctype.o
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index 02505222d8c2..2259ac2c2cd6 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -2233,6 +2233,7 @@ const char * const perf_disassembler__strs[] = {
 	[PERF_DISASM_UNKNOWN]  = "unknown",
 	[PERF_DISASM_LLVM]     = "llvm",
 	[PERF_DISASM_CAPSTONE] = "capstone",
+	[PERF_DISASM_LIBASM]   = "libasm",
 	[PERF_DISASM_OBJDUMP]  = "objdump",
 };
 
@@ -2254,8 +2255,8 @@ static void annotation_options__add_disassembler(struct annotation_options *opti
 	pr_err("Failed to add disassembler %d\n", dis);
 }
 
-static int annotation_options__add_disassemblers_str(struct annotation_options *options,
-						const char *str)
+int annotation_options__add_disassemblers_str(struct annotation_options *options,
+					     const char *str)
 {
 	while (str && *str != '\0') {
 		const char *comma = strchr(str, ',');
@@ -2372,6 +2373,9 @@ static void annotation_options__default_init_disassemblers(struct annotation_opt
 		/* Already initialized. */
 		return;
 	}
+#ifdef HAVE_LIBASM_SUPPORT
+	annotation_options__add_disassembler(options, PERF_DISASM_LIBASM);
+#endif
 #ifdef HAVE_LIBLLVM_SUPPORT
 	annotation_options__add_disassembler(options, PERF_DISASM_LLVM);
 #endif
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h
index 1aa6df7d1618..21ac2b6472c1 100644
--- a/tools/perf/util/annotate.h
+++ b/tools/perf/util/annotate.h
@@ -38,6 +38,7 @@ enum perf_disassembler {
 	PERF_DISASM_UNKNOWN = 0,
 	PERF_DISASM_LLVM,
 	PERF_DISASM_CAPSTONE,
+	PERF_DISASM_LIBASM,
 	PERF_DISASM_OBJDUMP,
 };
 #define MAX_DISASSEMBLERS (PERF_DISASM_OBJDUMP + 1)
@@ -484,6 +485,8 @@ int hist_entry__tty_annotate2(struct hist_entry *he, struct evsel *evsel);
 
 void annotation_options__init(void);
 void annotation_options__exit(void);
+int annotation_options__add_disassemblers_str(struct annotation_options *options,
+					      const char *str);
 
 void annotation_config__init(void);
 
diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c
index 59ba88e1f744..42af3603fdff 100644
--- a/tools/perf/util/disasm.c
+++ b/tools/perf/util/disasm.c
@@ -22,6 +22,7 @@
 #include "capstone.h"
 #include "debug.h"
 #include "disasm.h"
+#include "libasm.h"
 #include "dso.h"
 #include "dwarf-regs.h"
 #include "env.h"
@@ -1622,6 +1623,10 @@ int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
 			args->options->disassembler_used = PERF_DISASM_CAPSTONE;
 			err = symbol__disassemble_capstone(symfs_filename, sym, args);
 			break;
+		case PERF_DISASM_LIBASM:
+			args->options->disassembler_used = PERF_DISASM_LIBASM;
+			err = symbol__disassemble_libasm(symfs_filename, sym, args);
+			break;
 		case PERF_DISASM_OBJDUMP:
 			args->options->disassembler_used = PERF_DISASM_OBJDUMP;
 			err = symbol__disassemble_objdump(symfs_filename, sym, args);
diff --git a/tools/perf/util/libasm.c b/tools/perf/util/libasm.c
new file mode 100644
index 000000000000..3f4274b1a01f
--- /dev/null
+++ b/tools/perf/util/libasm.c
@@ -0,0 +1,184 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "libasm.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <string.h>
+#include <unistd.h>
+#include <elfutils/libasm.h>
+#include <gelf.h>
+
+#include "annotate.h"
+#include "debug.h"
+#include "disasm.h"
+#include "dso.h"
+#include "map.h"
+#include "namespaces.h"
+#include "symbol.h"
+
+typedef struct ebl Ebl;
+extern Ebl *ebl_openbackend(Elf *elf);
+extern void ebl_closebackend(Ebl *ebl);
+
+struct disasm_output_arg {
+	char *buf;
+	size_t size;
+};
+
+static int disasm_output_cb(char *str, size_t len, void *arg)
+{
+	struct disasm_output_arg *oa = arg;
+	size_t to_copy = len < oa->size - 1 ? len : oa->size - 1;
+
+	memcpy(oa->buf, str, to_copy);
+	oa->buf += to_copy;
+	oa->size -= to_copy;
+	*oa->buf = '\0';
+	return 0;
+}
+
+int symbol__disassemble_libasm(const char *filename, struct symbol *sym,
+			       struct annotate_args *args)
+{
+	struct annotation *notes = symbol__annotation(sym);
+	struct map *map = args->ms->map;
+	struct dso *dso = map__dso(map);
+	u64 start = map__rip_2objdump(map, sym->start);
+	u64 offset;
+	bool is_64bit = false;
+	u8 *code_buf = NULL;
+	const u8 *buf;
+	u64 buf_len;
+	Elf *elf = NULL;
+	Ebl *ebl = NULL;
+	DisasmCtx_t *handle = NULL;
+	char disasm_buf[512];
+	struct disasm_line *dl;
+	struct nscookie nsc;
+	const uint8_t *pc;
+	const uint8_t *end;
+	u64 addr;
+	size_t insn_len;
+	int ret;
+	int fd = -1;
+	int count = 0;
+
+	if (args->options->objdump_path)
+		return -1;
+
+	buf = dso__read_symbol(dso, filename, map, sym,
+			       &code_buf, &buf_len, &is_64bit);
+	if (buf == NULL)
+		return errno;
+
+	/* add the function address and name */
+	scnprintf(disasm_buf, sizeof(disasm_buf), "%#"PRIx64" <%s>:",
+		  start, sym->name);
+
+	args->offset = -1;
+	args->line = disasm_buf;
+	args->line_nr = 0;
+	args->fileloc = NULL;
+	args->ms->sym = sym;
+
+	dl = disasm_line__new(args);
+	if (dl == NULL)
+		goto err;
+
+	annotation_line__add(&dl->al, &notes->src->source);
+
+	nsinfo__mountns_enter(dso__nsinfo(dso), &nsc);
+	fd = open(filename, O_RDONLY);
+	nsinfo__mountns_exit(&nsc);
+	if (fd < 0)
+		goto err;
+
+	elf = elf_begin(fd, ELF_C_READ, NULL);
+	if (!elf)
+		goto err;
+
+	ebl = ebl_openbackend(elf);
+	if (!ebl)
+		goto err;
+
+	handle = disasm_begin(ebl, elf, NULL);
+	if (!handle)
+		goto err;
+
+	pc = buf;
+	end = buf + buf_len;
+	addr = start;
+
+	offset = 0;
+	while (pc < end) {
+		struct disasm_output_arg oa = {
+			.buf = disasm_buf,
+			.size = sizeof(disasm_buf),
+		};
+		const uint8_t *prev_pc = pc;
+
+		ret = disasm_cb(handle, &pc, end, addr, "%7m %.1o,%.2o,%.3o,%.4o,%.5o",
+				    disasm_output_cb, &oa, NULL);
+		if (ret != 0 || pc == prev_pc) {
+			/* Disassembly failed or got stuck */
+			break;
+		}
+
+		args->offset = offset;
+		args->line = disasm_buf;
+
+		dl = disasm_line__new(args);
+		if (dl == NULL)
+			goto err;
+
+		annotation_line__add(&dl->al, &notes->src->source);
+
+		insn_len = pc - prev_pc;
+		offset += insn_len;
+		addr += insn_len;
+		count++;
+	}
+
+	if (offset != buf_len) {
+		struct list_head *list = &notes->src->source;
+
+		/* Discard all lines and fallback to objdump */
+		while (!list_empty(list)) {
+			dl = list_first_entry(list, struct disasm_line, al.node);
+
+			list_del_init(&dl->al.node);
+			disasm_line__free(dl);
+		}
+		count = -1;
+	}
+
+out:
+	if (handle)
+		disasm_end(handle);
+	if (ebl)
+		ebl_closebackend(ebl);
+	if (elf)
+		elf_end(elf);
+	if (fd >= 0)
+		close(fd);
+	free(code_buf);
+	return count < 0 ? count : 0;
+
+err:
+	{
+		struct disasm_line *tmp;
+
+		/*
+		 * It probably failed in the middle of the above loop.
+		 * Release any resources it might add.
+		 */
+		list_for_each_entry_safe(dl, tmp, &notes->src->source, al.node) {
+			list_del(&dl->al.node);
+			disasm_line__free(dl);
+		}
+	}
+
+	count = -1;
+	goto out;
+}
diff --git a/tools/perf/util/libasm.h b/tools/perf/util/libasm.h
new file mode 100644
index 000000000000..d4324edf059e
--- /dev/null
+++ b/tools/perf/util/libasm.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __PERF_LIBASM_H
+#define __PERF_LIBASM_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <linux/compiler.h>
+#include <linux/types.h>
+
+struct annotate_args;
+struct symbol;
+
+#ifdef HAVE_LIBASM_SUPPORT
+int symbol__disassemble_libasm(const char *filename, struct symbol *sym,
+				 struct annotate_args *args);
+#else /* !HAVE_LIBASM_SUPPORT */
+static inline int symbol__disassemble_libasm(const char *filename __maybe_unused,
+					       struct symbol *sym __maybe_unused,
+					       struct annotate_args *args __maybe_unused)
+{
+	return -1;
+}
+#endif /* HAVE_LIBASM_SUPPORT */
+
+#endif /* __PERF_LIBASM_H */
-- 
2.54.0.1064.gd145956f57-goog


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v2 4/7] perf annotate: Add --disassembler command-line option
  2026-06-09  7:07 [PATCH v2 0/7] perf annotate: Add elfutils libasm disassembler support Ian Rogers
                   ` (2 preceding siblings ...)
  2026-06-09  7:07 ` [PATCH v2 3/7] perf annotate: Implement elfutils libasm disassembler backend Ian Rogers
@ 2026-06-09  7:07 ` Ian Rogers
  2026-06-09  7:07 ` [PATCH v2 5/7] perf test: Enhance annotate test coverage and isolate config Ian Rogers
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 16+ messages in thread
From: Ian Rogers @ 2026-06-09  7:07 UTC (permalink / raw)
  To: Serhei Makarov, mark, Peter Zijlstra, Ingo Molnar,
	Arnaldo Carvalho de Melo, Namhyung Kim, Jiri Olsa, Ian Rogers,
	Adrian Hunter, James Clark, Nick Terrell, David Sterba,
	Nathan Chancellor, Tomas Glozar, Blake Jones, Dmitrii Dolgov,
	Alexandre Chartre, Costa Shulyupin, Yuzhuo Jing, Michael Jeanson,
	Leo Yan, Tianyou Li, Zecheng Li, Rong Bao, linux-kernel,
	linux-perf-users, bpf

Add the --disassembler command-line option to perf annotate. This
allows users to explicitly request or override the disassembler
preferences (e.g. --disassembler libasm) directly on the command
line.

Assisted-by: Antigravity:Google Gemini 3.5-flash
Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/builtin-annotate.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 8a0eb30eac24..57a88adfb130 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -699,6 +699,7 @@ int cmd_annotate(int argc, const char **argv)
 		.set = 0,
 	};
 	const char *disassembler_style = NULL, *objdump_path = NULL, *addr2line_path = NULL;
+	const char *disassemblers_str = NULL;
 	struct option options[] = {
 	OPT_STRING('i', "input", &input_name, "file",
 		    "input file name"),
@@ -782,6 +783,8 @@ int cmd_annotate(int argc, const char **argv)
 		    "Do not display empty (or dummy) events in the output"),
 	OPT_BOOLEAN(0, "code-with-type", &annotate_opts.code_with_type,
 		    "Show data type info in code annotation (memory instructions only)"),
+	OPT_STRING(0, "disassembler", &disassemblers_str, "names",
+		   "comma separated list of disassemblers to use"),
 	OPT_END()
 	};
 	int ret;
@@ -825,6 +828,13 @@ int cmd_annotate(int argc, const char **argv)
 			return -ENOMEM;
 	}
 
+	if (disassemblers_str) {
+		memset(annotate_opts.disassemblers, 0, sizeof(annotate_opts.disassemblers));
+		ret = annotation_options__add_disassemblers_str(&annotate_opts, disassemblers_str);
+		if (ret)
+			return -EINVAL;
+	}
+
 	if (annotate_check_args() < 0)
 		return -EINVAL;
 
-- 
2.54.0.1064.gd145956f57-goog


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v2 5/7] perf test: Enhance annotate test coverage and isolate config
  2026-06-09  7:07 [PATCH v2 0/7] perf annotate: Add elfutils libasm disassembler support Ian Rogers
                   ` (3 preceding siblings ...)
  2026-06-09  7:07 ` [PATCH v2 4/7] perf annotate: Add --disassembler command-line option Ian Rogers
@ 2026-06-09  7:07 ` Ian Rogers
  2026-06-09  7:07 ` [PATCH v2 6/7] perf annotate: Support BPF JIT disassembly via genelf Ian Rogers
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 16+ messages in thread
From: Ian Rogers @ 2026-06-09  7:07 UTC (permalink / raw)
  To: Serhei Makarov, mark, Peter Zijlstra, Ingo Molnar,
	Arnaldo Carvalho de Melo, Namhyung Kim, Jiri Olsa, Ian Rogers,
	Adrian Hunter, James Clark, Nick Terrell, David Sterba,
	Nathan Chancellor, Tomas Glozar, Blake Jones, Dmitrii Dolgov,
	Alexandre Chartre, Costa Shulyupin, Yuzhuo Jing, Michael Jeanson,
	Leo Yan, Tianyou Li, Zecheng Li, Rong Bao, linux-kernel,
	linux-perf-users, bpf

Update the annotate.sh shell test to test different disassembler
backends (objdump, llvm, capstone, libasm) utilizing the new
--disassembler command-line option.
Isolate the test script from host environment pollution by exporting
PERF_CONFIG=/dev/null at the start of the script, ensuring it runs
hermetically.

Assisted-by: Antigravity:Google Gemini 3.5-flash
Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/tests/shell/annotate.sh | 54 ++++++++++++++++++++++++++++++
 1 file changed, 54 insertions(+)

diff --git a/tools/perf/tests/shell/annotate.sh b/tools/perf/tests/shell/annotate.sh
index 689de58e9238..2c06d2670cb1 100755
--- a/tools/perf/tests/shell/annotate.sh
+++ b/tools/perf/tests/shell/annotate.sh
@@ -4,6 +4,8 @@
 
 set -e
 
+export PERF_CONFIG=/dev/null
+
 shelldir=$(dirname "$0")
 
 # shellcheck source=lib/perf_has_symbol.sh
@@ -106,8 +108,60 @@ test_basic() {
   echo "${mode} annotate test [Success]"
 }
 
+test_disassembler() {
+  disassembler=$1
+  feature=$2
+
+  if [ -n "${feature}" ]
+  then
+    if ! perf check feature "${feature}" > /dev/null 2>&1
+    then
+      echo "Skip test for ${disassembler} (feature ${feature} not supported)"
+      return 0
+    fi
+  fi
+
+  echo "Test annotate with disassembler: ${disassembler}"
+
+  perf annotate --no-demangle -i "${perfdata}" --stdio --percent-limit 10 --disassembler "${disassembler}" 2> /dev/null > "${perfout}" || ret=$?
+
+  if [ "x${ret}" != "x0" ]
+  then
+    echo "annotate with ${disassembler} [Failed: perf annotate error]"
+    err=1
+    return 0
+  fi
+
+  # check if it has the target symbol
+  if ! grep -q "${testsym}" "${perfout}"
+  then
+    echo "annotate with ${disassembler} [Failed: missing target symbol]"
+    err=1
+    return 0
+  fi
+
+  # check if it has the disassembly lines
+  if ! grep -q "${disasm_regex}" "${perfout}"
+  then
+    echo "annotate with ${disassembler} [Failed: missing disasm output]"
+    err=1
+    return 0
+  fi
+
+  echo "annotate with ${disassembler} [Success]"
+  return 0
+}
+
 test_basic Basic
 test_basic Pipe
 
+if [ "${err}" -eq 0 ]
+then
+  test_disassembler "objdump" ""
+  test_disassembler "llvm" "libLLVM"
+  test_disassembler "capstone" "libcapstone"
+  test_disassembler "libasm" "libasm"
+fi
+
 cleanup
 exit $err
-- 
2.54.0.1064.gd145956f57-goog


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v2 6/7] perf annotate: Support BPF JIT disassembly via genelf
  2026-06-09  7:07 [PATCH v2 0/7] perf annotate: Add elfutils libasm disassembler support Ian Rogers
                   ` (4 preceding siblings ...)
  2026-06-09  7:07 ` [PATCH v2 5/7] perf test: Enhance annotate test coverage and isolate config Ian Rogers
@ 2026-06-09  7:07 ` Ian Rogers
  2026-06-09  7:07 ` [PATCH v2 7/7] perf test: Add BPF JIT annotation test coverage for all disassemblers Ian Rogers
  2026-06-09 18:21 ` [PATCH v3 0/7] perf annotate: Add elfutils libasm disassembler and BPF JIT disassembly support Ian Rogers
  7 siblings, 0 replies; 16+ messages in thread
From: Ian Rogers @ 2026-06-09  7:07 UTC (permalink / raw)
  To: Serhei Makarov, mark, Peter Zijlstra, Ingo Molnar,
	Arnaldo Carvalho de Melo, Namhyung Kim, Jiri Olsa, Ian Rogers,
	Adrian Hunter, James Clark, Nick Terrell, David Sterba,
	Nathan Chancellor, Tomas Glozar, Blake Jones, Dmitrii Dolgov,
	Alexandre Chartre, Costa Shulyupin, Yuzhuo Jing, Michael Jeanson,
	Leo Yan, Tianyou Li, Zecheng Li, Rong Bao, linux-kernel,
	linux-perf-users, bpf

For in-memory BPF DSOs (DSO_BINARY_TYPE__BPF_PROG_INFO), write the
JITted instruction buffer to a temporary ELF file on disk using the
existing genelf framework (jit_write_elf). Reroute disassembly to
this temporary ELF file, allowing objdump and libasm to disassemble it
natively. Clean up the temporary file afterward.

Assisted-by: Antigravity:Google Gemini 3.5-flash
Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/tests/genelf.c |   2 +-
 tools/perf/util/disasm.c  | 111 +++++++++++++++++++++++++++++++++++---
 tools/perf/util/disasm.h  |   1 +
 tools/perf/util/genelf.c  |   4 +-
 tools/perf/util/genelf.h  |   2 +-
 tools/perf/util/jitdump.c |   2 +-
 6 files changed, 110 insertions(+), 12 deletions(-)

diff --git a/tools/perf/tests/genelf.c b/tools/perf/tests/genelf.c
index 95f3be1b683a..1723370db32e 100644
--- a/tools/perf/tests/genelf.c
+++ b/tools/perf/tests/genelf.c
@@ -38,7 +38,7 @@ static int test__jit_write_elf(struct test_suite *test __maybe_unused,
 
 	pr_info("Writing jit code to: %s\n", path);
 
-	ret = jit_write_elf(fd, 0, "main", x86_code, sizeof(x86_code),
+	ret = jit_write_elf(fd, GEN_ELF_ARCH, 0, "main", x86_code, sizeof(x86_code),
 			NULL, 0, NULL, 0, 0);
 	close(fd);
 
diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c
index 42af3603fdff..0648afd1b5f3 100644
--- a/tools/perf/util/disasm.c
+++ b/tools/perf/util/disasm.c
@@ -23,6 +23,9 @@
 #include "debug.h"
 #include "disasm.h"
 #include "libasm.h"
+#ifdef HAVE_LIBELF_SUPPORT
+#include "genelf.h"
+#endif
 #include "dso.h"
 #include "dwarf-regs.h"
 #include "env.h"
@@ -1420,7 +1423,7 @@ 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)
+	if (dso__binary_type(dso) == DSO_BINARY_TYPE__BPF_PROG_INFO && !args->is_temp_elf)
 		return symbol__disassemble_bpf_libbfd(sym, args);
 
 	if (dso__binary_type(dso) == DSO_BINARY_TYPE__BPF_IMAGE)
@@ -1540,6 +1543,45 @@ static int symbol__disassemble_objdump(const char *filename, struct symbol *sym,
 	return err;
 }
 
+#ifdef HAVE_LIBELF_SUPPORT
+#include <unistd.h>
+#include "dwarf-regs.h"
+static int symbol__create_bpf_temp_elf(const char *filename, struct symbol *sym,
+				       struct annotate_args *args,
+				       char *tmp_elf, size_t tmp_elf_sz)
+{
+	struct map *map = args->ms->map;
+	struct dso *dso = map__dso(map);
+	u8 *code_buf = NULL;
+	const u8 *buf;
+	u64 buf_len;
+	bool is_64bit;
+	int tmp_fd;
+	int err = -1;
+
+	buf = dso__read_symbol(dso, filename, map, sym, &code_buf, &buf_len, &is_64bit);
+	if (!buf)
+		return -1;
+
+	snprintf(tmp_elf, tmp_elf_sz, "/tmp/perf-bpf-XXXXXX");
+	tmp_fd = mkstemp(tmp_elf);
+	if (tmp_fd >= 0) {
+		uint16_t e_machine = args->arch ? args->arch->id.e_machine : EM_HOST;
+
+		if (jit_write_elf(tmp_fd, e_machine, map__rip_2objdump(map, sym->start),
+				  sym->name, buf, buf_len,
+				  NULL, 0, NULL, 0, 0) == 0) {
+			err = 0;
+		}
+		close(tmp_fd);
+		if (err)
+			unlink(tmp_elf);
+	}
+	free(code_buf);
+	return err;
+}
+#endif
+
 int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
 {
 	struct annotation_options *options = args->options;
@@ -1549,8 +1591,11 @@ int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
 	bool delete_extract = false;
 	struct kcore_extract kce;
 	bool decomp = false;
-	int err = dso__disassemble_filename(dso, symfs_filename, sizeof(symfs_filename));
+	int err;
+
+	args->is_temp_elf = false;
 
+	err = dso__disassemble_filename(dso, symfs_filename, sizeof(symfs_filename));
 	if (err)
 		return err;
 
@@ -1605,7 +1650,25 @@ int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
 
 	/* FIXME: LLVM and CAPSTONE should support source code */
 	if (options->annotate_src && !options->hide_src_code) {
-		err = symbol__disassemble_objdump(symfs_filename, sym, args);
+		const char *disasm_filename = symfs_filename;
+		bool is_temp = false;
+		char tmp_elf[PATH_MAX];
+
+#ifdef HAVE_LIBELF_SUPPORT
+		if (dso__binary_type(dso) == DSO_BINARY_TYPE__BPF_PROG_INFO) {
+			if (symbol__create_bpf_temp_elf(symfs_filename, sym, args,
+							tmp_elf, sizeof(tmp_elf)) == 0) {
+				disasm_filename = tmp_elf;
+				is_temp = true;
+				args->is_temp_elf = true;
+			}
+		}
+#endif
+		err = symbol__disassemble_objdump(disasm_filename, sym, args);
+		if (is_temp) {
+			unlink(tmp_elf);
+			args->is_temp_elf = false;
+		}
 		if (err == 0)
 			goto out_remove_tmp;
 	}
@@ -1613,29 +1676,63 @@ int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
 	err = -1;
 	for (u8 i = 0; i < ARRAY_SIZE(options->disassemblers) && err != 0; i++) {
 		enum perf_disassembler dis = options->disassemblers[i];
+		const char *disasm_filename = symfs_filename;
+		bool is_temp = false;
+		char tmp_elf[PATH_MAX];
+
+		switch (dis) {
+		case PERF_DISASM_LIBASM:
+		case PERF_DISASM_OBJDUMP:
+#ifdef HAVE_LIBELF_SUPPORT
+			if (dso__binary_type(dso) == DSO_BINARY_TYPE__BPF_PROG_INFO) {
+				if (symbol__create_bpf_temp_elf(symfs_filename, sym, args,
+								tmp_elf, sizeof(tmp_elf)) == 0) {
+					disasm_filename = tmp_elf;
+					is_temp = true;
+					args->is_temp_elf = true;
+				}
+			}
+#endif
+			break;
+		case PERF_DISASM_LLVM:
+		case PERF_DISASM_CAPSTONE:
+		case PERF_DISASM_UNKNOWN:
+		default:
+			break;
+		}
 
 		switch (dis) {
 		case PERF_DISASM_LLVM:
 			args->options->disassembler_used = PERF_DISASM_LLVM;
-			err = symbol__disassemble_llvm(symfs_filename, sym, args);
+			err = symbol__disassemble_llvm(disasm_filename, sym, args);
 			break;
 		case PERF_DISASM_CAPSTONE:
 			args->options->disassembler_used = PERF_DISASM_CAPSTONE;
-			err = symbol__disassemble_capstone(symfs_filename, sym, args);
+			err = symbol__disassemble_capstone(disasm_filename, sym, args);
 			break;
 		case PERF_DISASM_LIBASM:
 			args->options->disassembler_used = PERF_DISASM_LIBASM;
-			err = symbol__disassemble_libasm(symfs_filename, sym, args);
+			err = symbol__disassemble_libasm(disasm_filename, sym, args);
 			break;
 		case PERF_DISASM_OBJDUMP:
 			args->options->disassembler_used = PERF_DISASM_OBJDUMP;
-			err = symbol__disassemble_objdump(symfs_filename, sym, args);
+			err = symbol__disassemble_objdump(disasm_filename, sym, args);
 			break;
 		case PERF_DISASM_UNKNOWN: /* End of disassemblers. */
 		default:
 			args->options->disassembler_used = PERF_DISASM_UNKNOWN;
+			if (is_temp) {
+				unlink(tmp_elf);
+				args->is_temp_elf = false;
+			}
 			goto out_remove_tmp;
 		}
+
+		if (is_temp) {
+			unlink(tmp_elf);
+			args->is_temp_elf = false;
+		}
+
 		if (err == 0)
 			pr_debug("Disassembled with %s\n", perf_disassembler__strs[dis]);
 	}
diff --git a/tools/perf/util/disasm.h b/tools/perf/util/disasm.h
index 25756e3f47e4..32a5b3f5d1c6 100644
--- a/tools/perf/util/disasm.h
+++ b/tools/perf/util/disasm.h
@@ -106,6 +106,7 @@ struct annotate_args {
 	char			  *line;
 	int			  line_nr;
 	char			  *fileloc;
+	bool			  is_temp_elf;
 };
 
 const struct arch *arch__find(uint16_t e_machine, uint32_t e_flags, const char *cpuid);
diff --git a/tools/perf/util/genelf.c b/tools/perf/util/genelf.c
index 14882def9704..42731b6de797 100644
--- a/tools/perf/util/genelf.c
+++ b/tools/perf/util/genelf.c
@@ -179,7 +179,7 @@ static void blake2s_update_tagged(struct blake2s_ctx *ctx, int tag,
  * csize: the code size in bytes
  */
 int
-jit_write_elf(int fd, uint64_t load_addr __maybe_unused, const char *sym,
+jit_write_elf(int fd, uint16_t e_machine, uint64_t load_addr __maybe_unused, const char *sym,
 	      const void *code, int csize,
 	      void *debug __maybe_unused, int nr_debug_entries __maybe_unused,
 	      void *unwinding, uint64_t unwinding_header_size, uint64_t unwinding_size)
@@ -218,7 +218,7 @@ jit_write_elf(int fd, uint64_t load_addr __maybe_unused, const char *sym,
 
 	ehdr->e_ident[EI_DATA] = GEN_ELF_ENDIAN;
 	ehdr->e_ident[EI_CLASS] = GEN_ELF_CLASS;
-	ehdr->e_machine = GEN_ELF_ARCH;
+	ehdr->e_machine = e_machine;
 	ehdr->e_type = ET_DYN;
 	ehdr->e_entry = GEN_ELF_TEXT_OFFSET;
 	ehdr->e_version = EV_CURRENT;
diff --git a/tools/perf/util/genelf.h b/tools/perf/util/genelf.h
index 9f0b875d6548..992ca1eb2734 100644
--- a/tools/perf/util/genelf.h
+++ b/tools/perf/util/genelf.h
@@ -5,7 +5,7 @@
 #include <linux/math.h>
 
 /* genelf.c */
-int jit_write_elf(int fd, uint64_t code_addr, const char *sym,
+int jit_write_elf(int fd, uint16_t e_machine, uint64_t code_addr, const char *sym,
 		  const void *code, int csize, void *debug, int nr_debug_entries,
 		  void *unwinding, uint64_t unwinding_header_size, uint64_t unwinding_size);
 #ifdef HAVE_LIBDW_SUPPORT
diff --git a/tools/perf/util/jitdump.c b/tools/perf/util/jitdump.c
index 18fd84a82153..febf3e5a7851 100644
--- a/tools/perf/util/jitdump.c
+++ b/tools/perf/util/jitdump.c
@@ -95,7 +95,7 @@ jit_emit_elf(struct jit_buf_desc *jd,
 		return -1;
 	}
 
-	ret = jit_write_elf(fd, code_addr, sym, (const void *)code, csize, debug, nr_debug_entries,
+	ret = jit_write_elf(fd, GEN_ELF_ARCH, code_addr, sym, (const void *)code, csize, debug, nr_debug_entries,
 			    unwinding, unwinding_header_size, unwinding_size);
 
         close(fd);
-- 
2.54.0.1064.gd145956f57-goog


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v2 7/7] perf test: Add BPF JIT annotation test coverage for all disassemblers
  2026-06-09  7:07 [PATCH v2 0/7] perf annotate: Add elfutils libasm disassembler support Ian Rogers
                   ` (5 preceding siblings ...)
  2026-06-09  7:07 ` [PATCH v2 6/7] perf annotate: Support BPF JIT disassembly via genelf Ian Rogers
@ 2026-06-09  7:07 ` Ian Rogers
  2026-06-09 18:21 ` [PATCH v3 0/7] perf annotate: Add elfutils libasm disassembler and BPF JIT disassembly support Ian Rogers
  7 siblings, 0 replies; 16+ messages in thread
From: Ian Rogers @ 2026-06-09  7:07 UTC (permalink / raw)
  To: Serhei Makarov, mark, Peter Zijlstra, Ingo Molnar,
	Arnaldo Carvalho de Melo, Namhyung Kim, Jiri Olsa, Ian Rogers,
	Adrian Hunter, James Clark, Nick Terrell, David Sterba,
	Nathan Chancellor, Tomas Glozar, Blake Jones, Dmitrii Dolgov,
	Alexandre Chartre, Costa Shulyupin, Yuzhuo Jing, Michael Jeanson,
	Leo Yan, Tianyou Li, Zecheng Li, Rong Bao, linux-kernel,
	linux-perf-users, bpf

Expand the annotate.sh shell test to verify BPF JIT disassembly.
If the test is run with sufficient privileges (root/CAP_BPF) and
captures system BPF programs, it will extract a JITted BPF program
symbol from the perf report and run perf annotate on it.
This validates the temporary ELF generation and disassembles it using
each of the supported disassembler backends (objdump, llvm, capstone,
and libasm).

Assisted-by: Antigravity:Google Gemini 3.5-flash
Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/tests/shell/annotate.sh | 79 ++++++++++++++++++++++++++++++
 1 file changed, 79 insertions(+)

diff --git a/tools/perf/tests/shell/annotate.sh b/tools/perf/tests/shell/annotate.sh
index 2c06d2670cb1..9719e592befd 100755
--- a/tools/perf/tests/shell/annotate.sh
+++ b/tools/perf/tests/shell/annotate.sh
@@ -163,5 +163,84 @@ then
   test_disassembler "libasm" "libasm"
 fi
 
+test_bpf_disassembler() {
+  disassembler=$1
+  feature=$2
+
+  if [ -n "${feature}" ]
+  then
+    if ! perf check feature "${feature}" > /dev/null 2>&1
+    then
+      echo "Skip BPF JIT test for ${disassembler} (feature ${feature} not supported)"
+      return 0
+    fi
+  fi
+
+  echo "Test BPF JIT annotate with disassembler: ${disassembler}"
+
+  if ! perf annotate --no-demangle -i "${perfdata}" --stdio "${bpf_sym}" \
+      --disassembler "${disassembler}" 2> /dev/null > "${perfout}"
+  then
+    echo "BPF JIT annotate with ${disassembler} [Failed: perf annotate error]"
+    err=1
+    return 0
+  fi
+
+  if ! grep -q "${disasm_regex}" "${perfout}"
+  then
+    echo "BPF JIT annotate with ${disassembler} [Failed: missing disasm output]"
+    err=1
+    return 0
+  fi
+
+  echo "BPF JIT annotate with ${disassembler} [Success]"
+  return 0
+}
+
+test_bpf() {
+  echo "Test annotate with BPF JIT output"
+
+  if ! perf check -q feature libbpf-strings ; then
+    echo "BPF annotation test [Skipped - libbpf-strings not supported]"
+    return 0
+  fi
+
+  bpftmp=$(mktemp -d /tmp/__perf_test.bpf.XXXXXX)
+  bpf_perfdata="${bpftmp}/perf.data"
+
+  if ! perf record -a -e cycles -F 4000 -o "${bpf_perfdata}" -- sleep 1 2> /dev/null
+  then
+    echo "BPF annotation test [Skipped - perf record -a failed, probably no privileges]"
+    rm -rf "${bpftmp}"
+    return 0
+  fi
+
+  bpf_sym=$(perf report --stdio -i "${bpf_perfdata}" 2>/dev/null | \
+      grep -E -o 'bpf_prog_[0-9a-f]{16}_[0-9A-Za-z_]*' | head -1)
+
+  if [ -z "${bpf_sym}" ]; then
+    echo "BPF annotation test [Skipped - no JITted BPF symbols with samples found]"
+    rm -rf "${bpftmp}"
+    return 0
+  fi
+
+  # temporarily override perfdata so test_bpf_disassembler uses the right file
+  local old_perfdata="${perfdata}"
+  perfdata="${bpf_perfdata}"
+
+  test_bpf_disassembler "objdump" ""
+  test_bpf_disassembler "llvm" "libLLVM"
+  test_bpf_disassembler "capstone" "libcapstone"
+  test_bpf_disassembler "libasm" "libasm"
+
+  perfdata="${old_perfdata}"
+  rm -rf "${bpftmp}"
+}
+
+if [ "${err}" -eq 0 ]
+then
+  test_bpf
+fi
+
 cleanup
 exit $err
-- 
2.54.0.1064.gd145956f57-goog


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v3 0/7] perf annotate: Add elfutils libasm disassembler and BPF JIT disassembly support
  2026-06-09  7:07 [PATCH v2 0/7] perf annotate: Add elfutils libasm disassembler support Ian Rogers
                   ` (6 preceding siblings ...)
  2026-06-09  7:07 ` [PATCH v2 7/7] perf test: Add BPF JIT annotation test coverage for all disassemblers Ian Rogers
@ 2026-06-09 18:21 ` Ian Rogers
  2026-06-09 18:21   ` [PATCH v3 1/7] tools build: Add feature check for elfutils libasm Ian Rogers
                     ` (6 more replies)
  7 siblings, 7 replies; 16+ messages in thread
From: Ian Rogers @ 2026-06-09 18:21 UTC (permalink / raw)
  To: irogers, acme, namhyung
  Cc: 9erthalion6, adrian.hunter, alexandre.chartre, blakejones, bpf,
	costa.shul, dsterba, james.clark, jolsa, leo.yan, linux-kernel,
	linux-perf-users, mark, mingo, mjeanson, nathan, peterz,
	rong.bao, serhei, terrelln, tglozar, tianyou.li, yuzhuo, zecheng

This patch series introduces support for using the elfutils `libasm` library
as a disassembler backend in `perf annotate`. This provides a lightweight,
library-based alternative to LLVM and Capstone for in-process disassembly.

Additionally, this series implements disassembly support for in-memory BPF
JITted programs by writing the JITted instruction buffer to a temporary ELF
file on the fly and disassembling it using the ELF-based backends (`objdump`
and `libasm`).

Changes in v3:
- Address review comments on v2 series.
- Explicitly link against `-lasm -ldw -lelf` in the feature check.
- Document that older versions of elfutils that require explicitly linking
  `-lebl` statically are unsupported.
- Fix instruction-by-instruction disassembly loop logic in `libasm.c` by
  returning 1 from output callback and checking for `ret != 1`.
- Pass the correct ELF machine (`e_machine`) to `jit_write_elf` instead of
  hardcoding host architecture.
- Update `annotate.sh` tests to use `perf test -w` workload instead of `free`
  to avoid CI environment issues.
- Expand test coverage to test all disassemblers (including libasm) on BPF
  JIT symbols when run with root privileges.


Ian Rogers (7):
  tools build: Add feature check for elfutils libasm
  perf build: Add build support and capability for elfutils libasm
  perf annotate: Implement elfutils libasm disassembler backend
  perf annotate: Add --disassembler command-line option
  perf test: Enhance annotate test coverage and isolate config
  perf annotate: Support BPF JIT disassembly via genelf
  perf test: Add BPF JIT annotation test coverage for all disassemblers

 tools/build/Makefile.feature       |   2 +
 tools/build/feature/Makefile       |   9 ++
 tools/build/feature/test-libasm.c  |  20 ++++
 tools/perf/Makefile.config         |  22 ++++
 tools/perf/builtin-annotate.c      |  10 ++
 tools/perf/builtin-check.c         |   1 +
 tools/perf/tests/genelf.c          |   2 +-
 tools/perf/tests/shell/annotate.sh | 153 ++++++++++++++++++++++++
 tools/perf/util/Build              |   1 +
 tools/perf/util/annotate.c         |   8 +-
 tools/perf/util/annotate.h         |   3 +
 tools/perf/util/disasm.c           | 114 +++++++++++++++++-
 tools/perf/util/disasm.h           |   1 +
 tools/perf/util/genelf.c           |  16 +--
 tools/perf/util/genelf.h           |   2 +-
 tools/perf/util/jitdump.c          |   3 +-
 tools/perf/util/libasm.c           | 184 +++++++++++++++++++++++++++++
 tools/perf/util/libasm.h           |  27 +++++
 18 files changed, 559 insertions(+), 19 deletions(-)
 create mode 100644 tools/build/feature/test-libasm.c
 create mode 100644 tools/perf/util/libasm.c
 create mode 100644 tools/perf/util/libasm.h

-- 
2.54.0.1099.g489fc7bff1-goog


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v3 1/7] tools build: Add feature check for elfutils libasm
  2026-06-09 18:21 ` [PATCH v3 0/7] perf annotate: Add elfutils libasm disassembler and BPF JIT disassembly support Ian Rogers
@ 2026-06-09 18:21   ` Ian Rogers
  2026-06-09 18:21   ` [PATCH v3 2/7] perf build: Add build support and capability " Ian Rogers
                     ` (5 subsequent siblings)
  6 siblings, 0 replies; 16+ messages in thread
From: Ian Rogers @ 2026-06-09 18:21 UTC (permalink / raw)
  To: irogers, acme, namhyung
  Cc: 9erthalion6, adrian.hunter, alexandre.chartre, blakejones, bpf,
	costa.shul, dsterba, james.clark, jolsa, leo.yan, linux-kernel,
	linux-perf-users, mark, mingo, mjeanson, nathan, peterz,
	rong.bao, serhei, terrelln, tglozar, tianyou.li, yuzhuo, zecheng

This commit introduces a build-time feature test for the elfutils libasm
library in the shared tools/build system. It adds the test program and
updates the Makefiles to attempt building it to detect support.

Note on static linking: pkg-config for libdw/libelf on modern systems
does not list -lebl as a dependency. Older versions of elfutils that
require explicitly linking -lebl statically are unsupported by this
feature check.

Assisted-by: Antigravity:Google Gemini 3.5-flash
Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/build/Makefile.feature      |  2 ++
 tools/build/feature/Makefile      |  9 +++++++++
 tools/build/feature/test-libasm.c | 20 ++++++++++++++++++++
 3 files changed, 31 insertions(+)
 create mode 100644 tools/build/feature/test-libasm.c

diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature
index ed1374af31c1..422329dbc20a 100644
--- a/tools/build/Makefile.feature
+++ b/tools/build/Makefile.feature
@@ -118,6 +118,7 @@ FEATURE_TESTS_EXTRA :=                  \
          hello                          \
          babeltrace2-ctf-writer         \
          libcapstone                    \
+         libasm                         \
          libcheck                       \
          libbfd-liberty                 \
          libbfd-liberty-z               \
@@ -150,6 +151,7 @@ FEATURE_DISPLAY ?=              \
          numa_num_possible_cpus \
          libpython              \
          libcapstone            \
+         libasm                 \
          llvm-perf              \
          zlib                   \
          lzma                   \
diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile
index 62909a9c799d..f0f39a2ab203 100644
--- a/tools/build/feature/Makefile
+++ b/tools/build/feature/Makefile
@@ -47,6 +47,7 @@ FILES=                                          \
          test-timerfd.bin                       \
          test-babeltrace2-ctf-writer.bin        \
          test-libcapstone.bin			\
+         test-libasm.bin			\
          test-libcheck.bin			\
          test-compile-32.bin                    \
          test-compile-x32.bin                   \
@@ -184,6 +185,11 @@ ifeq ($(findstring -static,${LDFLAGS}),-static)
   DWARFLIBS += -ldl
 endif
 
+ASMLIBS := -lasm -ldw -lelf
+ifeq ($(findstring -static,${LDFLAGS}),-static)
+  ASMLIBS += -lz -llzma -lbz2 -lzstd
+endif
+
 $(OUTPUT)test-libdw.bin:
 	$(BUILD) $(DWLIBS)
 
@@ -311,6 +317,9 @@ $(OUTPUT)test-babeltrace2-ctf-writer.bin:
 $(OUTPUT)test-libcapstone.bin:
 	$(BUILD) # -lcapstone provided by $(FEATURE_CHECK_LDFLAGS-libcapstone)
 
+$(OUTPUT)test-libasm.bin:
+	$(BUILD) $(ASMLIBS)
+
 $(OUTPUT)test-libcheck.bin:
 	$(BUILD) # -lcheck is provided by $(FEATURE_CHECK_LDFLAGS-libcheck)
 
diff --git a/tools/build/feature/test-libasm.c b/tools/build/feature/test-libasm.c
new file mode 100644
index 000000000000..928fc46ecc75
--- /dev/null
+++ b/tools/build/feature/test-libasm.c
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <stddef.h>
+#include <elfutils/libasm.h>
+#include <gelf.h>
+
+struct ebl;
+extern struct ebl *ebl_openbackend(Elf *elf);
+extern void ebl_closebackend(struct ebl *ebl);
+
+int main(void)
+{
+	Elf *elf = elf_begin(0, ELF_C_READ, NULL);
+	struct ebl *ebl = ebl_openbackend(elf);
+	DisasmCtx_t *ctx = disasm_begin(ebl, elf, NULL);
+
+	disasm_end(ctx);
+	ebl_closebackend(ebl);
+	elf_end(elf);
+	return 0;
+}
-- 
2.54.0.1099.g489fc7bff1-goog


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v3 2/7] perf build: Add build support and capability for elfutils libasm
  2026-06-09 18:21 ` [PATCH v3 0/7] perf annotate: Add elfutils libasm disassembler and BPF JIT disassembly support Ian Rogers
  2026-06-09 18:21   ` [PATCH v3 1/7] tools build: Add feature check for elfutils libasm Ian Rogers
@ 2026-06-09 18:21   ` Ian Rogers
  2026-06-09 18:21   ` [PATCH v3 3/7] perf annotate: Implement elfutils libasm disassembler backend Ian Rogers
                     ` (4 subsequent siblings)
  6 siblings, 0 replies; 16+ messages in thread
From: Ian Rogers @ 2026-06-09 18:21 UTC (permalink / raw)
  To: irogers, acme, namhyung
  Cc: 9erthalion6, adrian.hunter, alexandre.chartre, blakejones, bpf,
	costa.shul, dsterba, james.clark, jolsa, leo.yan, linux-kernel,
	linux-perf-users, mark, mingo, mjeanson, nathan, peterz,
	rong.bao, serhei, terrelln, tglozar, tianyou.li, yuzhuo, zecheng

Configure perf build system to utilize the libasm feature check
(supporting both static and dynamic builds) and export the config flags.
Add "libasm" capability detection to perf check.

Assisted-by: Antigravity:Google Gemini 3.5-flash
Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/Makefile.config | 22 ++++++++++++++++++++++
 tools/perf/builtin-check.c |  1 +
 2 files changed, 23 insertions(+)

diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config
index 6e7b15fab2ec..632cb069c944 100644
--- a/tools/perf/Makefile.config
+++ b/tools/perf/Makefile.config
@@ -199,6 +199,13 @@ endif
 FEATURE_CHECK_CFLAGS-libcapstone := $(LIBCAPSTONE_CFLAGS)
 FEATURE_CHECK_LDFLAGS-libcapstone := $(LIBCAPSTONE_LDFLAGS) -lcapstone
 
+ifdef LIBASM_DIR
+  LIBASM_CFLAGS  := -I$(LIBASM_DIR)/include
+  LIBASM_LDFLAGS := -L$(LIBASM_DIR)/lib
+endif
+FEATURE_CHECK_CFLAGS-libasm := $(LIBASM_CFLAGS)
+FEATURE_CHECK_LDFLAGS-libasm := $(LIBASM_LDFLAGS) -lasm -ldw -lelf
+
 ifdef LIBZSTD_DIR
   LIBZSTD_CFLAGS  := -I$(LIBZSTD_DIR)/lib
   LIBZSTD_LDFLAGS := -L$(LIBZSTD_DIR)/lib
@@ -432,6 +439,7 @@ ifeq ($(feature-file-handle), 1)
 endif
 
 ifdef NO_LIBELF
+  NO_LIBASM := 1
   NO_LIBDW := 1
   NO_LIBUNWIND := 1
   NO_LIBBPF := 1
@@ -1060,6 +1068,20 @@ ifndef NO_CAPSTONE
   endif
 endif
 
+ifdef NO_LIBDW
+  NO_LIBASM := 1
+endif
+
+ifndef NO_LIBASM
+  $(call feature_check,libasm)
+  ifeq ($(feature-libasm), 1)
+    CFLAGS += -DHAVE_LIBASM_SUPPORT $(LIBASM_CFLAGS)
+    LDFLAGS += $(LIBASM_LDFLAGS)
+    EXTLIBS += -lasm -ldw -lelf
+    $(call detected,CONFIG_LIBASM)
+  endif
+endif
+
 ifdef EXTRA_TESTS
     $(call detected,CONFIG_EXTRA_TESTS)
     CFLAGS += -DHAVE_EXTRA_TESTS
diff --git a/tools/perf/builtin-check.c b/tools/perf/builtin-check.c
index 60437650c50f..d2ffea4d9b8b 100644
--- a/tools/perf/builtin-check.c
+++ b/tools/perf/builtin-check.c
@@ -46,6 +46,7 @@ struct feature_status supported_features[] = {
 	FEATURE_STATUS("babeltrace2-ctf-writer", HAVE_BABELTRACE2_CTF_WRITER_SUPPORT),
 	FEATURE_STATUS("libbpf-strings", HAVE_LIBBPF_STRINGS_SUPPORT),
 	FEATURE_STATUS("libcapstone", HAVE_LIBCAPSTONE_SUPPORT),
+	FEATURE_STATUS("libasm", HAVE_LIBASM_SUPPORT),
 	FEATURE_STATUS("libdw-dwarf-unwind", HAVE_LIBDW_SUPPORT),
 	FEATURE_STATUS("libelf", HAVE_LIBELF_SUPPORT),
 	FEATURE_STATUS("libLLVM", HAVE_LIBLLVM_SUPPORT),
-- 
2.54.0.1099.g489fc7bff1-goog


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v3 3/7] perf annotate: Implement elfutils libasm disassembler backend
  2026-06-09 18:21 ` [PATCH v3 0/7] perf annotate: Add elfutils libasm disassembler and BPF JIT disassembly support Ian Rogers
  2026-06-09 18:21   ` [PATCH v3 1/7] tools build: Add feature check for elfutils libasm Ian Rogers
  2026-06-09 18:21   ` [PATCH v3 2/7] perf build: Add build support and capability " Ian Rogers
@ 2026-06-09 18:21   ` Ian Rogers
  2026-06-09 18:21   ` [PATCH v3 4/7] perf annotate: Add --disassembler command-line option Ian Rogers
                     ` (3 subsequent siblings)
  6 siblings, 0 replies; 16+ messages in thread
From: Ian Rogers @ 2026-06-09 18:21 UTC (permalink / raw)
  To: irogers, acme, namhyung
  Cc: 9erthalion6, adrian.hunter, alexandre.chartre, blakejones, bpf,
	costa.shul, dsterba, james.clark, jolsa, leo.yan, linux-kernel,
	linux-perf-users, mark, mingo, mjeanson, nathan, peterz,
	rong.bao, serhei, terrelln, tglozar, tianyou.li, yuzhuo, zecheng

Implement the core disassembler backend in libasm.c utilizing elfutils'
libasm API. Hook the backend into perf annotate disassembler routing.
Configure libasm as the first-choice disassembler backend by default
(ahead of LLVM/capstone) when available.
Also, expose annotation_options__add_disassemblers_str to allow
custom disassembler selection.

Assisted-by: Antigravity:Google Gemini 3.5-flash
Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/util/Build      |   1 +
 tools/perf/util/annotate.c |   8 +-
 tools/perf/util/annotate.h |   3 +
 tools/perf/util/disasm.c   |   5 +
 tools/perf/util/libasm.c   | 184 +++++++++++++++++++++++++++++++++++++
 tools/perf/util/libasm.h   |  27 ++++++
 6 files changed, 226 insertions(+), 2 deletions(-)
 create mode 100644 tools/perf/util/libasm.c
 create mode 100644 tools/perf/util/libasm.h

diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index b22cdc24082a..f1d5ebb2edfa 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -12,6 +12,7 @@ perf-util-y += block-range.o
 perf-util-y += build-id.o
 perf-util-y += cacheline.o
 perf-util-$(CONFIG_LIBCAPSTONE) += capstone.o
+perf-util-$(CONFIG_LIBASM) += libasm.o
 perf-util-y += config.o
 perf-util-y += copyfile.o
 perf-util-y += ctype.o
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index 02505222d8c2..2259ac2c2cd6 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -2233,6 +2233,7 @@ const char * const perf_disassembler__strs[] = {
 	[PERF_DISASM_UNKNOWN]  = "unknown",
 	[PERF_DISASM_LLVM]     = "llvm",
 	[PERF_DISASM_CAPSTONE] = "capstone",
+	[PERF_DISASM_LIBASM]   = "libasm",
 	[PERF_DISASM_OBJDUMP]  = "objdump",
 };
 
@@ -2254,8 +2255,8 @@ static void annotation_options__add_disassembler(struct annotation_options *opti
 	pr_err("Failed to add disassembler %d\n", dis);
 }
 
-static int annotation_options__add_disassemblers_str(struct annotation_options *options,
-						const char *str)
+int annotation_options__add_disassemblers_str(struct annotation_options *options,
+					     const char *str)
 {
 	while (str && *str != '\0') {
 		const char *comma = strchr(str, ',');
@@ -2372,6 +2373,9 @@ static void annotation_options__default_init_disassemblers(struct annotation_opt
 		/* Already initialized. */
 		return;
 	}
+#ifdef HAVE_LIBASM_SUPPORT
+	annotation_options__add_disassembler(options, PERF_DISASM_LIBASM);
+#endif
 #ifdef HAVE_LIBLLVM_SUPPORT
 	annotation_options__add_disassembler(options, PERF_DISASM_LLVM);
 #endif
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h
index 1aa6df7d1618..21ac2b6472c1 100644
--- a/tools/perf/util/annotate.h
+++ b/tools/perf/util/annotate.h
@@ -38,6 +38,7 @@ enum perf_disassembler {
 	PERF_DISASM_UNKNOWN = 0,
 	PERF_DISASM_LLVM,
 	PERF_DISASM_CAPSTONE,
+	PERF_DISASM_LIBASM,
 	PERF_DISASM_OBJDUMP,
 };
 #define MAX_DISASSEMBLERS (PERF_DISASM_OBJDUMP + 1)
@@ -484,6 +485,8 @@ int hist_entry__tty_annotate2(struct hist_entry *he, struct evsel *evsel);
 
 void annotation_options__init(void);
 void annotation_options__exit(void);
+int annotation_options__add_disassemblers_str(struct annotation_options *options,
+					      const char *str);
 
 void annotation_config__init(void);
 
diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c
index 59ba88e1f744..42af3603fdff 100644
--- a/tools/perf/util/disasm.c
+++ b/tools/perf/util/disasm.c
@@ -22,6 +22,7 @@
 #include "capstone.h"
 #include "debug.h"
 #include "disasm.h"
+#include "libasm.h"
 #include "dso.h"
 #include "dwarf-regs.h"
 #include "env.h"
@@ -1622,6 +1623,10 @@ int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
 			args->options->disassembler_used = PERF_DISASM_CAPSTONE;
 			err = symbol__disassemble_capstone(symfs_filename, sym, args);
 			break;
+		case PERF_DISASM_LIBASM:
+			args->options->disassembler_used = PERF_DISASM_LIBASM;
+			err = symbol__disassemble_libasm(symfs_filename, sym, args);
+			break;
 		case PERF_DISASM_OBJDUMP:
 			args->options->disassembler_used = PERF_DISASM_OBJDUMP;
 			err = symbol__disassemble_objdump(symfs_filename, sym, args);
diff --git a/tools/perf/util/libasm.c b/tools/perf/util/libasm.c
new file mode 100644
index 000000000000..2f0a65733522
--- /dev/null
+++ b/tools/perf/util/libasm.c
@@ -0,0 +1,184 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "libasm.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <string.h>
+#include <unistd.h>
+#include <elfutils/libasm.h>
+#include <gelf.h>
+
+#include "annotate.h"
+#include "debug.h"
+#include "disasm.h"
+#include "dso.h"
+#include "map.h"
+#include "namespaces.h"
+#include "symbol.h"
+
+struct ebl;
+extern struct ebl *ebl_openbackend(Elf *elf);
+extern void ebl_closebackend(struct ebl *ebl);
+
+struct disasm_output_arg {
+	char *buf;
+	size_t size;
+};
+
+static int disasm_output_cb(char *str, size_t len, void *arg)
+{
+	struct disasm_output_arg *oa = arg;
+	size_t to_copy = len < oa->size - 1 ? len : oa->size - 1;
+
+	memcpy(oa->buf, str, to_copy);
+	oa->buf += to_copy;
+	oa->size -= to_copy;
+	*oa->buf = '\0';
+	return 1;
+}
+
+int symbol__disassemble_libasm(const char *filename, struct symbol *sym,
+			       struct annotate_args *args)
+{
+	struct annotation *notes = symbol__annotation(sym);
+	struct map *map = args->ms->map;
+	struct dso *dso = map__dso(map);
+	u64 start = map__rip_2objdump(map, sym->start);
+	u64 offset;
+	bool is_64bit = false;
+	u8 *code_buf = NULL;
+	const u8 *buf;
+	u64 buf_len;
+	Elf *elf = NULL;
+	struct ebl *ebl = NULL;
+	DisasmCtx_t *handle = NULL;
+	char disasm_buf[512];
+	struct disasm_line *dl;
+	struct nscookie nsc;
+	const uint8_t *pc;
+	const uint8_t *end;
+	u64 addr;
+	size_t insn_len;
+	int ret;
+	int fd = -1;
+	int count = 0;
+
+	if (args->options->objdump_path)
+		return -1;
+
+	buf = dso__read_symbol(dso, filename, map, sym,
+			       &code_buf, &buf_len, &is_64bit);
+	if (buf == NULL)
+		return errno;
+
+	/* add the function address and name */
+	scnprintf(disasm_buf, sizeof(disasm_buf), "%#"PRIx64" <%s>:",
+		  start, sym->name);
+
+	args->offset = -1;
+	args->line = disasm_buf;
+	args->line_nr = 0;
+	args->fileloc = NULL;
+	args->ms->sym = sym;
+
+	dl = disasm_line__new(args);
+	if (dl == NULL)
+		goto err;
+
+	annotation_line__add(&dl->al, &notes->src->source);
+
+	nsinfo__mountns_enter(dso__nsinfo(dso), &nsc);
+	fd = open(filename, O_RDONLY);
+	nsinfo__mountns_exit(&nsc);
+	if (fd < 0)
+		goto err;
+
+	elf = elf_begin(fd, ELF_C_READ, NULL);
+	if (!elf)
+		goto err;
+
+	ebl = ebl_openbackend(elf);
+	if (!ebl)
+		goto err;
+
+	handle = disasm_begin(ebl, elf, NULL);
+	if (!handle)
+		goto err;
+
+	pc = buf;
+	end = buf + buf_len;
+	addr = start;
+
+	offset = 0;
+	while (pc < end) {
+		struct disasm_output_arg oa = {
+			.buf = disasm_buf,
+			.size = sizeof(disasm_buf),
+		};
+		const uint8_t *prev_pc = pc;
+
+		ret = disasm_cb(handle, &pc, end, addr, "%7m %.1o,%.2o,%.3o,%.4o,%.5o",
+				    disasm_output_cb, &oa, NULL);
+		if (ret != 1 || pc == prev_pc) {
+			/* Disassembly failed or got stuck */
+			break;
+		}
+
+		args->offset = offset;
+		args->line = disasm_buf;
+
+		dl = disasm_line__new(args);
+		if (dl == NULL)
+			goto err;
+
+		annotation_line__add(&dl->al, &notes->src->source);
+
+		insn_len = pc - prev_pc;
+		offset += insn_len;
+		addr += insn_len;
+		count++;
+	}
+
+	if (offset != buf_len) {
+		struct list_head *list = &notes->src->source;
+
+		/* Discard all lines and fallback to objdump */
+		while (!list_empty(list)) {
+			dl = list_first_entry(list, struct disasm_line, al.node);
+
+			list_del_init(&dl->al.node);
+			disasm_line__free(dl);
+		}
+		count = -1;
+	}
+
+out:
+	if (handle)
+		disasm_end(handle);
+	if (ebl)
+		ebl_closebackend(ebl);
+	if (elf)
+		elf_end(elf);
+	if (fd >= 0)
+		close(fd);
+	free(code_buf);
+	return count < 0 ? count : 0;
+
+err:
+	{
+		struct disasm_line *tmp;
+
+		/*
+		 * It probably failed in the middle of the above loop.
+		 * Release any resources it might add.
+		 */
+		list_for_each_entry_safe(dl, tmp, &notes->src->source, al.node) {
+			list_del(&dl->al.node);
+			disasm_line__free(dl);
+		}
+	}
+
+	count = -1;
+	goto out;
+}
diff --git a/tools/perf/util/libasm.h b/tools/perf/util/libasm.h
new file mode 100644
index 000000000000..d4324edf059e
--- /dev/null
+++ b/tools/perf/util/libasm.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __PERF_LIBASM_H
+#define __PERF_LIBASM_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <linux/compiler.h>
+#include <linux/types.h>
+
+struct annotate_args;
+struct symbol;
+
+#ifdef HAVE_LIBASM_SUPPORT
+int symbol__disassemble_libasm(const char *filename, struct symbol *sym,
+				 struct annotate_args *args);
+#else /* !HAVE_LIBASM_SUPPORT */
+static inline int symbol__disassemble_libasm(const char *filename __maybe_unused,
+					       struct symbol *sym __maybe_unused,
+					       struct annotate_args *args __maybe_unused)
+{
+	return -1;
+}
+#endif /* HAVE_LIBASM_SUPPORT */
+
+#endif /* __PERF_LIBASM_H */
-- 
2.54.0.1099.g489fc7bff1-goog


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v3 4/7] perf annotate: Add --disassembler command-line option
  2026-06-09 18:21 ` [PATCH v3 0/7] perf annotate: Add elfutils libasm disassembler and BPF JIT disassembly support Ian Rogers
                     ` (2 preceding siblings ...)
  2026-06-09 18:21   ` [PATCH v3 3/7] perf annotate: Implement elfutils libasm disassembler backend Ian Rogers
@ 2026-06-09 18:21   ` Ian Rogers
  2026-06-09 18:21   ` [PATCH v3 5/7] perf test: Enhance annotate test coverage and isolate config Ian Rogers
                     ` (2 subsequent siblings)
  6 siblings, 0 replies; 16+ messages in thread
From: Ian Rogers @ 2026-06-09 18:21 UTC (permalink / raw)
  To: irogers, acme, namhyung
  Cc: 9erthalion6, adrian.hunter, alexandre.chartre, blakejones, bpf,
	costa.shul, dsterba, james.clark, jolsa, leo.yan, linux-kernel,
	linux-perf-users, mark, mingo, mjeanson, nathan, peterz,
	rong.bao, serhei, terrelln, tglozar, tianyou.li, yuzhuo, zecheng

Add the --disassembler command-line option to perf annotate. This
allows users to explicitly request or override the disassembler
preferences (e.g. --disassembler libasm) directly on the command
line.

Assisted-by: Antigravity:Google Gemini 3.5-flash
Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/builtin-annotate.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 8a0eb30eac24..57a88adfb130 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -699,6 +699,7 @@ int cmd_annotate(int argc, const char **argv)
 		.set = 0,
 	};
 	const char *disassembler_style = NULL, *objdump_path = NULL, *addr2line_path = NULL;
+	const char *disassemblers_str = NULL;
 	struct option options[] = {
 	OPT_STRING('i', "input", &input_name, "file",
 		    "input file name"),
@@ -782,6 +783,8 @@ int cmd_annotate(int argc, const char **argv)
 		    "Do not display empty (or dummy) events in the output"),
 	OPT_BOOLEAN(0, "code-with-type", &annotate_opts.code_with_type,
 		    "Show data type info in code annotation (memory instructions only)"),
+	OPT_STRING(0, "disassembler", &disassemblers_str, "names",
+		   "comma separated list of disassemblers to use"),
 	OPT_END()
 	};
 	int ret;
@@ -825,6 +828,13 @@ int cmd_annotate(int argc, const char **argv)
 			return -ENOMEM;
 	}
 
+	if (disassemblers_str) {
+		memset(annotate_opts.disassemblers, 0, sizeof(annotate_opts.disassemblers));
+		ret = annotation_options__add_disassemblers_str(&annotate_opts, disassemblers_str);
+		if (ret)
+			return -EINVAL;
+	}
+
 	if (annotate_check_args() < 0)
 		return -EINVAL;
 
-- 
2.54.0.1099.g489fc7bff1-goog


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v3 5/7] perf test: Enhance annotate test coverage and isolate config
  2026-06-09 18:21 ` [PATCH v3 0/7] perf annotate: Add elfutils libasm disassembler and BPF JIT disassembly support Ian Rogers
                     ` (3 preceding siblings ...)
  2026-06-09 18:21   ` [PATCH v3 4/7] perf annotate: Add --disassembler command-line option Ian Rogers
@ 2026-06-09 18:21   ` Ian Rogers
  2026-06-09 18:21   ` [PATCH v3 6/7] perf annotate: Support BPF JIT disassembly via genelf Ian Rogers
  2026-06-09 18:21   ` [PATCH v3 7/7] perf test: Add BPF JIT annotation test coverage for all disassemblers Ian Rogers
  6 siblings, 0 replies; 16+ messages in thread
From: Ian Rogers @ 2026-06-09 18:21 UTC (permalink / raw)
  To: irogers, acme, namhyung
  Cc: 9erthalion6, adrian.hunter, alexandre.chartre, blakejones, bpf,
	costa.shul, dsterba, james.clark, jolsa, leo.yan, linux-kernel,
	linux-perf-users, mark, mingo, mjeanson, nathan, peterz,
	rong.bao, serhei, terrelln, tglozar, tianyou.li, yuzhuo, zecheng

Update the annotate.sh shell test to test different disassembler
backends (objdump, llvm, capstone, libasm) utilizing the new
--disassembler command-line option.
Isolate the test script from host environment pollution by exporting
PERF_CONFIG=/dev/null at the start of the script, ensuring it runs
hermetically.

Assisted-by: Antigravity:Google Gemini 3.5-flash
Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/tests/shell/annotate.sh | 60 ++++++++++++++++++++++++++++++
 1 file changed, 60 insertions(+)

diff --git a/tools/perf/tests/shell/annotate.sh b/tools/perf/tests/shell/annotate.sh
index 689de58e9238..eae6a46a247b 100755
--- a/tools/perf/tests/shell/annotate.sh
+++ b/tools/perf/tests/shell/annotate.sh
@@ -4,6 +4,8 @@
 
 set -e
 
+export PERF_CONFIG=/dev/null
+
 shelldir=$(dirname "$0")
 
 # shellcheck source=lib/perf_has_symbol.sh
@@ -106,8 +108,66 @@ test_basic() {
   echo "${mode} annotate test [Success]"
 }
 
+test_disassembler() {
+  disassembler=$1
+  feature=$2
+  local ret=0
+
+  if [ -n "${feature}" ]
+  then
+    if ! perf check feature "${feature}" > /dev/null 2>&1
+    then
+      echo "Skip test for ${disassembler} (feature ${feature} not supported)"
+      return 0
+    fi
+  fi
+
+  echo "Test annotate with disassembler: ${disassembler}"
+
+  perf annotate --no-demangle -i "${perfdata}" --stdio \
+    --percent-limit 10 --disassembler "${disassembler}" \
+    2> /dev/null > "${perfout}" || ret=$?
+
+  if [ "${ret}" -ne 0 ]
+  then
+    echo "annotate with ${disassembler} [Failed: perf annotate error]"
+    err=1
+    return 0
+  fi
+
+  # check if it has the target symbol
+  if ! grep -q "${testsym}" "${perfout}"
+  then
+    echo "annotate with ${disassembler} [Failed: missing target symbol]"
+    err=1
+    return 0
+  fi
+
+  # check if it has the disassembly lines
+  if ! grep -q "${disasm_regex}" "${perfout}"
+  then
+    echo "annotate with ${disassembler} [Failed: missing disasm output]"
+    err=1
+    return 0
+  fi
+
+  echo "annotate with ${disassembler} [Success]"
+  return 0
+}
+
 test_basic Basic
 test_basic Pipe
 
+# Restore perfdata to a regular format for disassembler tests
+perf record -o "${perfdata}" ${testprog} > /dev/null 2>&1
+
+if [ "${err}" -eq 0 ]
+then
+  test_disassembler "objdump" ""
+  test_disassembler "llvm" "libLLVM"
+  test_disassembler "capstone" "libcapstone"
+  test_disassembler "libasm" "libasm"
+fi
+
 cleanup
 exit $err
-- 
2.54.0.1099.g489fc7bff1-goog


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v3 6/7] perf annotate: Support BPF JIT disassembly via genelf
  2026-06-09 18:21 ` [PATCH v3 0/7] perf annotate: Add elfutils libasm disassembler and BPF JIT disassembly support Ian Rogers
                     ` (4 preceding siblings ...)
  2026-06-09 18:21   ` [PATCH v3 5/7] perf test: Enhance annotate test coverage and isolate config Ian Rogers
@ 2026-06-09 18:21   ` Ian Rogers
  2026-06-09 18:21   ` [PATCH v3 7/7] perf test: Add BPF JIT annotation test coverage for all disassemblers Ian Rogers
  6 siblings, 0 replies; 16+ messages in thread
From: Ian Rogers @ 2026-06-09 18:21 UTC (permalink / raw)
  To: irogers, acme, namhyung
  Cc: 9erthalion6, adrian.hunter, alexandre.chartre, blakejones, bpf,
	costa.shul, dsterba, james.clark, jolsa, leo.yan, linux-kernel,
	linux-perf-users, mark, mingo, mjeanson, nathan, peterz,
	rong.bao, serhei, terrelln, tglozar, tianyou.li, yuzhuo, zecheng

For in-memory BPF DSOs (DSO_BINARY_TYPE__BPF_PROG_INFO), write the
JITted instruction buffer to a temporary ELF file on disk using the
existing genelf framework (jit_write_elf). Reroute disassembly to
this temporary ELF file, allowing objdump and libasm to disassemble it
natively. Clean up the temporary file afterward.

Assisted-by: Antigravity:Google Gemini 3.5-flash
Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/tests/genelf.c |   2 +-
 tools/perf/util/disasm.c  | 111 +++++++++++++++++++++++++++++++++++---
 tools/perf/util/disasm.h  |   1 +
 tools/perf/util/genelf.c  |  16 +++---
 tools/perf/util/genelf.h  |   2 +-
 tools/perf/util/jitdump.c |   3 +-
 6 files changed, 117 insertions(+), 18 deletions(-)

diff --git a/tools/perf/tests/genelf.c b/tools/perf/tests/genelf.c
index 95f3be1b683a..1723370db32e 100644
--- a/tools/perf/tests/genelf.c
+++ b/tools/perf/tests/genelf.c
@@ -38,7 +38,7 @@ static int test__jit_write_elf(struct test_suite *test __maybe_unused,
 
 	pr_info("Writing jit code to: %s\n", path);
 
-	ret = jit_write_elf(fd, 0, "main", x86_code, sizeof(x86_code),
+	ret = jit_write_elf(fd, GEN_ELF_ARCH, 0, "main", x86_code, sizeof(x86_code),
 			NULL, 0, NULL, 0, 0);
 	close(fd);
 
diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c
index 42af3603fdff..0648afd1b5f3 100644
--- a/tools/perf/util/disasm.c
+++ b/tools/perf/util/disasm.c
@@ -23,6 +23,9 @@
 #include "debug.h"
 #include "disasm.h"
 #include "libasm.h"
+#ifdef HAVE_LIBELF_SUPPORT
+#include "genelf.h"
+#endif
 #include "dso.h"
 #include "dwarf-regs.h"
 #include "env.h"
@@ -1420,7 +1423,7 @@ 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)
+	if (dso__binary_type(dso) == DSO_BINARY_TYPE__BPF_PROG_INFO && !args->is_temp_elf)
 		return symbol__disassemble_bpf_libbfd(sym, args);
 
 	if (dso__binary_type(dso) == DSO_BINARY_TYPE__BPF_IMAGE)
@@ -1540,6 +1543,45 @@ static int symbol__disassemble_objdump(const char *filename, struct symbol *sym,
 	return err;
 }
 
+#ifdef HAVE_LIBELF_SUPPORT
+#include <unistd.h>
+#include "dwarf-regs.h"
+static int symbol__create_bpf_temp_elf(const char *filename, struct symbol *sym,
+				       struct annotate_args *args,
+				       char *tmp_elf, size_t tmp_elf_sz)
+{
+	struct map *map = args->ms->map;
+	struct dso *dso = map__dso(map);
+	u8 *code_buf = NULL;
+	const u8 *buf;
+	u64 buf_len;
+	bool is_64bit;
+	int tmp_fd;
+	int err = -1;
+
+	buf = dso__read_symbol(dso, filename, map, sym, &code_buf, &buf_len, &is_64bit);
+	if (!buf)
+		return -1;
+
+	snprintf(tmp_elf, tmp_elf_sz, "/tmp/perf-bpf-XXXXXX");
+	tmp_fd = mkstemp(tmp_elf);
+	if (tmp_fd >= 0) {
+		uint16_t e_machine = args->arch ? args->arch->id.e_machine : EM_HOST;
+
+		if (jit_write_elf(tmp_fd, e_machine, map__rip_2objdump(map, sym->start),
+				  sym->name, buf, buf_len,
+				  NULL, 0, NULL, 0, 0) == 0) {
+			err = 0;
+		}
+		close(tmp_fd);
+		if (err)
+			unlink(tmp_elf);
+	}
+	free(code_buf);
+	return err;
+}
+#endif
+
 int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
 {
 	struct annotation_options *options = args->options;
@@ -1549,8 +1591,11 @@ int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
 	bool delete_extract = false;
 	struct kcore_extract kce;
 	bool decomp = false;
-	int err = dso__disassemble_filename(dso, symfs_filename, sizeof(symfs_filename));
+	int err;
+
+	args->is_temp_elf = false;
 
+	err = dso__disassemble_filename(dso, symfs_filename, sizeof(symfs_filename));
 	if (err)
 		return err;
 
@@ -1605,7 +1650,25 @@ int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
 
 	/* FIXME: LLVM and CAPSTONE should support source code */
 	if (options->annotate_src && !options->hide_src_code) {
-		err = symbol__disassemble_objdump(symfs_filename, sym, args);
+		const char *disasm_filename = symfs_filename;
+		bool is_temp = false;
+		char tmp_elf[PATH_MAX];
+
+#ifdef HAVE_LIBELF_SUPPORT
+		if (dso__binary_type(dso) == DSO_BINARY_TYPE__BPF_PROG_INFO) {
+			if (symbol__create_bpf_temp_elf(symfs_filename, sym, args,
+							tmp_elf, sizeof(tmp_elf)) == 0) {
+				disasm_filename = tmp_elf;
+				is_temp = true;
+				args->is_temp_elf = true;
+			}
+		}
+#endif
+		err = symbol__disassemble_objdump(disasm_filename, sym, args);
+		if (is_temp) {
+			unlink(tmp_elf);
+			args->is_temp_elf = false;
+		}
 		if (err == 0)
 			goto out_remove_tmp;
 	}
@@ -1613,29 +1676,63 @@ int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
 	err = -1;
 	for (u8 i = 0; i < ARRAY_SIZE(options->disassemblers) && err != 0; i++) {
 		enum perf_disassembler dis = options->disassemblers[i];
+		const char *disasm_filename = symfs_filename;
+		bool is_temp = false;
+		char tmp_elf[PATH_MAX];
+
+		switch (dis) {
+		case PERF_DISASM_LIBASM:
+		case PERF_DISASM_OBJDUMP:
+#ifdef HAVE_LIBELF_SUPPORT
+			if (dso__binary_type(dso) == DSO_BINARY_TYPE__BPF_PROG_INFO) {
+				if (symbol__create_bpf_temp_elf(symfs_filename, sym, args,
+								tmp_elf, sizeof(tmp_elf)) == 0) {
+					disasm_filename = tmp_elf;
+					is_temp = true;
+					args->is_temp_elf = true;
+				}
+			}
+#endif
+			break;
+		case PERF_DISASM_LLVM:
+		case PERF_DISASM_CAPSTONE:
+		case PERF_DISASM_UNKNOWN:
+		default:
+			break;
+		}
 
 		switch (dis) {
 		case PERF_DISASM_LLVM:
 			args->options->disassembler_used = PERF_DISASM_LLVM;
-			err = symbol__disassemble_llvm(symfs_filename, sym, args);
+			err = symbol__disassemble_llvm(disasm_filename, sym, args);
 			break;
 		case PERF_DISASM_CAPSTONE:
 			args->options->disassembler_used = PERF_DISASM_CAPSTONE;
-			err = symbol__disassemble_capstone(symfs_filename, sym, args);
+			err = symbol__disassemble_capstone(disasm_filename, sym, args);
 			break;
 		case PERF_DISASM_LIBASM:
 			args->options->disassembler_used = PERF_DISASM_LIBASM;
-			err = symbol__disassemble_libasm(symfs_filename, sym, args);
+			err = symbol__disassemble_libasm(disasm_filename, sym, args);
 			break;
 		case PERF_DISASM_OBJDUMP:
 			args->options->disassembler_used = PERF_DISASM_OBJDUMP;
-			err = symbol__disassemble_objdump(symfs_filename, sym, args);
+			err = symbol__disassemble_objdump(disasm_filename, sym, args);
 			break;
 		case PERF_DISASM_UNKNOWN: /* End of disassemblers. */
 		default:
 			args->options->disassembler_used = PERF_DISASM_UNKNOWN;
+			if (is_temp) {
+				unlink(tmp_elf);
+				args->is_temp_elf = false;
+			}
 			goto out_remove_tmp;
 		}
+
+		if (is_temp) {
+			unlink(tmp_elf);
+			args->is_temp_elf = false;
+		}
+
 		if (err == 0)
 			pr_debug("Disassembled with %s\n", perf_disassembler__strs[dis]);
 	}
diff --git a/tools/perf/util/disasm.h b/tools/perf/util/disasm.h
index 25756e3f47e4..32a5b3f5d1c6 100644
--- a/tools/perf/util/disasm.h
+++ b/tools/perf/util/disasm.h
@@ -106,6 +106,7 @@ struct annotate_args {
 	char			  *line;
 	int			  line_nr;
 	char			  *fileloc;
+	bool			  is_temp_elf;
 };
 
 const struct arch *arch__find(uint16_t e_machine, uint32_t e_flags, const char *cpuid);
diff --git a/tools/perf/util/genelf.c b/tools/perf/util/genelf.c
index 14882def9704..c3af040121ee 100644
--- a/tools/perf/util/genelf.c
+++ b/tools/perf/util/genelf.c
@@ -179,7 +179,7 @@ static void blake2s_update_tagged(struct blake2s_ctx *ctx, int tag,
  * csize: the code size in bytes
  */
 int
-jit_write_elf(int fd, uint64_t load_addr __maybe_unused, const char *sym,
+jit_write_elf(int fd, uint16_t e_machine, uint64_t load_addr, const char *sym,
 	      const void *code, int csize,
 	      void *debug __maybe_unused, int nr_debug_entries __maybe_unused,
 	      void *unwinding, uint64_t unwinding_header_size, uint64_t unwinding_size)
@@ -218,9 +218,9 @@ jit_write_elf(int fd, uint64_t load_addr __maybe_unused, const char *sym,
 
 	ehdr->e_ident[EI_DATA] = GEN_ELF_ENDIAN;
 	ehdr->e_ident[EI_CLASS] = GEN_ELF_CLASS;
-	ehdr->e_machine = GEN_ELF_ARCH;
+	ehdr->e_machine = e_machine;
 	ehdr->e_type = ET_DYN;
-	ehdr->e_entry = GEN_ELF_TEXT_OFFSET;
+	ehdr->e_entry = load_addr;
 	ehdr->e_version = EV_CURRENT;
 	ehdr->e_shstrndx= unwinding ? 4 : 2; /* shdr index for section name */
 
@@ -230,8 +230,8 @@ jit_write_elf(int fd, uint64_t load_addr __maybe_unused, const char *sym,
 	phdr = elf_newphdr(e, 1);
 	phdr[0].p_type = PT_LOAD;
 	phdr[0].p_offset = GEN_ELF_TEXT_OFFSET;
-	phdr[0].p_vaddr = GEN_ELF_TEXT_OFFSET;
-	phdr[0].p_paddr = GEN_ELF_TEXT_OFFSET;
+	phdr[0].p_vaddr = load_addr;
+	phdr[0].p_paddr = load_addr;
 	phdr[0].p_filesz = csize;
 	phdr[0].p_memsz = csize;
 	phdr[0].p_flags = PF_X | PF_R;
@@ -267,7 +267,7 @@ jit_write_elf(int fd, uint64_t load_addr __maybe_unused, const char *sym,
 
 	shdr->sh_name = 1;
 	shdr->sh_type = SHT_PROGBITS;
-	shdr->sh_addr = GEN_ELF_TEXT_OFFSET;
+	shdr->sh_addr = load_addr;
 	shdr->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
 	shdr->sh_entsize = 0;
 
@@ -278,7 +278,7 @@ jit_write_elf(int fd, uint64_t load_addr __maybe_unused, const char *sym,
 	 * Setup .eh_frame_hdr and .eh_frame
 	 */
 	if (unwinding) {
-		eh_frame_base_offset = ALIGN_8(GEN_ELF_TEXT_OFFSET + csize);
+		eh_frame_base_offset = ALIGN_8(load_addr + csize);
 		retval = jit_add_eh_frame_info(e, unwinding,
 					       unwinding_header_size, unwinding_size,
 					       eh_frame_base_offset);
@@ -324,7 +324,7 @@ jit_write_elf(int fd, uint64_t load_addr __maybe_unused, const char *sym,
 	 * setup symtab section
 	 */
 	symtab[1].st_size  = csize;
-	symtab[1].st_value = GEN_ELF_TEXT_OFFSET;
+	symtab[1].st_value = load_addr;
 
 	scn = elf_newscn(e);
 	if (!scn) {
diff --git a/tools/perf/util/genelf.h b/tools/perf/util/genelf.h
index 9f0b875d6548..992ca1eb2734 100644
--- a/tools/perf/util/genelf.h
+++ b/tools/perf/util/genelf.h
@@ -5,7 +5,7 @@
 #include <linux/math.h>
 
 /* genelf.c */
-int jit_write_elf(int fd, uint64_t code_addr, const char *sym,
+int jit_write_elf(int fd, uint16_t e_machine, uint64_t code_addr, const char *sym,
 		  const void *code, int csize, void *debug, int nr_debug_entries,
 		  void *unwinding, uint64_t unwinding_header_size, uint64_t unwinding_size);
 #ifdef HAVE_LIBDW_SUPPORT
diff --git a/tools/perf/util/jitdump.c b/tools/perf/util/jitdump.c
index 18fd84a82153..ddf69032b01c 100644
--- a/tools/perf/util/jitdump.c
+++ b/tools/perf/util/jitdump.c
@@ -95,7 +95,8 @@ jit_emit_elf(struct jit_buf_desc *jd,
 		return -1;
 	}
 
-	ret = jit_write_elf(fd, code_addr, sym, (const void *)code, csize, debug, nr_debug_entries,
+	ret = jit_write_elf(fd, GEN_ELF_ARCH, code_addr, sym,
+			    (const void *)code, csize, debug, nr_debug_entries,
 			    unwinding, unwinding_header_size, unwinding_size);
 
         close(fd);
-- 
2.54.0.1099.g489fc7bff1-goog


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v3 7/7] perf test: Add BPF JIT annotation test coverage for all disassemblers
  2026-06-09 18:21 ` [PATCH v3 0/7] perf annotate: Add elfutils libasm disassembler and BPF JIT disassembly support Ian Rogers
                     ` (5 preceding siblings ...)
  2026-06-09 18:21   ` [PATCH v3 6/7] perf annotate: Support BPF JIT disassembly via genelf Ian Rogers
@ 2026-06-09 18:21   ` Ian Rogers
  6 siblings, 0 replies; 16+ messages in thread
From: Ian Rogers @ 2026-06-09 18:21 UTC (permalink / raw)
  To: irogers, acme, namhyung
  Cc: 9erthalion6, adrian.hunter, alexandre.chartre, blakejones, bpf,
	costa.shul, dsterba, james.clark, jolsa, leo.yan, linux-kernel,
	linux-perf-users, mark, mingo, mjeanson, nathan, peterz,
	rong.bao, serhei, terrelln, tglozar, tianyou.li, yuzhuo, zecheng

Expand the annotate.sh shell test to verify BPF JIT disassembly.
If the test is run with sufficient privileges (root/CAP_BPF) and
captures system BPF programs, it will extract a JITted BPF program
symbol from the perf report and run perf annotate on it.
This validates the temporary ELF generation and disassembles it using
each of the supported disassembler backends (objdump, llvm, capstone,
and libasm).

Assisted-by: Antigravity:Google Gemini 3.5-flash
Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/tests/shell/annotate.sh | 93 ++++++++++++++++++++++++++++++
 1 file changed, 93 insertions(+)

diff --git a/tools/perf/tests/shell/annotate.sh b/tools/perf/tests/shell/annotate.sh
index eae6a46a247b..ae03580fa023 100755
--- a/tools/perf/tests/shell/annotate.sh
+++ b/tools/perf/tests/shell/annotate.sh
@@ -18,6 +18,7 @@ skip_test_missing_symbol ${testsym}
 err=0
 perfdata=$(mktemp /tmp/__perf_test.perf.data.XXXXX)
 perfout=$(mktemp /tmp/__perf_test.perf.out.XXXXX)
+bpftmp=""
 testprog="perf test -w noploop"
 # disassembly format: "percent : offset: instruction (operands ...)"
 disasm_regex="[0-9]*\.[0-9]* *: *\w*: *\w*"
@@ -25,6 +26,9 @@ disasm_regex="[0-9]*\.[0-9]* *: *\w*: *\w*"
 cleanup() {
   rm -rf "${perfdata}" "${perfout}"
   rm -rf "${perfdata}".old
+  if [ -n "${bpftmp}" ]; then
+    rm -rf "${bpftmp}"
+  fi
 
   trap - EXIT TERM INT
 }
@@ -169,5 +173,94 @@ then
   test_disassembler "libasm" "libasm"
 fi
 
+test_bpf_disassembler() {
+  disassembler=$1
+  feature=$2
+  bpf_perfdata_file=$3
+
+  if [ -n "${feature}" ]
+  then
+    if ! perf check feature "${feature}" > /dev/null 2>&1
+    then
+      echo "Skip BPF JIT test for ${disassembler} (feature ${feature} not supported)"
+      return 0
+    fi
+  fi
+
+  echo "Test BPF JIT annotate with disassembler: ${disassembler}"
+
+  if ! perf annotate --no-demangle -i "${bpf_perfdata_file}" --stdio "${bpf_sym}" \
+      --disassembler "${disassembler}" 2> /dev/null > "${perfout}"
+  then
+    echo "BPF JIT annotate with ${disassembler} [Failed: perf annotate error]"
+    err=1
+    return 0
+  fi
+
+  if ! grep -q "${disasm_regex}" "${perfout}"
+  then
+    echo "BPF JIT annotate with ${disassembler} [Failed: missing disasm output]"
+    err=1
+    return 0
+  fi
+
+  echo "BPF JIT annotate with ${disassembler} [Success]"
+  return 0
+}
+
+test_bpf() {
+  echo "Test annotate with BPF JIT output"
+
+  if ! perf check -q feature libbpf-strings ; then
+    echo "BPF annotation test [Skipped - libbpf-strings not supported]"
+    return 0
+  fi
+
+  bpftmp=$(mktemp -d /tmp/__perf_test.bpf.XXXXXX)
+  bpf_perfdata="${bpftmp}/perf.data"
+
+  if ! perf record -a -e cycles -F 4000 -o "${bpf_perfdata}" -- sleep 1 2> /dev/null
+  then
+    echo "BPF annotation test [Skipped - perf record -a failed, probably no privileges]"
+    rm -rf "${bpftmp}"
+    bpftmp=""
+    return 0
+  fi
+
+  bpf_symbols=$(perf report --stdio -i "${bpf_perfdata}" 2>/dev/null | \
+      grep -E -o 'bpf_prog_[0-9a-f]{16}_[0-9A-Za-z_]*' | sort -u)
+
+  bpf_sym=""
+  for sym in ${bpf_symbols}
+  do
+    # Check if we can annotate it (meaning JIT code is available)
+    if perf annotate -i "${bpf_perfdata}" --stdio "${sym}" > /dev/null 2>&1
+    then
+      bpf_sym="${sym}"
+      break
+    fi
+  done
+
+  if [ -z "${bpf_sym}" ]; then
+    echo "BPF annotation test [Skipped - no JITted BPF symbols with code found (race?)]"
+    rm -rf "${bpftmp}"
+    bpftmp=""
+    return 0
+  fi
+
+  test_bpf_disassembler "objdump" "" "${bpf_perfdata}"
+  test_bpf_disassembler "llvm" "libLLVM" "${bpf_perfdata}"
+  test_bpf_disassembler "capstone" "libcapstone" "${bpf_perfdata}"
+  test_bpf_disassembler "libasm" "libasm" "${bpf_perfdata}"
+
+  rm -rf "${bpftmp}"
+  bpftmp=""
+}
+
+if [ "${err}" -eq 0 ]
+then
+  test_bpf
+fi
+
 cleanup
 exit $err
-- 
2.54.0.1099.g489fc7bff1-goog


^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2026-06-09 18:21 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-06-09  7:07 [PATCH v2 0/7] perf annotate: Add elfutils libasm disassembler support Ian Rogers
2026-06-09  7:07 ` [PATCH v2 1/7] tools build: Add feature check for elfutils libasm Ian Rogers
2026-06-09  7:07 ` [PATCH v2 2/7] perf build: Add build support and capability " Ian Rogers
2026-06-09  7:07 ` [PATCH v2 3/7] perf annotate: Implement elfutils libasm disassembler backend Ian Rogers
2026-06-09  7:07 ` [PATCH v2 4/7] perf annotate: Add --disassembler command-line option Ian Rogers
2026-06-09  7:07 ` [PATCH v2 5/7] perf test: Enhance annotate test coverage and isolate config Ian Rogers
2026-06-09  7:07 ` [PATCH v2 6/7] perf annotate: Support BPF JIT disassembly via genelf Ian Rogers
2026-06-09  7:07 ` [PATCH v2 7/7] perf test: Add BPF JIT annotation test coverage for all disassemblers Ian Rogers
2026-06-09 18:21 ` [PATCH v3 0/7] perf annotate: Add elfutils libasm disassembler and BPF JIT disassembly support Ian Rogers
2026-06-09 18:21   ` [PATCH v3 1/7] tools build: Add feature check for elfutils libasm Ian Rogers
2026-06-09 18:21   ` [PATCH v3 2/7] perf build: Add build support and capability " Ian Rogers
2026-06-09 18:21   ` [PATCH v3 3/7] perf annotate: Implement elfutils libasm disassembler backend Ian Rogers
2026-06-09 18:21   ` [PATCH v3 4/7] perf annotate: Add --disassembler command-line option Ian Rogers
2026-06-09 18:21   ` [PATCH v3 5/7] perf test: Enhance annotate test coverage and isolate config Ian Rogers
2026-06-09 18:21   ` [PATCH v3 6/7] perf annotate: Support BPF JIT disassembly via genelf Ian Rogers
2026-06-09 18:21   ` [PATCH v3 7/7] perf test: Add BPF JIT annotation test coverage for all disassemblers Ian Rogers

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®