From: Ian Rogers <irogers@google.com>
To: irogers@google.com, acme@kernel.org, namhyung@kernel.org
Cc: 9erthalion6@gmail.com, adrian.hunter@intel.com,
alexandre.chartre@oracle.com, blakejones@google.com,
bpf@vger.kernel.org, costa.shul@redhat.com, dsterba@suse.com,
james.clark@linaro.org, jolsa@kernel.org, leo.yan@arm.com,
linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org,
mark@klomp.org, mingo@redhat.com, mjeanson@efficios.com,
nathan@kernel.org, peterz@infradead.org, rong.bao@csmantle.top,
serhei@serhei.io, terrelln@fb.com, tglozar@redhat.com,
tianyou.li@intel.com, yuzhuo@google.com, zecheng@google.com
Subject: [PATCH v3 3/7] perf annotate: Implement elfutils libasm disassembler backend
Date: Tue, 9 Jun 2026 11:21:04 -0700 [thread overview]
Message-ID: <20260609182108.975586-4-irogers@google.com> (raw)
In-Reply-To: <20260609182108.975586-1-irogers@google.com>
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, ¬es->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, ¬es->src->source);
+
+ insn_len = pc - prev_pc;
+ offset += insn_len;
+ addr += insn_len;
+ count++;
+ }
+
+ if (offset != buf_len) {
+ struct list_head *list = ¬es->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, ¬es->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
next prev parent reply other threads:[~2026-06-09 18:21 UTC|newest]
Thread overview: 25+ messages / expand[flat|nested] mbox.gz Atom feed top
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:21 ` sashiko-bot
2026-06-09 7:07 ` [PATCH v2 2/7] perf build: Add build support and capability " Ian Rogers
2026-06-09 7:19 ` sashiko-bot
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:15 ` sashiko-bot
2026-06-09 7:07 ` [PATCH v2 6/7] perf annotate: Support BPF JIT disassembly via genelf Ian Rogers
2026-06-09 7:22 ` sashiko-bot
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 7:18 ` sashiko-bot
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:46 ` sashiko-bot
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 [this message]
2026-06-09 18:52 ` [PATCH v3 3/7] perf annotate: Implement elfutils libasm disassembler backend sashiko-bot
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:46 ` sashiko-bot
2026-06-09 18:21 ` [PATCH v3 6/7] perf annotate: Support BPF JIT disassembly via genelf Ian Rogers
2026-06-09 18:49 ` sashiko-bot
2026-06-09 18:21 ` [PATCH v3 7/7] perf test: Add BPF JIT annotation test coverage for all disassemblers Ian Rogers
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260609182108.975586-4-irogers@google.com \
--to=irogers@google.com \
--cc=9erthalion6@gmail.com \
--cc=acme@kernel.org \
--cc=adrian.hunter@intel.com \
--cc=alexandre.chartre@oracle.com \
--cc=blakejones@google.com \
--cc=bpf@vger.kernel.org \
--cc=costa.shul@redhat.com \
--cc=dsterba@suse.com \
--cc=james.clark@linaro.org \
--cc=jolsa@kernel.org \
--cc=leo.yan@arm.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-perf-users@vger.kernel.org \
--cc=mark@klomp.org \
--cc=mingo@redhat.com \
--cc=mjeanson@efficios.com \
--cc=namhyung@kernel.org \
--cc=nathan@kernel.org \
--cc=peterz@infradead.org \
--cc=rong.bao@csmantle.top \
--cc=serhei@serhei.io \
--cc=terrelln@fb.com \
--cc=tglozar@redhat.com \
--cc=tianyou.li@intel.com \
--cc=yuzhuo@google.com \
--cc=zecheng@google.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.