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 --- 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 +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include + +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