* [PATCH v1 0/5] Support dynamic opening of capstone and libLLVM
@ 2025-01-20 17:32 Ian Rogers
2025-01-20 17:32 ` [PATCH v1 1/5] perf capstone: Move capstone functionality into its own file Ian Rogers
` (5 more replies)
0 siblings, 6 replies; 12+ messages in thread
From: Ian Rogers @ 2025-01-20 17:32 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Ian Rogers, Adrian Hunter, Kan Liang, Nathan Chancellor,
Nick Desaulniers, Bill Wendling, Justin Stitt,
Steinar H. Gunderson, Charlie Jenkins, Changbin Du,
Masami Hiramatsu (Google), James Clark, Kajol Jain, Athira Rajeev,
Li Huafei, Dmitry Vyukov, Andi Kleen, linux-kernel,
linux-perf-users, llvm
Linking against libcapstone and libLLVM can be a significant increase
in dependencies and size of memory footprint. For something like `perf
record` the disassembler and addr2line functionality won't be
used. Support dynamically loading these libraries using dlopen and
then calling the appropriate functions found using dlsym.
The patch series:
1) moves the capstone and LLVM code to their own C files,
2) simplifies a little the capstone code,
3) adds perf_ variants of the functions that will either directly call
the function or use dlsym to discover it.
The addr2line LLVM functionality is written in C++. To avoid linking
against libLLVM for this, a new LIBLLVM_DYNAMIC option is added where
the C++ code with the libLLVM dependency will be built into a
libperf-llvm.so and that dlsym-ed and called against.
Ian Rogers (5):
perf capstone: Move capstone functionality into its own file
perf llvm: Move llvm functionality into its own file
perf capstone: Remove open_capstone_handle
perf capstone: Support for dlopen-ing libcapstone.so
perf llvm: Support for dlopen-ing libLLVM.so
tools/perf/Makefile.config | 13 +
tools/perf/Makefile.perf | 23 +-
tools/perf/builtin-script.c | 2 -
tools/perf/tests/make | 2 +
tools/perf/util/Build | 4 +-
tools/perf/util/capstone.c | 723 +++++++++++++++++++++++++++++
tools/perf/util/capstone.h | 24 +
tools/perf/util/disasm.c | 612 +-----------------------
tools/perf/util/disasm.h | 2 +
tools/perf/util/llvm-c-helpers.cpp | 113 ++++-
tools/perf/util/llvm.c | 537 +++++++++++++++++++++
tools/perf/util/llvm.h | 24 +
tools/perf/util/print_insn.c | 117 +----
tools/perf/util/srcline.c | 65 +--
tools/perf/util/srcline.h | 6 +
15 files changed, 1481 insertions(+), 786 deletions(-)
create mode 100644 tools/perf/util/capstone.c
create mode 100644 tools/perf/util/capstone.h
create mode 100644 tools/perf/util/llvm.c
create mode 100644 tools/perf/util/llvm.h
--
2.48.0.rc2.279.g1de40edade-goog
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v1 1/5] perf capstone: Move capstone functionality into its own file
2025-01-20 17:32 [PATCH v1 0/5] Support dynamic opening of capstone and libLLVM Ian Rogers
@ 2025-01-20 17:32 ` Ian Rogers
2025-01-20 17:32 ` [PATCH v1 2/5] perf llvm: Move llvm " Ian Rogers
` (4 subsequent siblings)
5 siblings, 0 replies; 12+ messages in thread
From: Ian Rogers @ 2025-01-20 17:32 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Ian Rogers, Adrian Hunter, Kan Liang, Nathan Chancellor,
Nick Desaulniers, Bill Wendling, Justin Stitt,
Steinar H. Gunderson, Charlie Jenkins, Changbin Du,
Masami Hiramatsu (Google), James Clark, Kajol Jain, Athira Rajeev,
Li Huafei, Dmitry Vyukov, Andi Kleen, linux-kernel,
linux-perf-users, llvm
Capstone disassembly support was split between disasm.c and
print_insn.c. Move support out of these files into capstone.[ch] and
remove include capstone/capstone.h from those files. As disassembly
routines can fail, make failure the only option without
HAVE_LIBCAPSTONE_SUPPORT. For simplicity's sake, duplicate the
read_symbol utility function.
The intent with moving capstone support into a single file is that
dynamic support, using dlopen for libcapstone, can be added in later
patches. This can potentially always succeed or fail, so relying on
ifdefs isn't sufficient. Using dlopen is a useful option to minimize
the perf tools dependencies and potentially size.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/builtin-script.c | 2 -
tools/perf/util/Build | 1 +
tools/perf/util/capstone.c | 536 +++++++++++++++++++++++++++++++++++
tools/perf/util/capstone.h | 24 ++
tools/perf/util/disasm.c | 358 +----------------------
tools/perf/util/print_insn.c | 117 +-------
6 files changed, 569 insertions(+), 469 deletions(-)
create mode 100644 tools/perf/util/capstone.c
create mode 100644 tools/perf/util/capstone.h
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
index 33667b534634..f05b2b70d5a7 100644
--- a/tools/perf/builtin-script.c
+++ b/tools/perf/builtin-script.c
@@ -1200,7 +1200,6 @@ static int any_dump_insn(struct evsel *evsel __maybe_unused,
u8 *inbuf, int inlen, int *lenp,
FILE *fp)
{
-#ifdef HAVE_LIBCAPSTONE_SUPPORT
if (PRINT_FIELD(BRSTACKDISASM)) {
int printed = fprintf_insn_asm(x->machine, x->thread, x->cpumode, x->is64bit,
(uint8_t *)inbuf, inlen, ip, lenp,
@@ -1209,7 +1208,6 @@ static int any_dump_insn(struct evsel *evsel __maybe_unused,
if (printed > 0)
return printed;
}
-#endif
return fprintf(fp, "%s", dump_insn(x, ip, inbuf, inlen, lenp));
}
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 5ec97e8d6b6d..9542decf9625 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -8,6 +8,7 @@ perf-util-y += block-info.o
perf-util-y += block-range.o
perf-util-y += build-id.o
perf-util-y += cacheline.o
+perf-util-y += capstone.o
perf-util-y += config.o
perf-util-y += copyfile.o
perf-util-y += ctype.o
diff --git a/tools/perf/util/capstone.c b/tools/perf/util/capstone.c
new file mode 100644
index 000000000000..c0a6d94ebc18
--- /dev/null
+++ b/tools/perf/util/capstone.c
@@ -0,0 +1,536 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "capstone.h"
+#include "annotate.h"
+#include "addr_location.h"
+#include "debug.h"
+#include "disasm.h"
+#include "dso.h"
+#include "machine.h"
+#include "map.h"
+#include "namespaces.h"
+#include "print_insn.h"
+#include "symbol.h"
+#include "thread.h"
+#include <fcntl.h>
+#include <string.h>
+
+#ifdef HAVE_LIBCAPSTONE_SUPPORT
+#include <capstone/capstone.h>
+#endif
+
+#ifdef HAVE_LIBCAPSTONE_SUPPORT
+static int capstone_init(struct machine *machine, csh *cs_handle, bool is64,
+ bool disassembler_style)
+{
+ cs_arch arch;
+ cs_mode mode;
+
+ if (machine__is(machine, "x86_64") && is64) {
+ arch = CS_ARCH_X86;
+ mode = CS_MODE_64;
+ } else if (machine__normalized_is(machine, "x86")) {
+ arch = CS_ARCH_X86;
+ mode = CS_MODE_32;
+ } else if (machine__normalized_is(machine, "arm64")) {
+ arch = CS_ARCH_ARM64;
+ mode = CS_MODE_ARM;
+ } else if (machine__normalized_is(machine, "arm")) {
+ arch = CS_ARCH_ARM;
+ mode = CS_MODE_ARM + CS_MODE_V8;
+ } else if (machine__normalized_is(machine, "s390")) {
+ arch = CS_ARCH_SYSZ;
+ mode = CS_MODE_BIG_ENDIAN;
+ } else {
+ return -1;
+ }
+
+ if (cs_open(arch, mode, cs_handle) != CS_ERR_OK) {
+ pr_warning_once("cs_open failed\n");
+ return -1;
+ }
+
+ if (machine__normalized_is(machine, "x86")) {
+ /*
+ * In case of using capstone_init while symbol__disassemble
+ * setting CS_OPT_SYNTAX_ATT depends if disassembler_style opts
+ * is set via annotation args
+ */
+ if (disassembler_style)
+ cs_option(*cs_handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_ATT);
+ /*
+ * Resolving address operands to symbols is implemented
+ * on x86 by investigating instruction details.
+ */
+ cs_option(*cs_handle, CS_OPT_DETAIL, CS_OPT_ON);
+ }
+
+ return 0;
+}
+#endif
+
+#ifdef HAVE_LIBCAPSTONE_SUPPORT
+static size_t print_insn_x86(struct thread *thread, u8 cpumode, cs_insn *insn,
+ int print_opts, FILE *fp)
+{
+ struct addr_location al;
+ size_t printed = 0;
+
+ if (insn->detail && insn->detail->x86.op_count == 1) {
+ cs_x86_op *op = &insn->detail->x86.operands[0];
+
+ addr_location__init(&al);
+ if (op->type == X86_OP_IMM &&
+ thread__find_symbol(thread, cpumode, op->imm, &al)) {
+ printed += fprintf(fp, "%s ", insn[0].mnemonic);
+ printed += symbol__fprintf_symname_offs(al.sym, &al, fp);
+ if (print_opts & PRINT_INSN_IMM_HEX)
+ printed += fprintf(fp, " [%#" PRIx64 "]", op->imm);
+ addr_location__exit(&al);
+ return printed;
+ }
+ addr_location__exit(&al);
+ }
+
+ printed += fprintf(fp, "%s %s", insn[0].mnemonic, insn[0].op_str);
+ return printed;
+}
+#endif
+
+
+ssize_t capstone__fprintf_insn_asm(struct machine *machine __maybe_unused,
+ struct thread *thread __maybe_unused,
+ u8 cpumode __maybe_unused, bool is64bit __maybe_unused,
+ const uint8_t *code __maybe_unused,
+ size_t code_size __maybe_unused,
+ uint64_t ip __maybe_unused, int *lenp __maybe_unused,
+ int print_opts __maybe_unused, FILE *fp __maybe_unused)
+{
+#ifdef HAVE_LIBCAPSTONE_SUPPORT
+ size_t printed;
+ cs_insn *insn;
+ csh cs_handle;
+ size_t count;
+ int ret;
+
+ /* TODO: Try to initiate capstone only once but need a proper place. */
+ ret = capstone_init(machine, &cs_handle, is64bit, true);
+ if (ret < 0)
+ return ret;
+
+ count = cs_disasm(cs_handle, code, code_size, ip, 1, &insn);
+ if (count > 0) {
+ if (machine__normalized_is(machine, "x86"))
+ printed = print_insn_x86(thread, cpumode, &insn[0], print_opts, fp);
+ else
+ printed = fprintf(fp, "%s %s", insn[0].mnemonic, insn[0].op_str);
+ if (lenp)
+ *lenp = insn->size;
+ cs_free(insn, count);
+ } else {
+ printed = -1;
+ }
+
+ cs_close(&cs_handle);
+ return printed;
+#else
+ return -1;
+#endif
+}
+
+#ifdef HAVE_LIBCAPSTONE_SUPPORT
+static int open_capstone_handle(struct annotate_args *args, bool is_64bit, csh *handle)
+{
+ struct annotation_options *opt = args->options;
+ cs_mode mode = is_64bit ? CS_MODE_64 : CS_MODE_32;
+
+ /* TODO: support more architectures */
+ if (!arch__is(args->arch, "x86"))
+ return -1;
+
+ if (cs_open(CS_ARCH_X86, mode, handle) != CS_ERR_OK)
+ return -1;
+
+ if (!opt->disassembler_style ||
+ !strcmp(opt->disassembler_style, "att"))
+ cs_option(*handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_ATT);
+
+ /*
+ * Resolving address operands to symbols is implemented
+ * on x86 by investigating instruction details.
+ */
+ cs_option(*handle, CS_OPT_DETAIL, CS_OPT_ON);
+
+ return 0;
+}
+#endif
+
+#ifdef HAVE_LIBCAPSTONE_SUPPORT
+static void print_capstone_detail(cs_insn *insn, char *buf, size_t len,
+ struct annotate_args *args, u64 addr)
+{
+ int i;
+ struct map *map = args->ms.map;
+ struct symbol *sym;
+
+ /* TODO: support more architectures */
+ if (!arch__is(args->arch, "x86"))
+ return;
+
+ if (insn->detail == NULL)
+ return;
+
+ for (i = 0; i < insn->detail->x86.op_count; i++) {
+ cs_x86_op *op = &insn->detail->x86.operands[i];
+ u64 orig_addr;
+
+ if (op->type != X86_OP_MEM)
+ continue;
+
+ /* only print RIP-based global symbols for now */
+ if (op->mem.base != X86_REG_RIP)
+ continue;
+
+ /* get the target address */
+ orig_addr = addr + insn->size + op->mem.disp;
+ addr = map__objdump_2mem(map, orig_addr);
+
+ if (dso__kernel(map__dso(map))) {
+ /*
+ * The kernel maps can be splitted into sections,
+ * let's find the map first and the search the symbol.
+ */
+ map = maps__find(map__kmaps(map), addr);
+ if (map == NULL)
+ continue;
+ }
+
+ /* convert it to map-relative address for search */
+ addr = map__map_ip(map, addr);
+
+ sym = map__find_symbol(map, addr);
+ if (sym == NULL)
+ continue;
+
+ if (addr == sym->start) {
+ scnprintf(buf, len, "\t# %"PRIx64" <%s>",
+ orig_addr, sym->name);
+ } else {
+ scnprintf(buf, len, "\t# %"PRIx64" <%s+%#"PRIx64">",
+ orig_addr, sym->name, addr - sym->start);
+ }
+ break;
+ }
+}
+#endif
+
+#ifdef HAVE_LIBCAPSTONE_SUPPORT
+struct find_file_offset_data {
+ u64 ip;
+ u64 offset;
+};
+
+/* This will be called for each PHDR in an ELF binary */
+static int find_file_offset(u64 start, u64 len, u64 pgoff, void *arg)
+{
+ struct find_file_offset_data *data = arg;
+
+ if (start <= data->ip && data->ip < start + len) {
+ data->offset = pgoff + data->ip - start;
+ return 1;
+ }
+ return 0;
+}
+#endif
+
+#ifdef HAVE_LIBCAPSTONE_SUPPORT
+static u8 *
+read_symbol(const char *filename, struct map *map, struct symbol *sym,
+ u64 *len, bool *is_64bit)
+{
+ struct dso *dso = map__dso(map);
+ struct nscookie nsc;
+ u64 start = map__rip_2objdump(map, sym->start);
+ u64 end = map__rip_2objdump(map, sym->end);
+ int fd, count;
+ u8 *buf = NULL;
+ struct find_file_offset_data data = {
+ .ip = start,
+ };
+
+ *is_64bit = false;
+
+ nsinfo__mountns_enter(dso__nsinfo(dso), &nsc);
+ fd = open(filename, O_RDONLY);
+ nsinfo__mountns_exit(&nsc);
+ if (fd < 0)
+ return NULL;
+
+ if (file__read_maps(fd, /*exe=*/true, find_file_offset, &data,
+ is_64bit) == 0)
+ goto err;
+
+ *len = end - start;
+ buf = malloc(*len);
+ if (buf == NULL)
+ goto err;
+
+ count = pread(fd, buf, *len, data.offset);
+ close(fd);
+ fd = -1;
+
+ if ((u64)count != *len)
+ goto err;
+
+ return buf;
+
+err:
+ if (fd >= 0)
+ close(fd);
+ free(buf);
+ return NULL;
+}
+#endif
+
+int symbol__disassemble_capstone(const char *filename __maybe_unused,
+ struct symbol *sym __maybe_unused,
+ struct annotate_args *args __maybe_unused)
+{
+#ifdef HAVE_LIBCAPSTONE_SUPPORT
+ struct annotation *notes = symbol__annotation(sym);
+ struct map *map = args->ms.map;
+ u64 start = map__rip_2objdump(map, sym->start);
+ u64 len;
+ u64 offset;
+ int i, count, free_count;
+ bool is_64bit = false;
+ bool needs_cs_close = false;
+ u8 *buf = NULL;
+ csh handle;
+ cs_insn *insn = NULL;
+ char disasm_buf[512];
+ struct disasm_line *dl;
+
+ if (args->options->objdump_path)
+ return -1;
+
+ buf = read_symbol(filename, map, sym, &len, &is_64bit);
+ if (buf == NULL)
+ return -1;
+
+ /* 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);
+
+ if (open_capstone_handle(args, is_64bit, &handle) < 0)
+ goto err;
+
+ needs_cs_close = true;
+
+ free_count = count = cs_disasm(handle, buf, len, start, len, &insn);
+ for (i = 0, offset = 0; i < count; i++) {
+ int printed;
+
+ printed = scnprintf(disasm_buf, sizeof(disasm_buf),
+ " %-7s %s",
+ insn[i].mnemonic, insn[i].op_str);
+ print_capstone_detail(&insn[i], disasm_buf + printed,
+ sizeof(disasm_buf) - printed, args,
+ start + offset);
+
+ 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);
+
+ offset += insn[i].size;
+ }
+
+ /* It failed in the middle: probably due to unknown instructions */
+ if (offset != 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 (needs_cs_close) {
+ cs_close(&handle);
+ if (free_count > 0)
+ cs_free(insn, free_count);
+ }
+ free(buf);
+ return count < 0 ? count : 0;
+
+err:
+ if (needs_cs_close) {
+ 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;
+#else
+ return -1;
+#endif
+}
+
+int symbol__disassemble_capstone_powerpc(const char *filename __maybe_unused,
+ struct symbol *sym __maybe_unused,
+ struct annotate_args *args __maybe_unused)
+{
+#ifdef HAVE_LIBCAPSTONE_SUPPORT
+ struct annotation *notes = symbol__annotation(sym);
+ struct map *map = args->ms.map;
+ struct dso *dso = map__dso(map);
+ struct nscookie nsc;
+ u64 start = map__rip_2objdump(map, sym->start);
+ u64 end = map__rip_2objdump(map, sym->end);
+ u64 len = end - start;
+ u64 offset;
+ int i, fd, count;
+ bool is_64bit = false;
+ bool needs_cs_close = false;
+ u8 *buf = NULL;
+ struct find_file_offset_data data = {
+ .ip = start,
+ };
+ csh handle;
+ char disasm_buf[512];
+ struct disasm_line *dl;
+ u32 *line;
+ bool disassembler_style = false;
+
+ if (args->options->objdump_path)
+ return -1;
+
+ nsinfo__mountns_enter(dso__nsinfo(dso), &nsc);
+ fd = open(filename, O_RDONLY);
+ nsinfo__mountns_exit(&nsc);
+ if (fd < 0)
+ return -1;
+
+ if (file__read_maps(fd, /*exe=*/true, find_file_offset, &data,
+ &is_64bit) == 0)
+ goto err;
+
+ if (!args->options->disassembler_style ||
+ !strcmp(args->options->disassembler_style, "att"))
+ disassembler_style = true;
+
+ if (capstone_init(maps__machine(args->ms.maps), &handle, is_64bit, disassembler_style) < 0)
+ goto err;
+
+ needs_cs_close = true;
+
+ buf = malloc(len);
+ if (buf == NULL)
+ goto err;
+
+ count = pread(fd, buf, len, data.offset);
+ close(fd);
+ fd = -1;
+
+ if ((u64)count != len)
+ goto err;
+
+ line = (u32 *)buf;
+
+ /* 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);
+
+ /*
+ * TODO: enable disassm for powerpc
+ * count = cs_disasm(handle, buf, len, start, len, &insn);
+ *
+ * For now, only binary code is saved in disassembled line
+ * to be used in "type" and "typeoff" sort keys. Each raw code
+ * is 32 bit instruction. So use "len/4" to get the number of
+ * entries.
+ */
+ count = len/4;
+
+ for (i = 0, offset = 0; i < count; i++) {
+ args->offset = offset;
+ sprintf(args->line, "%x", line[i]);
+
+ dl = disasm_line__new(args);
+ if (dl == NULL)
+ break;
+
+ annotation_line__add(&dl->al, ¬es->src->source);
+
+ offset += 4;
+ }
+
+ /* It failed in the middle */
+ if (offset != 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 (needs_cs_close)
+ cs_close(&handle);
+ free(buf);
+ return count < 0 ? count : 0;
+
+err:
+ if (fd >= 0)
+ close(fd);
+ count = -1;
+ goto out;
+#else
+ return -1;
+#endif
+}
diff --git a/tools/perf/util/capstone.h b/tools/perf/util/capstone.h
new file mode 100644
index 000000000000..0f030ea034b6
--- /dev/null
+++ b/tools/perf/util/capstone.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __PERF_CAPSTONE_H
+#define __PERF_CAPSTONE_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <linux/types.h>
+
+struct annotate_args;
+struct machine;
+struct symbol;
+struct thread;
+
+ssize_t capstone__fprintf_insn_asm(struct machine *machine, struct thread *thread, u8 cpumode,
+ bool is64bit, const uint8_t *code, size_t code_size,
+ uint64_t ip, int *lenp, int print_opts, FILE *fp);
+int symbol__disassemble_capstone(const char *filename, struct symbol *sym,
+ struct annotate_args *args);
+int symbol__disassemble_capstone_powerpc(const char *filename, struct symbol *sym,
+ struct annotate_args *args);
+
+#endif /* __PERF_CAPSTONE_H */
diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c
index b7de4d9fd004..0e5881189ae8 100644
--- a/tools/perf/util/disasm.c
+++ b/tools/perf/util/disasm.c
@@ -14,6 +14,7 @@
#include "annotate.h"
#include "annotate-data.h"
#include "build-id.h"
+#include "capstone.h"
#include "debug.h"
#include "disasm.h"
#include "disasm_bpf.h"
@@ -1329,39 +1330,7 @@ static int dso__disassemble_filename(struct dso *dso, char *filename, size_t fil
return 0;
}
-#ifdef HAVE_LIBCAPSTONE_SUPPORT
-#include <capstone/capstone.h>
-
-int capstone_init(struct machine *machine, csh *cs_handle, bool is64, bool disassembler_style);
-
-static int open_capstone_handle(struct annotate_args *args, bool is_64bit,
- csh *handle)
-{
- struct annotation_options *opt = args->options;
- cs_mode mode = is_64bit ? CS_MODE_64 : CS_MODE_32;
-
- /* TODO: support more architectures */
- if (!arch__is(args->arch, "x86"))
- return -1;
-
- if (cs_open(CS_ARCH_X86, mode, handle) != CS_ERR_OK)
- return -1;
-
- if (!opt->disassembler_style ||
- !strcmp(opt->disassembler_style, "att"))
- cs_option(*handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_ATT);
-
- /*
- * Resolving address operands to symbols is implemented
- * on x86 by investigating instruction details.
- */
- cs_option(*handle, CS_OPT_DETAIL, CS_OPT_ON);
-
- return 0;
-}
-#endif
-
-#if defined(HAVE_LIBCAPSTONE_SUPPORT) || defined(HAVE_LIBLLVM_SUPPORT)
+#if defined(HAVE_LIBLLVM_SUPPORT)
struct find_file_offset_data {
u64 ip;
u64 offset;
@@ -1427,322 +1396,6 @@ read_symbol(const char *filename, struct map *map, struct symbol *sym,
}
#endif
-#if !defined(HAVE_LIBCAPSTONE_SUPPORT) || !defined(HAVE_LIBLLVM_SUPPORT)
-static void symbol__disassembler_missing(const char *disassembler, const char *filename,
- struct symbol *sym)
-{
- pr_debug("The %s disassembler isn't linked in for %s in %s\n",
- disassembler, sym->name, filename);
-}
-#endif
-
-#ifdef HAVE_LIBCAPSTONE_SUPPORT
-static void print_capstone_detail(cs_insn *insn, char *buf, size_t len,
- struct annotate_args *args, u64 addr)
-{
- int i;
- struct map *map = args->ms.map;
- struct symbol *sym;
-
- /* TODO: support more architectures */
- if (!arch__is(args->arch, "x86"))
- return;
-
- if (insn->detail == NULL)
- return;
-
- for (i = 0; i < insn->detail->x86.op_count; i++) {
- cs_x86_op *op = &insn->detail->x86.operands[i];
- u64 orig_addr;
-
- if (op->type != X86_OP_MEM)
- continue;
-
- /* only print RIP-based global symbols for now */
- if (op->mem.base != X86_REG_RIP)
- continue;
-
- /* get the target address */
- orig_addr = addr + insn->size + op->mem.disp;
- addr = map__objdump_2mem(map, orig_addr);
-
- if (dso__kernel(map__dso(map))) {
- /*
- * The kernel maps can be splitted into sections,
- * let's find the map first and the search the symbol.
- */
- map = maps__find(map__kmaps(map), addr);
- if (map == NULL)
- continue;
- }
-
- /* convert it to map-relative address for search */
- addr = map__map_ip(map, addr);
-
- sym = map__find_symbol(map, addr);
- if (sym == NULL)
- continue;
-
- if (addr == sym->start) {
- scnprintf(buf, len, "\t# %"PRIx64" <%s>",
- orig_addr, sym->name);
- } else {
- scnprintf(buf, len, "\t# %"PRIx64" <%s+%#"PRIx64">",
- orig_addr, sym->name, addr - sym->start);
- }
- break;
- }
-}
-
-static int symbol__disassemble_capstone_powerpc(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);
- struct nscookie nsc;
- u64 start = map__rip_2objdump(map, sym->start);
- u64 end = map__rip_2objdump(map, sym->end);
- u64 len = end - start;
- u64 offset;
- int i, fd, count;
- bool is_64bit = false;
- bool needs_cs_close = false;
- u8 *buf = NULL;
- struct find_file_offset_data data = {
- .ip = start,
- };
- csh handle;
- char disasm_buf[512];
- struct disasm_line *dl;
- u32 *line;
- bool disassembler_style = false;
-
- if (args->options->objdump_path)
- return -1;
-
- nsinfo__mountns_enter(dso__nsinfo(dso), &nsc);
- fd = open(filename, O_RDONLY);
- nsinfo__mountns_exit(&nsc);
- if (fd < 0)
- return -1;
-
- if (file__read_maps(fd, /*exe=*/true, find_file_offset, &data,
- &is_64bit) == 0)
- goto err;
-
- if (!args->options->disassembler_style ||
- !strcmp(args->options->disassembler_style, "att"))
- disassembler_style = true;
-
- if (capstone_init(maps__machine(args->ms.maps), &handle, is_64bit, disassembler_style) < 0)
- goto err;
-
- needs_cs_close = true;
-
- buf = malloc(len);
- if (buf == NULL)
- goto err;
-
- count = pread(fd, buf, len, data.offset);
- close(fd);
- fd = -1;
-
- if ((u64)count != len)
- goto err;
-
- line = (u32 *)buf;
-
- /* 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);
-
- /*
- * TODO: enable disassm for powerpc
- * count = cs_disasm(handle, buf, len, start, len, &insn);
- *
- * For now, only binary code is saved in disassembled line
- * to be used in "type" and "typeoff" sort keys. Each raw code
- * is 32 bit instruction. So use "len/4" to get the number of
- * entries.
- */
- count = len/4;
-
- for (i = 0, offset = 0; i < count; i++) {
- args->offset = offset;
- sprintf(args->line, "%x", line[i]);
-
- dl = disasm_line__new(args);
- if (dl == NULL)
- break;
-
- annotation_line__add(&dl->al, ¬es->src->source);
-
- offset += 4;
- }
-
- /* It failed in the middle */
- if (offset != 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 (needs_cs_close)
- cs_close(&handle);
- free(buf);
- return count < 0 ? count : 0;
-
-err:
- if (fd >= 0)
- close(fd);
- count = -1;
- goto out;
-}
-
-static int symbol__disassemble_capstone(char *filename, struct symbol *sym,
- struct annotate_args *args)
-{
- struct annotation *notes = symbol__annotation(sym);
- struct map *map = args->ms.map;
- u64 start = map__rip_2objdump(map, sym->start);
- u64 len;
- u64 offset;
- int i, count, free_count;
- bool is_64bit = false;
- bool needs_cs_close = false;
- u8 *buf = NULL;
- csh handle;
- cs_insn *insn = NULL;
- char disasm_buf[512];
- struct disasm_line *dl;
-
- if (args->options->objdump_path)
- return -1;
-
- buf = read_symbol(filename, map, sym, &len, &is_64bit);
- if (buf == NULL)
- return -1;
-
- /* 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);
-
- if (open_capstone_handle(args, is_64bit, &handle) < 0)
- goto err;
-
- needs_cs_close = true;
-
- free_count = count = cs_disasm(handle, buf, len, start, len, &insn);
- for (i = 0, offset = 0; i < count; i++) {
- int printed;
-
- printed = scnprintf(disasm_buf, sizeof(disasm_buf),
- " %-7s %s",
- insn[i].mnemonic, insn[i].op_str);
- print_capstone_detail(&insn[i], disasm_buf + printed,
- sizeof(disasm_buf) - printed, args,
- start + offset);
-
- 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);
-
- offset += insn[i].size;
- }
-
- /* It failed in the middle: probably due to unknown instructions */
- if (offset != 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 (needs_cs_close) {
- cs_close(&handle);
- if (free_count > 0)
- cs_free(insn, free_count);
- }
- free(buf);
- return count < 0 ? count : 0;
-
-err:
- if (needs_cs_close) {
- 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;
-}
-#else // HAVE_LIBCAPSTONE_SUPPORT
-static int symbol__disassemble_capstone(char *filename, struct symbol *sym,
- struct annotate_args *args __maybe_unused)
-{
- symbol__disassembler_missing("capstone", filename, sym);
- return -1;
-}
-
-static int symbol__disassemble_capstone_powerpc(char *filename, struct symbol *sym,
- struct annotate_args *args __maybe_unused)
-{
- symbol__disassembler_missing("capstone powerpc", filename, sym);
- return -1;
-}
-#endif // HAVE_LIBCAPSTONE_SUPPORT
-
static int symbol__disassemble_raw(char *filename, struct symbol *sym,
struct annotate_args *args)
{
@@ -2010,10 +1663,11 @@ static int symbol__disassemble_llvm(char *filename, struct symbol *sym,
return ret;
}
#else // HAVE_LIBLLVM_SUPPORT
-static int symbol__disassemble_llvm(char *filename, struct symbol *sym,
+static int symbol__disassemble_llvm(const char *filename, struct symbol *sym,
struct annotate_args *args __maybe_unused)
{
- symbol__disassembler_missing("LLVM", filename, sym);
+ pr_debug("The LLVM disassembler isn't linked in for %s in %s\n",
+ sym->name, filename);
return -1;
}
#endif // HAVE_LIBLLVM_SUPPORT
@@ -2225,9 +1879,7 @@ static int annotation_options__init_disassemblers(struct annotation_options *opt
#ifdef HAVE_LIBLLVM_SUPPORT
"llvm,"
#endif
-#ifdef HAVE_LIBCAPSTONE_SUPPORT
"capstone,"
-#endif
"objdump";
options->disassemblers_str = strdup(default_disassemblers_str);
diff --git a/tools/perf/util/print_insn.c b/tools/perf/util/print_insn.c
index a33a7726422d..02e6fbb8ca04 100644
--- a/tools/perf/util/print_insn.c
+++ b/tools/perf/util/print_insn.c
@@ -7,6 +7,7 @@
#include <inttypes.h>
#include <string.h>
#include <stdbool.h>
+#include "capstone.h"
#include "debug.h"
#include "sample.h"
#include "symbol.h"
@@ -29,84 +30,6 @@ size_t sample__fprintf_insn_raw(struct perf_sample *sample, FILE *fp)
return printed;
}
-#ifdef HAVE_LIBCAPSTONE_SUPPORT
-#include <capstone/capstone.h>
-
-int capstone_init(struct machine *machine, csh *cs_handle, bool is64, bool disassembler_style);
-
-int capstone_init(struct machine *machine, csh *cs_handle, bool is64, bool disassembler_style)
-{
- cs_arch arch;
- cs_mode mode;
-
- if (machine__is(machine, "x86_64") && is64) {
- arch = CS_ARCH_X86;
- mode = CS_MODE_64;
- } else if (machine__normalized_is(machine, "x86")) {
- arch = CS_ARCH_X86;
- mode = CS_MODE_32;
- } else if (machine__normalized_is(machine, "arm64")) {
- arch = CS_ARCH_ARM64;
- mode = CS_MODE_ARM;
- } else if (machine__normalized_is(machine, "arm")) {
- arch = CS_ARCH_ARM;
- mode = CS_MODE_ARM + CS_MODE_V8;
- } else if (machine__normalized_is(machine, "s390")) {
- arch = CS_ARCH_SYSZ;
- mode = CS_MODE_BIG_ENDIAN;
- } else {
- return -1;
- }
-
- if (cs_open(arch, mode, cs_handle) != CS_ERR_OK) {
- pr_warning_once("cs_open failed\n");
- return -1;
- }
-
- if (machine__normalized_is(machine, "x86")) {
- /*
- * In case of using capstone_init while symbol__disassemble
- * setting CS_OPT_SYNTAX_ATT depends if disassembler_style opts
- * is set via annotation args
- */
- if (disassembler_style)
- cs_option(*cs_handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_ATT);
- /*
- * Resolving address operands to symbols is implemented
- * on x86 by investigating instruction details.
- */
- cs_option(*cs_handle, CS_OPT_DETAIL, CS_OPT_ON);
- }
-
- return 0;
-}
-
-static size_t print_insn_x86(struct thread *thread, u8 cpumode, cs_insn *insn,
- int print_opts, FILE *fp)
-{
- struct addr_location al;
- size_t printed = 0;
-
- if (insn->detail && insn->detail->x86.op_count == 1) {
- cs_x86_op *op = &insn->detail->x86.operands[0];
-
- addr_location__init(&al);
- if (op->type == X86_OP_IMM &&
- thread__find_symbol(thread, cpumode, op->imm, &al)) {
- printed += fprintf(fp, "%s ", insn[0].mnemonic);
- printed += symbol__fprintf_symname_offs(al.sym, &al, fp);
- if (print_opts & PRINT_INSN_IMM_HEX)
- printed += fprintf(fp, " [%#" PRIx64 "]", op->imm);
- addr_location__exit(&al);
- return printed;
- }
- addr_location__exit(&al);
- }
-
- printed += fprintf(fp, "%s %s", insn[0].mnemonic, insn[0].op_str);
- return printed;
-}
-
static bool is64bitip(struct machine *machine, struct addr_location *al)
{
const struct dso *dso = al->map ? map__dso(al->map) : NULL;
@@ -123,32 +46,8 @@ ssize_t fprintf_insn_asm(struct machine *machine, struct thread *thread, u8 cpum
bool is64bit, const uint8_t *code, size_t code_size,
uint64_t ip, int *lenp, int print_opts, FILE *fp)
{
- size_t printed;
- cs_insn *insn;
- csh cs_handle;
- size_t count;
- int ret;
-
- /* TODO: Try to initiate capstone only once but need a proper place. */
- ret = capstone_init(machine, &cs_handle, is64bit, true);
- if (ret < 0)
- return ret;
-
- count = cs_disasm(cs_handle, code, code_size, ip, 1, &insn);
- if (count > 0) {
- if (machine__normalized_is(machine, "x86"))
- printed = print_insn_x86(thread, cpumode, &insn[0], print_opts, fp);
- else
- printed = fprintf(fp, "%s %s", insn[0].mnemonic, insn[0].op_str);
- if (lenp)
- *lenp = insn->size;
- cs_free(insn, count);
- } else {
- printed = -1;
- }
-
- cs_close(&cs_handle);
- return printed;
+ return capstone__fprintf_insn_asm(machine, thread, cpumode, is64bit, code, code_size,
+ ip, lenp, print_opts, fp);
}
size_t sample__fprintf_insn_asm(struct perf_sample *sample, struct thread *thread,
@@ -166,13 +65,3 @@ size_t sample__fprintf_insn_asm(struct perf_sample *sample, struct thread *threa
return printed;
}
-#else
-size_t sample__fprintf_insn_asm(struct perf_sample *sample __maybe_unused,
- struct thread *thread __maybe_unused,
- struct machine *machine __maybe_unused,
- FILE *fp __maybe_unused,
- struct addr_location *al __maybe_unused)
-{
- return 0;
-}
-#endif
--
2.48.0.rc2.279.g1de40edade-goog
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v1 2/5] perf llvm: Move llvm functionality into its own file
2025-01-20 17:32 [PATCH v1 0/5] Support dynamic opening of capstone and libLLVM Ian Rogers
2025-01-20 17:32 ` [PATCH v1 1/5] perf capstone: Move capstone functionality into its own file Ian Rogers
@ 2025-01-20 17:32 ` Ian Rogers
2025-01-20 17:32 ` [PATCH v1 3/5] perf capstone: Remove open_capstone_handle Ian Rogers
` (3 subsequent siblings)
5 siblings, 0 replies; 12+ messages in thread
From: Ian Rogers @ 2025-01-20 17:32 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Ian Rogers, Adrian Hunter, Kan Liang, Nathan Chancellor,
Nick Desaulniers, Bill Wendling, Justin Stitt,
Steinar H. Gunderson, Charlie Jenkins, Changbin Du,
Masami Hiramatsu (Google), James Clark, Kajol Jain, Athira Rajeev,
Li Huafei, Dmitry Vyukov, Andi Kleen, linux-kernel,
linux-perf-users, llvm
LLVM disassembly support was in disasm.c and addr2line support in
srcline.c. Move support out of these files into llvm.[ch] and remove
LLVM includes from those files. As disassembl routines can fail, make
failure the only option without HAVE_LIBLLVM_SUPPORT. For simplicity's
sake, duplicate the read_symbol utility function.
The intent with moving LLVM support into a single file is that dynamic
support, using dlopen for libllvm, can be added in later patches. This
can potentially always succeed or fail, so relying on ifdefs isn't
sufficient. Using dlopen is a useful option to minimize the perf tools
dependencies and potentially size.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/Build | 1 +
tools/perf/util/disasm.c | 262 +-----------------------------
tools/perf/util/disasm.h | 2 +
tools/perf/util/llvm.c | 326 ++++++++++++++++++++++++++++++++++++++
tools/perf/util/llvm.h | 24 +++
tools/perf/util/srcline.c | 65 ++------
tools/perf/util/srcline.h | 6 +
7 files changed, 373 insertions(+), 313 deletions(-)
create mode 100644 tools/perf/util/llvm.c
create mode 100644 tools/perf/util/llvm.h
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 9542decf9625..6fe0b5882c97 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -26,6 +26,7 @@ perf-util-y += evswitch.o
perf-util-y += find_bit.o
perf-util-y += get_current_dir_name.o
perf-util-y += levenshtein.o
+perf-util-y += llvm.o
perf-util-y += mmap.o
perf-util-y += memswap.o
perf-util-y += parse-events.o
diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c
index 0e5881189ae8..a9cc588a3006 100644
--- a/tools/perf/util/disasm.c
+++ b/tools/perf/util/disasm.c
@@ -22,6 +22,7 @@
#include "dwarf-regs.h"
#include "env.h"
#include "evsel.h"
+#include "llvm.h"
#include "map.h"
#include "maps.h"
#include "namespaces.h"
@@ -50,7 +51,6 @@ static int call__scnprintf(struct ins *ins, char *bf, size_t size,
static void ins__sort(struct arch *arch);
static int disasm_line__parse(char *line, const char **namep, char **rawp);
static int disasm_line__parse_powerpc(struct disasm_line *dl);
-static char *expand_tabs(char *line, char **storage, size_t *storage_len);
static __attribute__((constructor)) void symbol__init_regexpr(void)
{
@@ -1330,72 +1330,6 @@ static int dso__disassemble_filename(struct dso *dso, char *filename, size_t fil
return 0;
}
-#if defined(HAVE_LIBLLVM_SUPPORT)
-struct find_file_offset_data {
- u64 ip;
- u64 offset;
-};
-
-/* This will be called for each PHDR in an ELF binary */
-static int find_file_offset(u64 start, u64 len, u64 pgoff, void *arg)
-{
- struct find_file_offset_data *data = arg;
-
- if (start <= data->ip && data->ip < start + len) {
- data->offset = pgoff + data->ip - start;
- return 1;
- }
- return 0;
-}
-
-static u8 *
-read_symbol(const char *filename, struct map *map, struct symbol *sym,
- u64 *len, bool *is_64bit)
-{
- struct dso *dso = map__dso(map);
- struct nscookie nsc;
- u64 start = map__rip_2objdump(map, sym->start);
- u64 end = map__rip_2objdump(map, sym->end);
- int fd, count;
- u8 *buf = NULL;
- struct find_file_offset_data data = {
- .ip = start,
- };
-
- *is_64bit = false;
-
- nsinfo__mountns_enter(dso__nsinfo(dso), &nsc);
- fd = open(filename, O_RDONLY);
- nsinfo__mountns_exit(&nsc);
- if (fd < 0)
- return NULL;
-
- if (file__read_maps(fd, /*exe=*/true, find_file_offset, &data,
- is_64bit) == 0)
- goto err;
-
- *len = end - start;
- buf = malloc(*len);
- if (buf == NULL)
- goto err;
-
- count = pread(fd, buf, *len, data.offset);
- close(fd);
- fd = -1;
-
- if ((u64)count != *len)
- goto err;
-
- return buf;
-
-err:
- if (fd >= 0)
- close(fd);
- free(buf);
- return NULL;
-}
-#endif
-
static int symbol__disassemble_raw(char *filename, struct symbol *sym,
struct annotate_args *args)
{
@@ -1482,202 +1416,12 @@ static int symbol__disassemble_raw(char *filename, struct symbol *sym,
goto out;
}
-#ifdef HAVE_LIBLLVM_SUPPORT
-#include <llvm-c/Disassembler.h>
-#include <llvm-c/Target.h>
-#include "util/llvm-c-helpers.h"
-
-struct symbol_lookup_storage {
- u64 branch_addr;
- u64 pcrel_load_addr;
-};
-
-/*
- * Whenever LLVM wants to resolve an address into a symbol, it calls this
- * callback. We don't ever actually _return_ anything (in particular, because
- * it puts quotation marks around what we return), but we use this as a hint
- * that there is a branch or PC-relative address in the expression that we
- * should add some textual annotation for after the instruction. The caller
- * will use this information to add the actual annotation.
- */
-static const char *
-symbol_lookup_callback(void *disinfo, uint64_t value,
- uint64_t *ref_type,
- uint64_t address __maybe_unused,
- const char **ref __maybe_unused)
-{
- struct symbol_lookup_storage *storage = disinfo;
-
- if (*ref_type == LLVMDisassembler_ReferenceType_In_Branch)
- storage->branch_addr = value;
- else if (*ref_type == LLVMDisassembler_ReferenceType_In_PCrel_Load)
- storage->pcrel_load_addr = value;
- *ref_type = LLVMDisassembler_ReferenceType_InOut_None;
- return NULL;
-}
-
-static int symbol__disassemble_llvm(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);
- u8 *buf;
- u64 len;
- u64 pc;
- bool is_64bit;
- char triplet[64];
- char disasm_buf[2048];
- size_t disasm_len;
- struct disasm_line *dl;
- LLVMDisasmContextRef disasm = NULL;
- struct symbol_lookup_storage storage;
- char *line_storage = NULL;
- size_t line_storage_len = 0;
- int ret = -1;
-
- if (args->options->objdump_path)
- return -1;
-
- LLVMInitializeAllTargetInfos();
- LLVMInitializeAllTargetMCs();
- LLVMInitializeAllDisassemblers();
-
- buf = read_symbol(filename, map, sym, &len, &is_64bit);
- if (buf == NULL)
- return -1;
-
- if (arch__is(args->arch, "x86")) {
- if (is_64bit)
- scnprintf(triplet, sizeof(triplet), "x86_64-pc-linux");
- else
- scnprintf(triplet, sizeof(triplet), "i686-pc-linux");
- } else {
- scnprintf(triplet, sizeof(triplet), "%s-linux-gnu",
- args->arch->name);
- }
-
- disasm = LLVMCreateDisasm(triplet, &storage, 0, NULL,
- symbol_lookup_callback);
- if (disasm == NULL)
- goto err;
-
- if (args->options->disassembler_style &&
- !strcmp(args->options->disassembler_style, "intel"))
- LLVMSetDisasmOptions(disasm,
- LLVMDisassembler_Option_AsmPrinterVariant);
-
- /*
- * This needs to be set after AsmPrinterVariant, due to a bug in LLVM;
- * setting AsmPrinterVariant makes a new instruction printer, making it
- * forget about the PrintImmHex flag (which is applied before if both
- * are given to the same call).
- */
- LLVMSetDisasmOptions(disasm, LLVMDisassembler_Option_PrintImmHex);
-
- /* 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);
-
- pc = start;
- for (u64 offset = 0; offset < len; ) {
- unsigned int ins_len;
-
- storage.branch_addr = 0;
- storage.pcrel_load_addr = 0;
-
- ins_len = LLVMDisasmInstruction(disasm, buf + offset,
- len - offset, pc,
- disasm_buf, sizeof(disasm_buf));
- if (ins_len == 0)
- goto err;
- disasm_len = strlen(disasm_buf);
-
- if (storage.branch_addr != 0) {
- char *name = llvm_name_for_code(dso, filename,
- storage.branch_addr);
- if (name != NULL) {
- disasm_len += scnprintf(disasm_buf + disasm_len,
- sizeof(disasm_buf) -
- disasm_len,
- " <%s>", name);
- free(name);
- }
- }
- if (storage.pcrel_load_addr != 0) {
- char *name = llvm_name_for_data(dso, filename,
- storage.pcrel_load_addr);
- disasm_len += scnprintf(disasm_buf + disasm_len,
- sizeof(disasm_buf) - disasm_len,
- " # %#"PRIx64,
- storage.pcrel_load_addr);
- if (name) {
- disasm_len += scnprintf(disasm_buf + disasm_len,
- sizeof(disasm_buf) -
- disasm_len,
- " <%s>", name);
- free(name);
- }
- }
-
- args->offset = offset;
- args->line = expand_tabs(disasm_buf, &line_storage,
- &line_storage_len);
- args->line_nr = 0;
- args->fileloc = NULL;
- args->ms.sym = sym;
-
- llvm_addr2line(filename, pc, &args->fileloc,
- (unsigned int *)&args->line_nr, false, NULL);
-
- dl = disasm_line__new(args);
- if (dl == NULL)
- goto err;
-
- annotation_line__add(&dl->al, ¬es->src->source);
-
- free(args->fileloc);
- pc += ins_len;
- offset += ins_len;
- }
-
- ret = 0;
-
-err:
- LLVMDisasmDispose(disasm);
- free(buf);
- free(line_storage);
- return ret;
-}
-#else // HAVE_LIBLLVM_SUPPORT
-static int symbol__disassemble_llvm(const char *filename, struct symbol *sym,
- struct annotate_args *args __maybe_unused)
-{
- pr_debug("The LLVM disassembler isn't linked in for %s in %s\n",
- sym->name, filename);
- return -1;
-}
-#endif // HAVE_LIBLLVM_SUPPORT
-
/*
* Possibly create a new version of line with tabs expanded. Returns the
* existing or new line, storage is updated if a new line is allocated. If
* allocation fails then NULL is returned.
*/
-static char *expand_tabs(char *line, char **storage, size_t *storage_len)
+char *expand_tabs(char *line, char **storage, size_t *storage_len)
{
size_t i, src, dst, len, new_storage_len, num_tabs;
char *new_line;
@@ -1876,9 +1620,7 @@ static int annotation_options__init_disassemblers(struct annotation_options *opt
if (options->disassemblers_str == NULL) {
const char *default_disassemblers_str =
-#ifdef HAVE_LIBLLVM_SUPPORT
"llvm,"
-#endif
"capstone,"
"objdump";
diff --git a/tools/perf/util/disasm.h b/tools/perf/util/disasm.h
index c135db2416b5..2cb4e1a6bd30 100644
--- a/tools/perf/util/disasm.h
+++ b/tools/perf/util/disasm.h
@@ -128,4 +128,6 @@ int disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size,
int symbol__disassemble(struct symbol *sym, struct annotate_args *args);
+char *expand_tabs(char *line, char **storage, size_t *storage_len);
+
#endif /* __PERF_UTIL_DISASM_H */
diff --git a/tools/perf/util/llvm.c b/tools/perf/util/llvm.c
new file mode 100644
index 000000000000..ddc737194692
--- /dev/null
+++ b/tools/perf/util/llvm.c
@@ -0,0 +1,326 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "llvm.h"
+#include "annotate.h"
+#include "debug.h"
+#include "dso.h"
+#include "map.h"
+#include "namespaces.h"
+#include "srcline.h"
+#include "symbol.h"
+#include <fcntl.h>
+#include <unistd.h>
+#include <linux/zalloc.h>
+
+#ifdef HAVE_LIBLLVM_SUPPORT
+#include "llvm-c-helpers.h"
+#include <llvm-c/Disassembler.h>
+#include <llvm-c/Target.h>
+#endif
+
+#ifdef HAVE_LIBLLVM_SUPPORT
+static void free_llvm_inline_frames(struct llvm_a2l_frame *inline_frames,
+ int num_frames)
+{
+ if (inline_frames != NULL) {
+ for (int i = 0; i < num_frames; ++i) {
+ zfree(&inline_frames[i].filename);
+ zfree(&inline_frames[i].funcname);
+ }
+ zfree(&inline_frames);
+ }
+}
+#endif
+
+int llvm__addr2line(const char *dso_name __maybe_unused, u64 addr __maybe_unused,
+ char **file __maybe_unused, unsigned int *line __maybe_unused,
+ struct dso *dso __maybe_unused, bool unwind_inlines __maybe_unused,
+ struct inline_node *node __maybe_unused, struct symbol *sym __maybe_unused)
+{
+#ifdef HAVE_LIBLLVM_SUPPORT
+ struct llvm_a2l_frame *inline_frames = NULL;
+ int num_frames = llvm_addr2line(dso_name, addr, file, line,
+ node && unwind_inlines, &inline_frames);
+
+ if (num_frames == 0 || !inline_frames) {
+ /* Error, or we didn't want inlines. */
+ return num_frames;
+ }
+
+ for (int i = 0; i < num_frames; ++i) {
+ struct symbol *inline_sym =
+ new_inline_sym(dso, sym, inline_frames[i].funcname);
+ char *srcline = NULL;
+
+ if (inline_frames[i].filename) {
+ srcline =
+ srcline_from_fileline(inline_frames[i].filename,
+ inline_frames[i].line);
+ }
+ if (inline_list__append(inline_sym, srcline, node) != 0) {
+ free_llvm_inline_frames(inline_frames, num_frames);
+ return 0;
+ }
+ }
+ free_llvm_inline_frames(inline_frames, num_frames);
+
+ return num_frames;
+#else
+ return -1;
+#endif
+}
+
+void dso__free_a2l_llvm(struct dso *dso __maybe_unused)
+{
+ /* Nothing to free. */
+}
+
+
+#if defined(HAVE_LIBLLVM_SUPPORT)
+struct find_file_offset_data {
+ u64 ip;
+ u64 offset;
+};
+
+/* This will be called for each PHDR in an ELF binary */
+static int find_file_offset(u64 start, u64 len, u64 pgoff, void *arg)
+{
+ struct find_file_offset_data *data = arg;
+
+ if (start <= data->ip && data->ip < start + len) {
+ data->offset = pgoff + data->ip - start;
+ return 1;
+ }
+ return 0;
+}
+
+static u8 *
+read_symbol(const char *filename, struct map *map, struct symbol *sym,
+ u64 *len, bool *is_64bit)
+{
+ struct dso *dso = map__dso(map);
+ struct nscookie nsc;
+ u64 start = map__rip_2objdump(map, sym->start);
+ u64 end = map__rip_2objdump(map, sym->end);
+ int fd, count;
+ u8 *buf = NULL;
+ struct find_file_offset_data data = {
+ .ip = start,
+ };
+
+ *is_64bit = false;
+
+ nsinfo__mountns_enter(dso__nsinfo(dso), &nsc);
+ fd = open(filename, O_RDONLY);
+ nsinfo__mountns_exit(&nsc);
+ if (fd < 0)
+ return NULL;
+
+ if (file__read_maps(fd, /*exe=*/true, find_file_offset, &data,
+ is_64bit) == 0)
+ goto err;
+
+ *len = end - start;
+ buf = malloc(*len);
+ if (buf == NULL)
+ goto err;
+
+ count = pread(fd, buf, *len, data.offset);
+ close(fd);
+ fd = -1;
+
+ if ((u64)count != *len)
+ goto err;
+
+ return buf;
+
+err:
+ if (fd >= 0)
+ close(fd);
+ free(buf);
+ return NULL;
+}
+#endif
+
+/*
+ * Whenever LLVM wants to resolve an address into a symbol, it calls this
+ * callback. We don't ever actually _return_ anything (in particular, because
+ * it puts quotation marks around what we return), but we use this as a hint
+ * that there is a branch or PC-relative address in the expression that we
+ * should add some textual annotation for after the instruction. The caller
+ * will use this information to add the actual annotation.
+ */
+#ifdef HAVE_LIBLLVM_SUPPORT
+struct symbol_lookup_storage {
+ u64 branch_addr;
+ u64 pcrel_load_addr;
+};
+
+static const char *
+symbol_lookup_callback(void *disinfo, uint64_t value,
+ uint64_t *ref_type,
+ uint64_t address __maybe_unused,
+ const char **ref __maybe_unused)
+{
+ struct symbol_lookup_storage *storage = disinfo;
+
+ if (*ref_type == LLVMDisassembler_ReferenceType_In_Branch)
+ storage->branch_addr = value;
+ else if (*ref_type == LLVMDisassembler_ReferenceType_In_PCrel_Load)
+ storage->pcrel_load_addr = value;
+ *ref_type = LLVMDisassembler_ReferenceType_InOut_None;
+ return NULL;
+}
+#endif
+
+int symbol__disassemble_llvm(const char *filename, struct symbol *sym,
+ struct annotate_args *args __maybe_unused)
+{
+#ifdef HAVE_LIBLLVM_SUPPORT
+ 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);
+ u8 *buf;
+ u64 len;
+ u64 pc;
+ bool is_64bit;
+ char triplet[64];
+ char disasm_buf[2048];
+ size_t disasm_len;
+ struct disasm_line *dl;
+ LLVMDisasmContextRef disasm = NULL;
+ struct symbol_lookup_storage storage;
+ char *line_storage = NULL;
+ size_t line_storage_len = 0;
+ int ret = -1;
+
+ if (args->options->objdump_path)
+ return -1;
+
+ LLVMInitializeAllTargetInfos();
+ LLVMInitializeAllTargetMCs();
+ LLVMInitializeAllDisassemblers();
+
+ buf = read_symbol(filename, map, sym, &len, &is_64bit);
+ if (buf == NULL)
+ return -1;
+
+ if (arch__is(args->arch, "x86")) {
+ if (is_64bit)
+ scnprintf(triplet, sizeof(triplet), "x86_64-pc-linux");
+ else
+ scnprintf(triplet, sizeof(triplet), "i686-pc-linux");
+ } else {
+ scnprintf(triplet, sizeof(triplet), "%s-linux-gnu",
+ args->arch->name);
+ }
+
+ disasm = LLVMCreateDisasm(triplet, &storage, 0, NULL,
+ symbol_lookup_callback);
+ if (disasm == NULL)
+ goto err;
+
+ if (args->options->disassembler_style &&
+ !strcmp(args->options->disassembler_style, "intel"))
+ LLVMSetDisasmOptions(disasm,
+ LLVMDisassembler_Option_AsmPrinterVariant);
+
+ /*
+ * This needs to be set after AsmPrinterVariant, due to a bug in LLVM;
+ * setting AsmPrinterVariant makes a new instruction printer, making it
+ * forget about the PrintImmHex flag (which is applied before if both
+ * are given to the same call).
+ */
+ LLVMSetDisasmOptions(disasm, LLVMDisassembler_Option_PrintImmHex);
+
+ /* 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);
+
+ pc = start;
+ for (u64 offset = 0; offset < len; ) {
+ unsigned int ins_len;
+
+ storage.branch_addr = 0;
+ storage.pcrel_load_addr = 0;
+
+ ins_len = LLVMDisasmInstruction(disasm, buf + offset,
+ len - offset, pc,
+ disasm_buf, sizeof(disasm_buf));
+ if (ins_len == 0)
+ goto err;
+ disasm_len = strlen(disasm_buf);
+
+ if (storage.branch_addr != 0) {
+ char *name = llvm_name_for_code(dso, filename,
+ storage.branch_addr);
+ if (name != NULL) {
+ disasm_len += scnprintf(disasm_buf + disasm_len,
+ sizeof(disasm_buf) -
+ disasm_len,
+ " <%s>", name);
+ free(name);
+ }
+ }
+ if (storage.pcrel_load_addr != 0) {
+ char *name = llvm_name_for_data(dso, filename,
+ storage.pcrel_load_addr);
+ disasm_len += scnprintf(disasm_buf + disasm_len,
+ sizeof(disasm_buf) - disasm_len,
+ " # %#"PRIx64,
+ storage.pcrel_load_addr);
+ if (name) {
+ disasm_len += scnprintf(disasm_buf + disasm_len,
+ sizeof(disasm_buf) -
+ disasm_len,
+ " <%s>", name);
+ free(name);
+ }
+ }
+
+ args->offset = offset;
+ args->line = expand_tabs(disasm_buf, &line_storage,
+ &line_storage_len);
+ args->line_nr = 0;
+ args->fileloc = NULL;
+ args->ms.sym = sym;
+
+ llvm_addr2line(filename, pc, &args->fileloc,
+ (unsigned int *)&args->line_nr, false, NULL);
+
+ dl = disasm_line__new(args);
+ if (dl == NULL)
+ goto err;
+
+ annotation_line__add(&dl->al, ¬es->src->source);
+
+ free(args->fileloc);
+ pc += ins_len;
+ offset += ins_len;
+ }
+
+ ret = 0;
+
+err:
+ LLVMDisasmDispose(disasm);
+ free(buf);
+ free(line_storage);
+ return ret;
+#else // HAVE_LIBLLVM_SUPPORT
+ pr_debug("The LLVM disassembler isn't linked in for %s in %s\n",
+ sym->name, filename);
+ return -1;
+#endif
+}
diff --git a/tools/perf/util/llvm.h b/tools/perf/util/llvm.h
new file mode 100644
index 000000000000..8aa19bb6b068
--- /dev/null
+++ b/tools/perf/util/llvm.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __PERF_LLVM_H
+#define __PERF_LLVM_H
+
+#include <stdbool.h>
+#include <linux/types.h>
+
+struct annotate_args;
+struct dso;
+struct inline_node;
+struct symbol;
+
+int llvm__addr2line(const char *dso_name, u64 addr,
+ char **file, unsigned int *line, struct dso *dso,
+ bool unwind_inlines, struct inline_node *node,
+ struct symbol *sym);
+
+
+void dso__free_a2l_llvm(struct dso *dso);
+
+int symbol__disassemble_llvm(const char *filename, struct symbol *sym,
+ struct annotate_args *args);
+
+#endif /* __PERF_LLVM_H */
diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c
index f32d0d4f4bc9..26fd55455efd 100644
--- a/tools/perf/util/srcline.c
+++ b/tools/perf/util/srcline.c
@@ -17,9 +17,7 @@
#include "util/debug.h"
#include "util/callchain.h"
#include "util/symbol_conf.h"
-#ifdef HAVE_LIBLLVM_SUPPORT
-#include "util/llvm-c-helpers.h"
-#endif
+#include "llvm.h"
#include "srcline.h"
#include "string2.h"
#include "symbol.h"
@@ -49,8 +47,7 @@ static const char *srcline_dso_name(struct dso *dso)
return dso_name;
}
-static int inline_list__append(struct symbol *symbol, char *srcline,
- struct inline_node *node)
+int inline_list__append(struct symbol *symbol, char *srcline, struct inline_node *node)
{
struct inline_list *ilist;
@@ -77,7 +74,7 @@ static const char *gnu_basename(const char *path)
return base ? base + 1 : path;
}
-static char *srcline_from_fileline(const char *file, unsigned int line)
+char *srcline_from_fileline(const char *file, unsigned int line)
{
char *srcline;
@@ -93,9 +90,9 @@ static char *srcline_from_fileline(const char *file, unsigned int line)
return srcline;
}
-static struct symbol *new_inline_sym(struct dso *dso,
- struct symbol *base_sym,
- const char *funcname)
+struct symbol *new_inline_sym(struct dso *dso,
+ struct symbol *base_sym,
+ const char *funcname)
{
struct symbol *inline_sym;
char *demangled = NULL;
@@ -135,58 +132,20 @@ static struct symbol *new_inline_sym(struct dso *dso,
#define MAX_INLINE_NEST 1024
#ifdef HAVE_LIBLLVM_SUPPORT
-
-static void free_llvm_inline_frames(struct llvm_a2l_frame *inline_frames,
- int num_frames)
-{
- if (inline_frames != NULL) {
- for (int i = 0; i < num_frames; ++i) {
- zfree(&inline_frames[i].filename);
- zfree(&inline_frames[i].funcname);
- }
- zfree(&inline_frames);
- }
-}
+#include "llvm.h"
static int addr2line(const char *dso_name, u64 addr,
char **file, unsigned int *line, struct dso *dso,
- bool unwind_inlines, struct inline_node *node,
- struct symbol *sym)
+ bool unwind_inlines, struct inline_node *node,
+ struct symbol *sym)
{
- struct llvm_a2l_frame *inline_frames = NULL;
- int num_frames = llvm_addr2line(dso_name, addr, file, line,
- node && unwind_inlines, &inline_frames);
-
- if (num_frames == 0 || !inline_frames) {
- /* Error, or we didn't want inlines. */
- return num_frames;
- }
-
- for (int i = 0; i < num_frames; ++i) {
- struct symbol *inline_sym =
- new_inline_sym(dso, sym, inline_frames[i].funcname);
- char *srcline = NULL;
-
- if (inline_frames[i].filename) {
- srcline =
- srcline_from_fileline(inline_frames[i].filename,
- inline_frames[i].line);
- }
- if (inline_list__append(inline_sym, srcline, node) != 0) {
- free_llvm_inline_frames(inline_frames, num_frames);
- return 0;
- }
- }
- free_llvm_inline_frames(inline_frames, num_frames);
-
- return num_frames;
+ return llvm__addr2line(dso_name, addr, file, line, dso, unwind_inlines, node, sym);
}
-void dso__free_a2l(struct dso *dso __maybe_unused)
+void dso__free_a2l(struct dso *dso)
{
- /* Nothing to free. */
+ dso__free_a2l_llvm(dso);
}
-
#elif defined(HAVE_LIBBFD_SUPPORT)
/*
diff --git a/tools/perf/util/srcline.h b/tools/perf/util/srcline.h
index 75010d39ea28..80c20169e250 100644
--- a/tools/perf/util/srcline.h
+++ b/tools/perf/util/srcline.h
@@ -55,4 +55,10 @@ struct inline_node *inlines__tree_find(struct rb_root_cached *tree, u64 addr);
/* delete all nodes within the tree of inline_node s */
void inlines__tree_delete(struct rb_root_cached *tree);
+int inline_list__append(struct symbol *symbol, char *srcline, struct inline_node *node);
+char *srcline_from_fileline(const char *file, unsigned int line);
+struct symbol *new_inline_sym(struct dso *dso,
+ struct symbol *base_sym,
+ const char *funcname);
+
#endif /* PERF_SRCLINE_H */
--
2.48.0.rc2.279.g1de40edade-goog
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v1 3/5] perf capstone: Remove open_capstone_handle
2025-01-20 17:32 [PATCH v1 0/5] Support dynamic opening of capstone and libLLVM Ian Rogers
2025-01-20 17:32 ` [PATCH v1 1/5] perf capstone: Move capstone functionality into its own file Ian Rogers
2025-01-20 17:32 ` [PATCH v1 2/5] perf llvm: Move llvm " Ian Rogers
@ 2025-01-20 17:32 ` Ian Rogers
2025-01-20 17:32 ` [PATCH v1 4/5] perf capstone: Support for dlopen-ing libcapstone.so Ian Rogers
` (2 subsequent siblings)
5 siblings, 0 replies; 12+ messages in thread
From: Ian Rogers @ 2025-01-20 17:32 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Ian Rogers, Adrian Hunter, Kan Liang, Nathan Chancellor,
Nick Desaulniers, Bill Wendling, Justin Stitt,
Steinar H. Gunderson, Charlie Jenkins, Changbin Du,
Masami Hiramatsu (Google), James Clark, Kajol Jain, Athira Rajeev,
Li Huafei, Dmitry Vyukov, Andi Kleen, linux-kernel,
linux-perf-users, llvm
open_capstone_handle is similar to capstone_init and used only by
symbol__disassemble_capstone. symbol__disassemble_capstone_powerpc
already uses capstone_init, transition symbol__disassemble_capstone
and eliminate open_capstone_handle.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/capstone.c | 34 ++++++----------------------------
1 file changed, 6 insertions(+), 28 deletions(-)
diff --git a/tools/perf/util/capstone.c b/tools/perf/util/capstone.c
index c0a6d94ebc18..c9845e4d8781 100644
--- a/tools/perf/util/capstone.c
+++ b/tools/perf/util/capstone.c
@@ -137,33 +137,6 @@ ssize_t capstone__fprintf_insn_asm(struct machine *machine __maybe_unused,
#endif
}
-#ifdef HAVE_LIBCAPSTONE_SUPPORT
-static int open_capstone_handle(struct annotate_args *args, bool is_64bit, csh *handle)
-{
- struct annotation_options *opt = args->options;
- cs_mode mode = is_64bit ? CS_MODE_64 : CS_MODE_32;
-
- /* TODO: support more architectures */
- if (!arch__is(args->arch, "x86"))
- return -1;
-
- if (cs_open(CS_ARCH_X86, mode, handle) != CS_ERR_OK)
- return -1;
-
- if (!opt->disassembler_style ||
- !strcmp(opt->disassembler_style, "att"))
- cs_option(*handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_ATT);
-
- /*
- * Resolving address operands to symbols is implemented
- * on x86 by investigating instruction details.
- */
- cs_option(*handle, CS_OPT_DETAIL, CS_OPT_ON);
-
- return 0;
-}
-#endif
-
#ifdef HAVE_LIBCAPSTONE_SUPPORT
static void print_capstone_detail(cs_insn *insn, char *buf, size_t len,
struct annotate_args *args, u64 addr)
@@ -309,6 +282,7 @@ int symbol__disassemble_capstone(const char *filename __maybe_unused,
cs_insn *insn = NULL;
char disasm_buf[512];
struct disasm_line *dl;
+ bool disassembler_style = false;
if (args->options->objdump_path)
return -1;
@@ -333,7 +307,11 @@ int symbol__disassemble_capstone(const char *filename __maybe_unused,
annotation_line__add(&dl->al, ¬es->src->source);
- if (open_capstone_handle(args, is_64bit, &handle) < 0)
+ if (!args->options->disassembler_style ||
+ !strcmp(args->options->disassembler_style, "att"))
+ disassembler_style = true;
+
+ if (capstone_init(maps__machine(args->ms.maps), &handle, is_64bit, disassembler_style) < 0)
goto err;
needs_cs_close = true;
--
2.48.0.rc2.279.g1de40edade-goog
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v1 4/5] perf capstone: Support for dlopen-ing libcapstone.so
2025-01-20 17:32 [PATCH v1 0/5] Support dynamic opening of capstone and libLLVM Ian Rogers
` (2 preceding siblings ...)
2025-01-20 17:32 ` [PATCH v1 3/5] perf capstone: Remove open_capstone_handle Ian Rogers
@ 2025-01-20 17:32 ` Ian Rogers
2025-01-22 22:27 ` Namhyung Kim
2025-01-20 17:32 ` [PATCH v1 5/5] perf llvm: Support for dlopen-ing libLLVM.so Ian Rogers
2025-01-20 18:01 ` [PATCH v1 0/5] Support dynamic opening of capstone and libLLVM Ian Rogers
5 siblings, 1 reply; 12+ messages in thread
From: Ian Rogers @ 2025-01-20 17:32 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Ian Rogers, Adrian Hunter, Kan Liang, Nathan Chancellor,
Nick Desaulniers, Bill Wendling, Justin Stitt,
Steinar H. Gunderson, Charlie Jenkins, Changbin Du,
Masami Hiramatsu (Google), James Clark, Kajol Jain, Athira Rajeev,
Li Huafei, Dmitry Vyukov, Andi Kleen, linux-kernel,
linux-perf-users, llvm
If perf wasn't built against libcapstone, no HAVE_LIBCAPSTONE_SUPPORT,
support dlopen-ing libcapstone.so and then calling the necessary
functions by looking them up using dlsym. Reverse engineer the types
in the API using pahole, adding only what's used in the perf code or
necessary for the sake of struct size and alignment.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/capstone.c | 287 ++++++++++++++++++++++++++++++++-----
1 file changed, 248 insertions(+), 39 deletions(-)
diff --git a/tools/perf/util/capstone.c b/tools/perf/util/capstone.c
index c9845e4d8781..8d65c7a55a8b 100644
--- a/tools/perf/util/capstone.c
+++ b/tools/perf/util/capstone.c
@@ -11,19 +11,249 @@
#include "print_insn.h"
#include "symbol.h"
#include "thread.h"
+#include <dlfcn.h>
#include <fcntl.h>
+#include <inttypes.h>
#include <string.h>
#ifdef HAVE_LIBCAPSTONE_SUPPORT
#include <capstone/capstone.h>
+#else
+typedef size_t csh;
+enum cs_arch {
+ CS_ARCH_ARM = 0,
+ CS_ARCH_ARM64 = 1,
+ CS_ARCH_X86 = 3,
+ CS_ARCH_SYSZ = 6,
+};
+enum cs_mode {
+ CS_MODE_ARM = 0,
+ CS_MODE_32 = 1 << 2,
+ CS_MODE_64 = 1 << 3,
+ CS_MODE_V8 = 1 << 6,
+ CS_MODE_BIG_ENDIAN = 1 << 31,
+};
+enum cs_opt_type {
+ CS_OPT_SYNTAX = 1,
+ CS_OPT_DETAIL = 2,
+};
+enum cs_opt_value {
+ CS_OPT_SYNTAX_ATT = 2,
+ CS_OPT_ON = 3,
+};
+enum cs_err {
+ CS_ERR_OK = 0,
+ CS_ERR_HANDLE = 3,
+};
+enum x86_op_type {
+ X86_OP_IMM = 2,
+ X86_OP_MEM = 3,
+};
+enum x86_reg {
+ X86_REG_RIP = 41,
+};
+typedef int32_t x86_avx_bcast;
+struct x86_op_mem {
+ enum x86_reg segment;
+ enum x86_reg base;
+ enum x86_reg index;
+ int scale;
+ int64_t disp;
+};
+
+struct cs_x86_op {
+ enum x86_op_type type;
+ union {
+ enum x86_reg reg;
+ int64_t imm;
+ struct x86_op_mem mem;
+ };
+ uint8_t size;
+ uint8_t access;
+ x86_avx_bcast avx_bcast;
+ bool avx_zero_opmask;
+};
+struct cs_x86_encoding {
+ uint8_t modrm_offset;
+ uint8_t disp_offset;
+ uint8_t disp_size;
+ uint8_t imm_offset;
+ uint8_t imm_size;
+};
+typedef int32_t x86_xop_cc;
+typedef int32_t x86_sse_cc;
+typedef int32_t x86_avx_cc;
+typedef int32_t x86_avx_rm;
+struct cs_x86 {
+ uint8_t prefix[4];
+ uint8_t opcode[4];
+ uint8_t rex;
+ uint8_t addr_size;
+ uint8_t modrm;
+ uint8_t sib;
+ int64_t disp;
+ enum x86_reg sib_index;
+ int8_t sib_scale;
+ enum x86_reg sib_base;
+ x86_xop_cc xop_cc;
+ x86_sse_cc sse_cc;
+ x86_avx_cc avx_cc;
+ bool avx_sae;
+ x86_avx_rm avx_rm;
+ union {
+ uint64_t eflags;
+ uint64_t fpu_flags;
+ };
+ uint8_t op_count;
+ struct cs_x86_op operands[8];
+ struct cs_x86_encoding encoding;
+};
+struct cs_detail {
+ uint16_t regs_read[12];
+ uint8_t regs_read_count;
+ uint16_t regs_write[20];
+ uint8_t regs_write_count;
+ uint8_t groups[8];
+ uint8_t groups_count;
+
+ union {
+ struct cs_x86 x86;
+ };
+};
+struct cs_insn {
+ unsigned int id;
+ uint64_t address;
+ uint16_t size;
+ uint8_t bytes[16];
+ char mnemonic[32];
+ char op_str[160];
+ struct cs_detail *detail;
+};
+#endif
+
+#ifndef HAVE_LIBCAPSTONE_SUPPORT
+static void *perf_cs_dll_handle(void)
+{
+ static bool dll_handle_init;
+ static void *dll_handle;
+
+ if (!dll_handle_init) {
+ dll_handle_init = true;
+ dll_handle = dlopen("libcapstone.so", RTLD_LAZY);
+ if (!dll_handle)
+ pr_debug("dlopen failed for libcapstone.so\n");
+ }
+ return dll_handle;
+}
+#endif
+
+static enum cs_err perf_cs_open(enum cs_arch arch, enum cs_mode mode, csh *handle)
+{
+#ifdef HAVE_LIBCAPSTONE_SUPPORT
+ return cs_open(arch, mode, handle);
+#else
+ static bool fn_init;
+ static enum cs_err (*fn)(enum cs_arch arch, enum cs_mode mode, csh *handle);
+
+ if (!fn_init) {
+ fn = dlsym(perf_cs_dll_handle(), "cs_open");
+ if (!fn)
+ pr_debug("dlsym failed for cs_open\n");
+ fn_init = true;
+ }
+ if (!fn)
+ return CS_ERR_HANDLE;
+ return fn(arch, mode, handle);
+#endif
+}
+
+static enum cs_err perf_cs_option(csh handle, enum cs_opt_type type, size_t value)
+{
+#ifdef HAVE_LIBCAPSTONE_SUPPORT
+ return cs_option(handle, type, value);
+#else
+ static bool fn_init;
+ static enum cs_err (*fn)(csh handle, enum cs_opt_type type, size_t value);
+
+ if (!fn_init) {
+ fn = dlsym(perf_cs_dll_handle(), "cs_option");
+ if (!fn)
+ pr_debug("dlsym failed for cs_option\n");
+ fn_init = true;
+ }
+ if (!fn)
+ return CS_ERR_HANDLE;
+ return fn(handle, type, value);
+#endif
+}
+
+static size_t perf_cs_disasm(csh handle, const uint8_t *code, size_t code_size,
+ uint64_t address, size_t count, struct cs_insn **insn)
+{
+#ifdef HAVE_LIBCAPSTONE_SUPPORT
+ return cs_disasm(handle, code, code_size, address, count, insn);
+#else
+ static bool fn_init;
+ static enum cs_err (*fn)(csh handle, const uint8_t *code, size_t code_size,
+ uint64_t address, size_t count, struct cs_insn **insn);
+
+ if (!fn_init) {
+ fn = dlsym(perf_cs_dll_handle(), "cs_disasm");
+ if (!fn)
+ pr_debug("dlsym failed for cs_disasm\n");
+ fn_init = true;
+ }
+ if (!fn)
+ return CS_ERR_HANDLE;
+ return fn(handle, code, code_size, address, count, insn);
#endif
+}
+static void perf_cs_free(struct cs_insn *insn, size_t count)
+{
#ifdef HAVE_LIBCAPSTONE_SUPPORT
+ cs_free(insn, count);
+#else
+ static bool fn_init;
+ static void (*fn)(struct cs_insn *insn, size_t count);
+
+ if (!fn_init) {
+ fn = dlsym(perf_cs_dll_handle(), "cs_free");
+ if (!fn)
+ pr_debug("dlsym failed for cs_free\n");
+ fn_init = true;
+ }
+ if (!fn)
+ return;
+ fn(insn, count);
+#endif
+}
+
+static enum cs_err perf_cs_close(csh *handle)
+{
+#ifdef HAVE_LIBCAPSTONE_SUPPORT
+ return cs_close(handle);
+#else
+ static bool fn_init;
+ static enum cs_err (*fn)(csh *handle);
+
+ if (!fn_init) {
+ fn = dlsym(perf_cs_dll_handle(), "cs_close");
+ if (!fn)
+ pr_debug("dlsym failed for cs_close\n");
+ fn_init = true;
+ }
+ if (!fn)
+ return CS_ERR_HANDLE;
+ return fn(handle);
+#endif
+}
+
static int capstone_init(struct machine *machine, csh *cs_handle, bool is64,
bool disassembler_style)
{
- cs_arch arch;
- cs_mode mode;
+ enum cs_arch arch;
+ enum cs_mode mode;
if (machine__is(machine, "x86_64") && is64) {
arch = CS_ARCH_X86;
@@ -44,7 +274,7 @@ static int capstone_init(struct machine *machine, csh *cs_handle, bool is64,
return -1;
}
- if (cs_open(arch, mode, cs_handle) != CS_ERR_OK) {
+ if (perf_cs_open(arch, mode, cs_handle) != CS_ERR_OK) {
pr_warning_once("cs_open failed\n");
return -1;
}
@@ -56,27 +286,25 @@ static int capstone_init(struct machine *machine, csh *cs_handle, bool is64,
* is set via annotation args
*/
if (disassembler_style)
- cs_option(*cs_handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_ATT);
+ perf_cs_option(*cs_handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_ATT);
/*
* Resolving address operands to symbols is implemented
* on x86 by investigating instruction details.
*/
- cs_option(*cs_handle, CS_OPT_DETAIL, CS_OPT_ON);
+ perf_cs_option(*cs_handle, CS_OPT_DETAIL, CS_OPT_ON);
}
return 0;
}
-#endif
-#ifdef HAVE_LIBCAPSTONE_SUPPORT
-static size_t print_insn_x86(struct thread *thread, u8 cpumode, cs_insn *insn,
+static size_t print_insn_x86(struct thread *thread, u8 cpumode, struct cs_insn *insn,
int print_opts, FILE *fp)
{
struct addr_location al;
size_t printed = 0;
if (insn->detail && insn->detail->x86.op_count == 1) {
- cs_x86_op *op = &insn->detail->x86.operands[0];
+ struct cs_x86_op *op = &insn->detail->x86.operands[0];
addr_location__init(&al);
if (op->type == X86_OP_IMM &&
@@ -94,7 +322,6 @@ static size_t print_insn_x86(struct thread *thread, u8 cpumode, cs_insn *insn,
printed += fprintf(fp, "%s %s", insn[0].mnemonic, insn[0].op_str);
return printed;
}
-#endif
ssize_t capstone__fprintf_insn_asm(struct machine *machine __maybe_unused,
@@ -105,9 +332,8 @@ ssize_t capstone__fprintf_insn_asm(struct machine *machine __maybe_unused,
uint64_t ip __maybe_unused, int *lenp __maybe_unused,
int print_opts __maybe_unused, FILE *fp __maybe_unused)
{
-#ifdef HAVE_LIBCAPSTONE_SUPPORT
size_t printed;
- cs_insn *insn;
+ struct cs_insn *insn;
csh cs_handle;
size_t count;
int ret;
@@ -117,7 +343,7 @@ ssize_t capstone__fprintf_insn_asm(struct machine *machine __maybe_unused,
if (ret < 0)
return ret;
- count = cs_disasm(cs_handle, code, code_size, ip, 1, &insn);
+ count = perf_cs_disasm(cs_handle, code, code_size, ip, 1, &insn);
if (count > 0) {
if (machine__normalized_is(machine, "x86"))
printed = print_insn_x86(thread, cpumode, &insn[0], print_opts, fp);
@@ -125,20 +351,16 @@ ssize_t capstone__fprintf_insn_asm(struct machine *machine __maybe_unused,
printed = fprintf(fp, "%s %s", insn[0].mnemonic, insn[0].op_str);
if (lenp)
*lenp = insn->size;
- cs_free(insn, count);
+ perf_cs_free(insn, count);
} else {
printed = -1;
}
- cs_close(&cs_handle);
+ perf_cs_close(&cs_handle);
return printed;
-#else
- return -1;
-#endif
}
-#ifdef HAVE_LIBCAPSTONE_SUPPORT
-static void print_capstone_detail(cs_insn *insn, char *buf, size_t len,
+static void print_capstone_detail(struct cs_insn *insn, char *buf, size_t len,
struct annotate_args *args, u64 addr)
{
int i;
@@ -153,7 +375,7 @@ static void print_capstone_detail(cs_insn *insn, char *buf, size_t len,
return;
for (i = 0; i < insn->detail->x86.op_count; i++) {
- cs_x86_op *op = &insn->detail->x86.operands[i];
+ struct cs_x86_op *op = &insn->detail->x86.operands[i];
u64 orig_addr;
if (op->type != X86_OP_MEM)
@@ -194,9 +416,7 @@ static void print_capstone_detail(cs_insn *insn, char *buf, size_t len,
break;
}
}
-#endif
-#ifdef HAVE_LIBCAPSTONE_SUPPORT
struct find_file_offset_data {
u64 ip;
u64 offset;
@@ -213,9 +433,7 @@ static int find_file_offset(u64 start, u64 len, u64 pgoff, void *arg)
}
return 0;
}
-#endif
-#ifdef HAVE_LIBCAPSTONE_SUPPORT
static u8 *
read_symbol(const char *filename, struct map *map, struct symbol *sym,
u64 *len, bool *is_64bit)
@@ -262,13 +480,11 @@ read_symbol(const char *filename, struct map *map, struct symbol *sym,
free(buf);
return NULL;
}
-#endif
int symbol__disassemble_capstone(const char *filename __maybe_unused,
struct symbol *sym __maybe_unused,
struct annotate_args *args __maybe_unused)
{
-#ifdef HAVE_LIBCAPSTONE_SUPPORT
struct annotation *notes = symbol__annotation(sym);
struct map *map = args->ms.map;
u64 start = map__rip_2objdump(map, sym->start);
@@ -279,7 +495,7 @@ int symbol__disassemble_capstone(const char *filename __maybe_unused,
bool needs_cs_close = false;
u8 *buf = NULL;
csh handle;
- cs_insn *insn = NULL;
+ struct cs_insn *insn = NULL;
char disasm_buf[512];
struct disasm_line *dl;
bool disassembler_style = false;
@@ -316,7 +532,7 @@ int symbol__disassemble_capstone(const char *filename __maybe_unused,
needs_cs_close = true;
- free_count = count = cs_disasm(handle, buf, len, start, len, &insn);
+ free_count = count = perf_cs_disasm(handle, buf, len, start, len, &insn);
for (i = 0, offset = 0; i < count; i++) {
int printed;
@@ -355,9 +571,9 @@ int symbol__disassemble_capstone(const char *filename __maybe_unused,
out:
if (needs_cs_close) {
- cs_close(&handle);
+ perf_cs_close(&handle);
if (free_count > 0)
- cs_free(insn, free_count);
+ perf_cs_free(insn, free_count);
}
free(buf);
return count < 0 ? count : 0;
@@ -377,16 +593,12 @@ int symbol__disassemble_capstone(const char *filename __maybe_unused,
}
count = -1;
goto out;
-#else
- return -1;
-#endif
}
int symbol__disassemble_capstone_powerpc(const char *filename __maybe_unused,
struct symbol *sym __maybe_unused,
struct annotate_args *args __maybe_unused)
{
-#ifdef HAVE_LIBCAPSTONE_SUPPORT
struct annotation *notes = symbol__annotation(sym);
struct map *map = args->ms.map;
struct dso *dso = map__dso(map);
@@ -499,7 +711,7 @@ int symbol__disassemble_capstone_powerpc(const char *filename __maybe_unused,
out:
if (needs_cs_close)
- cs_close(&handle);
+ perf_cs_close(&handle);
free(buf);
return count < 0 ? count : 0;
@@ -508,7 +720,4 @@ int symbol__disassemble_capstone_powerpc(const char *filename __maybe_unused,
close(fd);
count = -1;
goto out;
-#else
- return -1;
-#endif
}
--
2.48.0.rc2.279.g1de40edade-goog
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v1 5/5] perf llvm: Support for dlopen-ing libLLVM.so
2025-01-20 17:32 [PATCH v1 0/5] Support dynamic opening of capstone and libLLVM Ian Rogers
` (3 preceding siblings ...)
2025-01-20 17:32 ` [PATCH v1 4/5] perf capstone: Support for dlopen-ing libcapstone.so Ian Rogers
@ 2025-01-20 17:32 ` Ian Rogers
2025-01-20 18:01 ` [PATCH v1 0/5] Support dynamic opening of capstone and libLLVM Ian Rogers
5 siblings, 0 replies; 12+ messages in thread
From: Ian Rogers @ 2025-01-20 17:32 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Ian Rogers, Adrian Hunter, Kan Liang, Nathan Chancellor,
Nick Desaulniers, Bill Wendling, Justin Stitt,
Steinar H. Gunderson, Charlie Jenkins, Changbin Du,
Masami Hiramatsu (Google), James Clark, Kajol Jain, Athira Rajeev,
Li Huafei, Dmitry Vyukov, Andi Kleen, linux-kernel,
linux-perf-users, llvm
If perf wasn't built against libLLVM, no HAVE_LIBLLVM_SUPPORT, support
dlopen-ing libLLVM.so and then calling the necessary functions by
looking them up using dlsym. As the C++ code in llvm-c-helpers used
for addr2line is problematic to call using dlsym, build that C++ code
against libLLVM.so as a separate shared object, and support dynamic
loading of it. This build option is enabled with LIBLLVM_DYNAMIC=1
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/Makefile.config | 13 ++
tools/perf/Makefile.perf | 23 ++-
tools/perf/tests/make | 2 +
tools/perf/util/Build | 2 +-
tools/perf/util/llvm-c-helpers.cpp | 113 +++++++++++-
tools/perf/util/llvm.c | 271 +++++++++++++++++++++++++----
6 files changed, 386 insertions(+), 38 deletions(-)
diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config
index a148ca9efca9..4042cc7f8cb3 100644
--- a/tools/perf/Makefile.config
+++ b/tools/perf/Makefile.config
@@ -963,6 +963,19 @@ ifndef NO_LIBLLVM
NO_LIBLLVM := 1
endif
endif
+ifdef LIBLLVM_DYNAMIC
+ ifndef NO_LIBLLVM
+ $(error LIBLLVM_DYNAMIC should be used with NO_LIBLLVM)
+ endif
+ $(call feature_check,llvm-perf)
+ ifneq ($(feature-llvm-perf), 1)
+ $(warning LIBLLVM_DYNAMIC requires libLLVM.so which wasn't feature detected)
+ endif
+ CFLAGS += -DHAVE_LIBLLVM_DYNAMIC
+ CFLAGS += $(shell $(LLVM_CONFIG) --cflags)
+ CXXFLAGS += -DHAVE_LIBLLVM_DYNAMIC
+ CXXFLAGS += $(shell $(LLVM_CONFIG) --cxxflags)
+endif
ifndef NO_DEMANGLE
$(call feature_check,cxa-demangle)
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index 55d6ce9ea52f..eae77f6af59d 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -456,6 +456,12 @@ ifndef NO_JVMTI
PROGRAMS += $(OUTPUT)$(LIBJVMTI)
endif
+LIBPERF_LLVM = libperf-llvm.so
+
+ifdef LIBLLVM_DYNAMIC
+PROGRAMS += $(OUTPUT)$(LIBPERF_LLVM)
+endif
+
DLFILTERS := dlfilter-test-api-v0.so dlfilter-test-api-v2.so dlfilter-show-cycles.so
DLFILTERS := $(patsubst %,$(OUTPUT)dlfilters/%,$(DLFILTERS))
@@ -1019,6 +1025,16 @@ $(LIBSYMBOL)-clean:
$(call QUIET_CLEAN, libsymbol)
$(Q)$(RM) -r -- $(LIBSYMBOL_OUTPUT)
+ifdef LIBLLVM_DYNAMIC
+LIBPERF_LLVM_CXXFLAGS := $(call filter-out,-DHAVE_LIBLLVM_DYNAMIC,$(CXXFLAGS)) -DHAVE_LIBLLVM_SUPPORT
+LIBPERF_LLVM_LIBS = -L$(shell $(LLVM_CONFIG) --libdir) $(LIBLLVM) -lstdc++
+
+$(OUTPUT)$(LIBPERF_LLVM): util/llvm-c-helpers.cpp
+ $(QUIET_LINK)$(CXX) $(LIBPERF_LLVM_CXXFLAGS) $(LIBPERF_LLVM_LIBS) -shared -o $@ $<
+
+$(OUTPUT)perf: $(OUTPUT)$(LIBPERF_LLVM)
+endif
+
help:
@echo 'Perf make targets:'
@echo ' doc - make *all* documentation (see below)'
@@ -1120,6 +1136,11 @@ ifndef NO_JVMTI
$(call QUIET_INSTALL, $(LIBJVMTI)) \
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(libdir_SQ)'; \
$(INSTALL) $(OUTPUT)$(LIBJVMTI) '$(DESTDIR_SQ)$(libdir_SQ)';
+endif
+ifdef LIBLLVM_DYNAMIC
+ $(call QUIET_INSTALL, $(LIBPERF_LLVM)) \
+ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(libdir_SQ)'; \
+ $(INSTALL) $(OUTPUT)$(LIBPERF_LLVM) '$(DESTDIR_SQ)$(libdir_SQ)';
endif
$(call QUIET_INSTALL, libexec) \
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
@@ -1301,7 +1322,7 @@ clean:: $(LIBAPI)-clean $(LIBBPF)-clean $(LIBSUBCMD)-clean $(LIBSYMBOL)-clean $(
-name '\.*.cmd' -delete -o -name '\.*.d' -delete -o -name '*.shellcheck_log' -delete
$(Q)$(RM) $(OUTPUT).config-detected
$(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf perf-read-vdso32 \
- perf-read-vdsox32 $(OUTPUT)$(LIBJVMTI).so
+ perf-read-vdsox32 $(OUTPUT)$(LIBJVMTI) $(OUTPUT)$(LIBPERF_LLVM)
$(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo \
$(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE \
$(OUTPUT)FEATURE-DUMP $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex* \
diff --git a/tools/perf/tests/make b/tools/perf/tests/make
index 0ee94caf9ec1..44d76eacce49 100644
--- a/tools/perf/tests/make
+++ b/tools/perf/tests/make
@@ -93,6 +93,7 @@ make_libbpf_dynamic := LIBBPF_DYNAMIC=1
make_no_libbpf_DEBUG := NO_LIBBPF=1 DEBUG=1
make_no_libcrypto := NO_LIBCRYPTO=1
make_no_libllvm := NO_LIBLLVM=1
+make_libllvm_dynamic := NO_LIBLLVM=1 LIBLLVM_DYNAMIC=1
make_with_babeltrace:= LIBBABELTRACE=1
make_with_coresight := CORESIGHT=1
make_no_sdt := NO_SDT=1
@@ -162,6 +163,7 @@ run += make_no_libbpf
run += make_no_libbpf_DEBUG
run += make_no_libcrypto
run += make_no_libllvm
+run += make_libllvm_dynamic
run += make_no_sdt
run += make_no_syscall_tbl
run += make_with_babeltrace
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 6fe0b5882c97..eb00c599e179 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -27,6 +27,7 @@ perf-util-y += find_bit.o
perf-util-y += get_current_dir_name.o
perf-util-y += levenshtein.o
perf-util-y += llvm.o
+perf-util-y += llvm-c-helpers.o
perf-util-y += mmap.o
perf-util-y += memswap.o
perf-util-y += parse-events.o
@@ -239,7 +240,6 @@ perf-util-$(CONFIG_CXX_DEMANGLE) += demangle-cxx.o
perf-util-y += demangle-ocaml.o
perf-util-y += demangle-java.o
perf-util-y += demangle-rust.o
-perf-util-$(CONFIG_LIBLLVM) += llvm-c-helpers.o
ifdef CONFIG_JITDUMP
perf-util-$(CONFIG_LIBELF) += jitdump.o
diff --git a/tools/perf/util/llvm-c-helpers.cpp b/tools/perf/util/llvm-c-helpers.cpp
index 004081bd12c9..5a6f76e6b705 100644
--- a/tools/perf/util/llvm-c-helpers.cpp
+++ b/tools/perf/util/llvm-c-helpers.cpp
@@ -5,17 +5,23 @@
* macros (e.g. noinline) that conflict with compiler builtins used
* by LLVM.
*/
+#ifdef HAVE_LIBLLVM_SUPPORT
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter" /* Needed for LLVM <= 15 */
#include <llvm/DebugInfo/Symbolize/Symbolize.h>
#include <llvm/Support/TargetSelect.h>
#pragma GCC diagnostic pop
+#endif
+#if !defined(HAVE_LIBLLVM_SUPPORT) || defined(HAVE_LIBLLVM_DYNAMIC)
+#include <dlfcn.h>
+#endif
#include <inttypes.h>
#include <stdio.h>
#include <sys/types.h>
#include <linux/compiler.h>
extern "C" {
+#include "debug.h"
#include <linux/zalloc.h>
}
#include "llvm-c-helpers.h"
@@ -23,14 +29,33 @@ extern "C" {
extern "C"
char *dso__demangle_sym(struct dso *dso, int kmodule, const char *elf_name);
+#ifdef HAVE_LIBLLVM_SUPPORT
using namespace llvm;
using llvm::symbolize::LLVMSymbolizer;
+#endif
+
+#if !defined(HAVE_LIBLLVM_SUPPORT) && defined(HAVE_LIBLLVM_DYNAMIC)
+static void *perf_llvm_c_helpers_dll_handle(void)
+{
+ static bool dll_handle_init;
+ static void *dll_handle;
+
+ if (!dll_handle_init) {
+ dll_handle_init = true;
+ dll_handle = dlopen("libperf-llvm.so", RTLD_LAZY);
+ if (!dll_handle)
+ pr_debug("dlopen failed for libperf-llvm.so\n");
+ }
+ return dll_handle;
+}
+#endif
/*
* Allocate a static LLVMSymbolizer, which will live to the end of the program.
* Unlike the bfd paths, LLVMSymbolizer has its own cache, so we do not need
* to store anything in the dso struct.
*/
+#if defined(HAVE_LIBLLVM_SUPPORT) && !defined(HAVE_LIBLLVM_DYNAMIC)
static LLVMSymbolizer *get_symbolizer()
{
static LLVMSymbolizer *instance = nullptr;
@@ -49,8 +74,10 @@ static LLVMSymbolizer *get_symbolizer()
}
return instance;
}
+#endif
/* Returns 0 on error, 1 on success. */
+#if defined(HAVE_LIBLLVM_SUPPORT) && !defined(HAVE_LIBLLVM_DYNAMIC)
static int extract_file_and_line(const DILineInfo &line_info, char **file,
unsigned int *line)
{
@@ -69,13 +96,15 @@ static int extract_file_and_line(const DILineInfo &line_info, char **file,
*line = line_info.Line;
return 1;
}
+#endif
extern "C"
-int llvm_addr2line(const char *dso_name, u64 addr,
- char **file, unsigned int *line,
- bool unwind_inlines,
- llvm_a2l_frame **inline_frames)
+int llvm_addr2line(const char *dso_name __maybe_unused, u64 addr __maybe_unused,
+ char **file __maybe_unused, unsigned int *line __maybe_unused,
+ bool unwind_inlines __maybe_unused,
+ llvm_a2l_frame **inline_frames __maybe_unused)
{
+#if defined(HAVE_LIBLLVM_SUPPORT) && !defined(HAVE_LIBLLVM_DYNAMIC)
LLVMSymbolizer *symbolizer = get_symbolizer();
object::SectionedAddress sectioned_addr = {
addr,
@@ -135,8 +164,33 @@ int llvm_addr2line(const char *dso_name, u64 addr,
return 0;
return extract_file_and_line(*res_or_err, file, line);
}
+#elif defined(HAVE_LIBLLVM_DYNAMIC)
+ static bool fn_init;
+ static int (*fn)(const char *dso_name, u64 addr,
+ char **file, unsigned int *line,
+ bool unwind_inlines,
+ llvm_a2l_frame **inline_frames);
+
+ if (!fn_init) {
+ void * handle = perf_llvm_c_helpers_dll_handle();
+
+ if (!handle)
+ return 0;
+
+ fn = reinterpret_cast<decltype(fn)>(dlsym(handle, "llvm_addr2line"));
+ if (!fn)
+ pr_debug("dlsym failed for llvm_addr2line\n");
+ fn_init = true;
+ }
+ if (!fn)
+ return 0;
+ return fn(dso_name, addr, file, line, unwind_inlines, inline_frames);
+#else
+ return 0;
+#endif
}
+#if defined(HAVE_LIBLLVM_SUPPORT) && !defined(HAVE_LIBLLVM_DYNAMIC)
static char *
make_symbol_relative_string(struct dso *dso, const char *sym_name,
u64 addr, u64 base_addr)
@@ -158,10 +212,13 @@ make_symbol_relative_string(struct dso *dso, const char *sym_name,
return strdup(sym_name);
}
}
+#endif
extern "C"
-char *llvm_name_for_code(struct dso *dso, const char *dso_name, u64 addr)
+char *llvm_name_for_code(struct dso *dso __maybe_unused, const char *dso_name __maybe_unused,
+ u64 addr __maybe_unused)
{
+#if defined(HAVE_LIBLLVM_SUPPORT) && !defined(HAVE_LIBLLVM_DYNAMIC)
LLVMSymbolizer *symbolizer = get_symbolizer();
object::SectionedAddress sectioned_addr = {
addr,
@@ -175,11 +232,34 @@ char *llvm_name_for_code(struct dso *dso, const char *dso_name, u64 addr)
return make_symbol_relative_string(
dso, res_or_err->FunctionName.c_str(),
addr, res_or_err->StartAddress ? *res_or_err->StartAddress : 0);
+#elif defined(HAVE_LIBLLVM_DYNAMIC)
+ static bool fn_init;
+ static char *(*fn)(struct dso *dso, const char *dso_name, u64 addr);
+
+ if (!fn_init) {
+ void * handle = perf_llvm_c_helpers_dll_handle();
+
+ if (!handle)
+ return NULL;
+
+ fn = reinterpret_cast<decltype(fn)>(dlsym(handle, "llvm_name_for_code"));
+ if (!fn)
+ pr_debug("dlsym failed for llvm_name_for_code\n");
+ fn_init = true;
+ }
+ if (!fn)
+ return NULL;
+ return fn(dso, dso_name, addr);
+#else
+ return 0;
+#endif
}
extern "C"
-char *llvm_name_for_data(struct dso *dso, const char *dso_name, u64 addr)
+char *llvm_name_for_data(struct dso *dso __maybe_unused, const char *dso_name __maybe_unused,
+ u64 addr __maybe_unused)
{
+#if defined(HAVE_LIBLLVM_SUPPORT) && !defined(HAVE_LIBLLVM_DYNAMIC)
LLVMSymbolizer *symbolizer = get_symbolizer();
object::SectionedAddress sectioned_addr = {
addr,
@@ -193,4 +273,25 @@ char *llvm_name_for_data(struct dso *dso, const char *dso_name, u64 addr)
return make_symbol_relative_string(
dso, res_or_err->Name.c_str(),
addr, res_or_err->Start);
+#elif defined(HAVE_LIBLLVM_DYNAMIC)
+ static bool fn_init;
+ static char *(*fn)(struct dso *dso, const char *dso_name, u64 addr);
+
+ if (!fn_init) {
+ void * handle = perf_llvm_c_helpers_dll_handle();
+
+ if (!handle)
+ return NULL;
+
+ fn = reinterpret_cast<decltype(fn)>(dlsym(handle, "llvm_name_for_data"));
+ if (!fn)
+ pr_debug("dlsym failed for llvm_name_for_data\n");
+ fn_init = true;
+ }
+ if (!fn)
+ return NULL;
+ return fn(dso, dso_name, addr);
+#else
+ return 0;
+#endif
}
diff --git a/tools/perf/util/llvm.c b/tools/perf/util/llvm.c
index ddc737194692..f6a8943b7c9d 100644
--- a/tools/perf/util/llvm.c
+++ b/tools/perf/util/llvm.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
#include "llvm.h"
+#include "llvm-c-helpers.h"
#include "annotate.h"
#include "debug.h"
#include "dso.h"
@@ -7,17 +8,243 @@
#include "namespaces.h"
#include "srcline.h"
#include "symbol.h"
+#include <dlfcn.h>
#include <fcntl.h>
+#include <inttypes.h>
#include <unistd.h>
#include <linux/zalloc.h>
-#ifdef HAVE_LIBLLVM_SUPPORT
-#include "llvm-c-helpers.h"
+#if defined(HAVE_LIBLLVM_SUPPORT) && !defined(HAVE_LIBLLVM_DYNAMIC)
#include <llvm-c/Disassembler.h>
#include <llvm-c/Target.h>
+#else
+typedef void *LLVMDisasmContextRef;
+typedef int (*LLVMOpInfoCallback)(void *dis_info, uint64_t pc, uint64_t offset,
+ uint64_t op_size, uint64_t inst_size,
+ int tag_type, void *tag_buf);
+typedef const char *(*LLVMSymbolLookupCallback)(void *dis_info,
+ uint64_t reference_value,
+ uint64_t *reference_type,
+ uint64_t reference_pc,
+ const char **reference_name);
+#define LLVMDisassembler_ReferenceType_InOut_None 0
+#define LLVMDisassembler_ReferenceType_In_Branch 1
+#define LLVMDisassembler_ReferenceType_In_PCrel_Load 2
+#define LLVMDisassembler_Option_PrintImmHex 2
+#define LLVMDisassembler_Option_AsmPrinterVariant 4
+const char *llvm_targets[] = {
+ "AMDGPU",
+ "ARM",
+ "AVR",
+ "BPF",
+ "Hexagon",
+ "Lanai",
+ "LoongArch",
+ "Mips",
+ "MSP430",
+ "NVPTX",
+ "PowerPC",
+ "RISCV",
+ "Sparc",
+ "SystemZ",
+ "VE",
+ "WebAssembly",
+ "X86",
+ "XCore",
+ "M68k",
+ "Xtensa",
+};
+#endif
+
+#if !defined(HAVE_LIBLLVM_SUPPORT) || defined(HAVE_LIBLLVM_DYNAMIC)
+static void *perf_llvm_dll_handle(void)
+{
+ static bool dll_handle_init;
+ static void *dll_handle;
+
+ if (!dll_handle_init) {
+ dll_handle_init = true;
+ dll_handle = dlopen("libLLVM.so", RTLD_LAZY);
+ if (!dll_handle)
+ pr_debug("dlopen failed for libLLVM.so\n");
+ }
+ return dll_handle;
+}
+#endif
+
+#if !defined(HAVE_LIBLLVM_SUPPORT) || defined(HAVE_LIBLLVM_DYNAMIC)
+static void *perf_llvm_dll_fun(const char *fmt, const char *target)
+{
+ char buf[128];
+ void *fn;
+
+ snprintf(buf, sizeof(buf), fmt, target);
+ fn = dlsym(perf_llvm_dll_handle(), buf);
+ if (!fn)
+ pr_debug("dlsym failed for %s\n", buf);
+
+ return fn;
+}
+#endif
+
+static void perf_LLVMInitializeAllTargetInfos(void)
+{
+#if defined(HAVE_LIBLLVM_SUPPORT) && !defined(HAVE_LIBLLVM_DYNAMIC)
+ LLVMInitializeAllTargetInfos();
+#else
+ /* LLVMInitializeAllTargetInfos is a header file function not available as a symbol. */
+ static bool done_init;
+
+ if (done_init)
+ return;
+
+ for (size_t i = 0; i < ARRAY_SIZE(llvm_targets); i++) {
+ void (*fn)(void) = perf_llvm_dll_fun("LLVMInitialize%sTargetInfo",
+ llvm_targets[i]);
+
+ if (!fn)
+ continue;
+ fn();
+ }
+ done_init = true;
+#endif
+}
+
+static void perf_LLVMInitializeAllTargetMCs(void)
+{
+#if defined(HAVE_LIBLLVM_SUPPORT) && !defined(HAVE_LIBLLVM_DYNAMIC)
+ LLVMInitializeAllTargetMCs();
+#else
+ /* LLVMInitializeAllTargetMCs is a header file function not available as a symbol. */
+ static bool done_init;
+
+ if (done_init)
+ return;
+
+ for (size_t i = 0; i < ARRAY_SIZE(llvm_targets); i++) {
+ void (*fn)(void) = perf_llvm_dll_fun("LLVMInitialize%sTargetMC",
+ llvm_targets[i]);
+
+ if (!fn)
+ continue;
+ fn();
+ }
+ done_init = true;
+#endif
+}
+
+static void perf_LLVMInitializeAllDisassemblers(void)
+{
+#if defined(HAVE_LIBLLVM_SUPPORT) && !defined(HAVE_LIBLLVM_DYNAMIC)
+ LLVMInitializeAllDisassemblers();
+#else
+ /* LLVMInitializeAllDisassemblers is a header file function not available as a symbol. */
+ static bool done_init;
+
+ if (done_init)
+ return;
+
+ for (size_t i = 0; i < ARRAY_SIZE(llvm_targets); i++) {
+ void (*fn)(void) = perf_llvm_dll_fun("LLVMInitialize%sDisassembler",
+ llvm_targets[i]);
+
+ if (!fn)
+ continue;
+ fn();
+ }
+ done_init = true;
+#endif
+}
+
+static LLVMDisasmContextRef perf_LLVMCreateDisasm(const char *triple_name, void *dis_info,
+ int tag_type, LLVMOpInfoCallback get_op_info,
+ LLVMSymbolLookupCallback symbol_lookup)
+{
+#if defined(HAVE_LIBLLVM_SUPPORT) && !defined(HAVE_LIBLLVM_DYNAMIC)
+ return LLVMCreateDisasm(triple_name, dis_info, tag_type, get_op_info, symbol_lookup);
+#else
+ static bool fn_init;
+ static LLVMDisasmContextRef (*fn)(const char *triple_name, void *dis_info,
+ int tag_type, LLVMOpInfoCallback get_op_info,
+ LLVMSymbolLookupCallback symbol_lookup);
+
+ if (!fn_init) {
+ fn = dlsym(perf_llvm_dll_handle(), "LLVMCreateDisasm");
+ if (!fn)
+ pr_debug("dlsym failed for LLVMCreateDisasm\n");
+ fn_init = true;
+ }
+ if (!fn)
+ return NULL;
+ return fn(triple_name, dis_info, tag_type, get_op_info, symbol_lookup);
+#endif
+}
+
+static int perf_LLVMSetDisasmOptions(LLVMDisasmContextRef context, uint64_t options)
+{
+#if defined(HAVE_LIBLLVM_SUPPORT) && !defined(HAVE_LIBLLVM_DYNAMIC)
+ return LLVMSetDisasmOptions(context, options);
+#else
+ static bool fn_init;
+ static int (*fn)(LLVMDisasmContextRef context, uint64_t options);
+
+ if (!fn_init) {
+ fn = dlsym(perf_llvm_dll_handle(), "LLVMSetDisasmOptions");
+ if (!fn)
+ pr_debug("dlsym failed for LLVMSetDisasmOptions\n");
+ fn_init = true;
+ }
+ if (!fn)
+ return 0;
+ return fn(context, options);
+#endif
+}
+
+static size_t perf_LLVMDisasmInstruction(LLVMDisasmContextRef context, uint8_t *bytes,
+ uint64_t bytes_size, uint64_t pc,
+ char *out_string, size_t out_string_size)
+{
+#if defined(HAVE_LIBLLVM_SUPPORT) && !defined(HAVE_LIBLLVM_DYNAMIC)
+ return LLVMDisasmInstruction(context, bytes, bytes_size, pc, out_string, out_string_size);
+#else
+ static bool fn_init;
+ static int (*fn)(LLVMDisasmContextRef context, uint8_t *bytes,
+ uint64_t bytes_size, uint64_t pc,
+ char *out_string, size_t out_string_size);
+
+ if (!fn_init) {
+ fn = dlsym(perf_llvm_dll_handle(), "LLVMDisasmInstruction");
+ if (!fn)
+ pr_debug("dlsym failed for LLVMDisasmInstruction\n");
+ fn_init = true;
+ }
+ if (!fn)
+ return 0;
+ return fn(context, bytes, bytes_size, pc, out_string, out_string_size);
+#endif
+}
+
+static void perf_LLVMDisasmDispose(LLVMDisasmContextRef context)
+{
+#if defined(HAVE_LIBLLVM_SUPPORT) && !defined(HAVE_LIBLLVM_DYNAMIC)
+ LLVMDisasmDispose(context);
+#else
+ static bool fn_init;
+ static int (*fn)(LLVMDisasmContextRef context);
+
+ if (!fn_init) {
+ fn = dlsym(perf_llvm_dll_handle(), "LLVMDisasmDispose");
+ if (!fn)
+ pr_debug("dlsym failed for LLVMDisasmDispose\n");
+ fn_init = true;
+ }
+ if (!fn)
+ return;
+ fn(context);
#endif
+}
+
-#ifdef HAVE_LIBLLVM_SUPPORT
static void free_llvm_inline_frames(struct llvm_a2l_frame *inline_frames,
int num_frames)
{
@@ -29,14 +256,12 @@ static void free_llvm_inline_frames(struct llvm_a2l_frame *inline_frames,
zfree(&inline_frames);
}
}
-#endif
int llvm__addr2line(const char *dso_name __maybe_unused, u64 addr __maybe_unused,
char **file __maybe_unused, unsigned int *line __maybe_unused,
struct dso *dso __maybe_unused, bool unwind_inlines __maybe_unused,
struct inline_node *node __maybe_unused, struct symbol *sym __maybe_unused)
{
-#ifdef HAVE_LIBLLVM_SUPPORT
struct llvm_a2l_frame *inline_frames = NULL;
int num_frames = llvm_addr2line(dso_name, addr, file, line,
node && unwind_inlines, &inline_frames);
@@ -64,9 +289,6 @@ int llvm__addr2line(const char *dso_name __maybe_unused, u64 addr __maybe_unused
free_llvm_inline_frames(inline_frames, num_frames);
return num_frames;
-#else
- return -1;
-#endif
}
void dso__free_a2l_llvm(struct dso *dso __maybe_unused)
@@ -75,7 +297,6 @@ void dso__free_a2l_llvm(struct dso *dso __maybe_unused)
}
-#if defined(HAVE_LIBLLVM_SUPPORT)
struct find_file_offset_data {
u64 ip;
u64 offset;
@@ -139,7 +360,6 @@ read_symbol(const char *filename, struct map *map, struct symbol *sym,
free(buf);
return NULL;
}
-#endif
/*
* Whenever LLVM wants to resolve an address into a symbol, it calls this
@@ -149,7 +369,6 @@ read_symbol(const char *filename, struct map *map, struct symbol *sym,
* should add some textual annotation for after the instruction. The caller
* will use this information to add the actual annotation.
*/
-#ifdef HAVE_LIBLLVM_SUPPORT
struct symbol_lookup_storage {
u64 branch_addr;
u64 pcrel_load_addr;
@@ -170,12 +389,10 @@ symbol_lookup_callback(void *disinfo, uint64_t value,
*ref_type = LLVMDisassembler_ReferenceType_InOut_None;
return NULL;
}
-#endif
int symbol__disassemble_llvm(const char *filename, struct symbol *sym,
struct annotate_args *args __maybe_unused)
{
-#ifdef HAVE_LIBLLVM_SUPPORT
struct annotation *notes = symbol__annotation(sym);
struct map *map = args->ms.map;
struct dso *dso = map__dso(map);
@@ -197,9 +414,9 @@ int symbol__disassemble_llvm(const char *filename, struct symbol *sym,
if (args->options->objdump_path)
return -1;
- LLVMInitializeAllTargetInfos();
- LLVMInitializeAllTargetMCs();
- LLVMInitializeAllDisassemblers();
+ perf_LLVMInitializeAllTargetInfos();
+ perf_LLVMInitializeAllTargetMCs();
+ perf_LLVMInitializeAllDisassemblers();
buf = read_symbol(filename, map, sym, &len, &is_64bit);
if (buf == NULL)
@@ -215,15 +432,14 @@ int symbol__disassemble_llvm(const char *filename, struct symbol *sym,
args->arch->name);
}
- disasm = LLVMCreateDisasm(triplet, &storage, 0, NULL,
- symbol_lookup_callback);
+ disasm = perf_LLVMCreateDisasm(triplet, &storage, 0, NULL,
+ symbol_lookup_callback);
if (disasm == NULL)
goto err;
if (args->options->disassembler_style &&
!strcmp(args->options->disassembler_style, "intel"))
- LLVMSetDisasmOptions(disasm,
- LLVMDisassembler_Option_AsmPrinterVariant);
+ perf_LLVMSetDisasmOptions(disasm, LLVMDisassembler_Option_AsmPrinterVariant);
/*
* This needs to be set after AsmPrinterVariant, due to a bug in LLVM;
@@ -231,7 +447,7 @@ int symbol__disassemble_llvm(const char *filename, struct symbol *sym,
* forget about the PrintImmHex flag (which is applied before if both
* are given to the same call).
*/
- LLVMSetDisasmOptions(disasm, LLVMDisassembler_Option_PrintImmHex);
+ perf_LLVMSetDisasmOptions(disasm, LLVMDisassembler_Option_PrintImmHex);
/* add the function address and name */
scnprintf(disasm_buf, sizeof(disasm_buf), "%#"PRIx64" <%s>:",
@@ -256,9 +472,9 @@ int symbol__disassemble_llvm(const char *filename, struct symbol *sym,
storage.branch_addr = 0;
storage.pcrel_load_addr = 0;
- ins_len = LLVMDisasmInstruction(disasm, buf + offset,
- len - offset, pc,
- disasm_buf, sizeof(disasm_buf));
+ ins_len = perf_LLVMDisasmInstruction(disasm, buf + offset,
+ len - offset, pc,
+ disasm_buf, sizeof(disasm_buf));
if (ins_len == 0)
goto err;
disasm_len = strlen(disasm_buf);
@@ -314,13 +530,8 @@ int symbol__disassemble_llvm(const char *filename, struct symbol *sym,
ret = 0;
err:
- LLVMDisasmDispose(disasm);
+ perf_LLVMDisasmDispose(disasm);
free(buf);
free(line_storage);
return ret;
-#else // HAVE_LIBLLVM_SUPPORT
- pr_debug("The LLVM disassembler isn't linked in for %s in %s\n",
- sym->name, filename);
- return -1;
-#endif
}
--
2.48.0.rc2.279.g1de40edade-goog
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v1 0/5] Support dynamic opening of capstone and libLLVM
2025-01-20 17:32 [PATCH v1 0/5] Support dynamic opening of capstone and libLLVM Ian Rogers
` (4 preceding siblings ...)
2025-01-20 17:32 ` [PATCH v1 5/5] perf llvm: Support for dlopen-ing libLLVM.so Ian Rogers
@ 2025-01-20 18:01 ` Ian Rogers
5 siblings, 0 replies; 12+ messages in thread
From: Ian Rogers @ 2025-01-20 18:01 UTC (permalink / raw)
To: Nick Desaulniers, Bill Wendling, linux-perf-users, llvm
Cc: Peter Zijlstra, Dmitry Vyukov, Masami Hiramatsu (Google),
Kajol Jain, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim,
Mark Rutland, Alexander Shishkin, Jiri Olsa, Adrian Hunter,
Kan Liang, Nathan Chancellor, Justin Stitt, Steinar H. Gunderson,
Charlie Jenkins, Changbin Du, James Clark, Athira Rajeev,
Li Huafei, Andi Kleen, LKML
On Mon, Jan 20, 2025 at 9:32 AM Ian Rogers <irogers@google.com> wrote:
>
> Linking against libcapstone and libLLVM can be a significant increase
> in dependencies and size of memory footprint. For something like `perf
> record` the disassembler and addr2line functionality won't be
> used. Support dynamically loading these libraries using dlopen and
> then calling the appropriate functions found using dlsym.
>
> The patch series:
> 1) moves the capstone and LLVM code to their own C files,
> 2) simplifies a little the capstone code,
> 3) adds perf_ variants of the functions that will either directly call
> the function or use dlsym to discover it.
>
> The addr2line LLVM functionality is written in C++. To avoid linking
> against libLLVM for this, a new LIBLLVM_DYNAMIC option is added where
> the C++ code with the libLLVM dependency will be built into a
> libperf-llvm.so and that dlsym-ed and called against.
A question for the LLVM folks longer term, could the
llvm/DebugInfo/Symbolize/Symbolize.h functionality be added to the
llvm-c API (ie the not C++ one) for the sake of perf's use using dlsym
for its addr2line implementation? This would enable the
libperf-llvm.so library added in these patches to be removed and
libLLVM.so used in its place.
Thanks,
Ian
> Ian Rogers (5):
> perf capstone: Move capstone functionality into its own file
> perf llvm: Move llvm functionality into its own file
> perf capstone: Remove open_capstone_handle
> perf capstone: Support for dlopen-ing libcapstone.so
> perf llvm: Support for dlopen-ing libLLVM.so
>
> tools/perf/Makefile.config | 13 +
> tools/perf/Makefile.perf | 23 +-
> tools/perf/builtin-script.c | 2 -
> tools/perf/tests/make | 2 +
> tools/perf/util/Build | 4 +-
> tools/perf/util/capstone.c | 723 +++++++++++++++++++++++++++++
> tools/perf/util/capstone.h | 24 +
> tools/perf/util/disasm.c | 612 +-----------------------
> tools/perf/util/disasm.h | 2 +
> tools/perf/util/llvm-c-helpers.cpp | 113 ++++-
> tools/perf/util/llvm.c | 537 +++++++++++++++++++++
> tools/perf/util/llvm.h | 24 +
> tools/perf/util/print_insn.c | 117 +----
> tools/perf/util/srcline.c | 65 +--
> tools/perf/util/srcline.h | 6 +
> 15 files changed, 1481 insertions(+), 786 deletions(-)
> create mode 100644 tools/perf/util/capstone.c
> create mode 100644 tools/perf/util/capstone.h
> create mode 100644 tools/perf/util/llvm.c
> create mode 100644 tools/perf/util/llvm.h
>
> --
> 2.48.0.rc2.279.g1de40edade-goog
>
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v1 4/5] perf capstone: Support for dlopen-ing libcapstone.so
2025-01-20 17:32 ` [PATCH v1 4/5] perf capstone: Support for dlopen-ing libcapstone.so Ian Rogers
@ 2025-01-22 22:27 ` Namhyung Kim
2025-01-22 22:42 ` Ian Rogers
0 siblings, 1 reply; 12+ messages in thread
From: Namhyung Kim @ 2025-01-22 22:27 UTC (permalink / raw)
To: Ian Rogers
Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Mark Rutland, Alexander Shishkin, Jiri Olsa, Adrian Hunter,
Kan Liang, Nathan Chancellor, Nick Desaulniers, Bill Wendling,
Justin Stitt, Steinar H. Gunderson, Charlie Jenkins, Changbin Du,
Masami Hiramatsu (Google), James Clark, Kajol Jain, Athira Rajeev,
Li Huafei, Dmitry Vyukov, Andi Kleen, linux-kernel,
linux-perf-users, llvm
Hi Ian,
On Mon, Jan 20, 2025 at 09:32:07AM -0800, Ian Rogers wrote:
> If perf wasn't built against libcapstone, no HAVE_LIBCAPSTONE_SUPPORT,
> support dlopen-ing libcapstone.so and then calling the necessary
> functions by looking them up using dlsym. Reverse engineer the types
> in the API using pahole, adding only what's used in the perf code or
> necessary for the sake of struct size and alignment.
I think reverse engineering the types is fragile. Can we simply change
to dlopen if libcapstone is available?
Thanks,
Namhyung
>
> Signed-off-by: Ian Rogers <irogers@google.com>
> ---
> tools/perf/util/capstone.c | 287 ++++++++++++++++++++++++++++++++-----
> 1 file changed, 248 insertions(+), 39 deletions(-)
>
> diff --git a/tools/perf/util/capstone.c b/tools/perf/util/capstone.c
> index c9845e4d8781..8d65c7a55a8b 100644
> --- a/tools/perf/util/capstone.c
> +++ b/tools/perf/util/capstone.c
> @@ -11,19 +11,249 @@
> #include "print_insn.h"
> #include "symbol.h"
> #include "thread.h"
> +#include <dlfcn.h>
> #include <fcntl.h>
> +#include <inttypes.h>
> #include <string.h>
>
> #ifdef HAVE_LIBCAPSTONE_SUPPORT
> #include <capstone/capstone.h>
> +#else
> +typedef size_t csh;
> +enum cs_arch {
> + CS_ARCH_ARM = 0,
> + CS_ARCH_ARM64 = 1,
> + CS_ARCH_X86 = 3,
> + CS_ARCH_SYSZ = 6,
> +};
> +enum cs_mode {
> + CS_MODE_ARM = 0,
> + CS_MODE_32 = 1 << 2,
> + CS_MODE_64 = 1 << 3,
> + CS_MODE_V8 = 1 << 6,
> + CS_MODE_BIG_ENDIAN = 1 << 31,
> +};
> +enum cs_opt_type {
> + CS_OPT_SYNTAX = 1,
> + CS_OPT_DETAIL = 2,
> +};
> +enum cs_opt_value {
> + CS_OPT_SYNTAX_ATT = 2,
> + CS_OPT_ON = 3,
> +};
> +enum cs_err {
> + CS_ERR_OK = 0,
> + CS_ERR_HANDLE = 3,
> +};
> +enum x86_op_type {
> + X86_OP_IMM = 2,
> + X86_OP_MEM = 3,
> +};
> +enum x86_reg {
> + X86_REG_RIP = 41,
> +};
> +typedef int32_t x86_avx_bcast;
> +struct x86_op_mem {
> + enum x86_reg segment;
> + enum x86_reg base;
> + enum x86_reg index;
> + int scale;
> + int64_t disp;
> +};
> +
> +struct cs_x86_op {
> + enum x86_op_type type;
> + union {
> + enum x86_reg reg;
> + int64_t imm;
> + struct x86_op_mem mem;
> + };
> + uint8_t size;
> + uint8_t access;
> + x86_avx_bcast avx_bcast;
> + bool avx_zero_opmask;
> +};
> +struct cs_x86_encoding {
> + uint8_t modrm_offset;
> + uint8_t disp_offset;
> + uint8_t disp_size;
> + uint8_t imm_offset;
> + uint8_t imm_size;
> +};
> +typedef int32_t x86_xop_cc;
> +typedef int32_t x86_sse_cc;
> +typedef int32_t x86_avx_cc;
> +typedef int32_t x86_avx_rm;
> +struct cs_x86 {
> + uint8_t prefix[4];
> + uint8_t opcode[4];
> + uint8_t rex;
> + uint8_t addr_size;
> + uint8_t modrm;
> + uint8_t sib;
> + int64_t disp;
> + enum x86_reg sib_index;
> + int8_t sib_scale;
> + enum x86_reg sib_base;
> + x86_xop_cc xop_cc;
> + x86_sse_cc sse_cc;
> + x86_avx_cc avx_cc;
> + bool avx_sae;
> + x86_avx_rm avx_rm;
> + union {
> + uint64_t eflags;
> + uint64_t fpu_flags;
> + };
> + uint8_t op_count;
> + struct cs_x86_op operands[8];
> + struct cs_x86_encoding encoding;
> +};
> +struct cs_detail {
> + uint16_t regs_read[12];
> + uint8_t regs_read_count;
> + uint16_t regs_write[20];
> + uint8_t regs_write_count;
> + uint8_t groups[8];
> + uint8_t groups_count;
> +
> + union {
> + struct cs_x86 x86;
> + };
> +};
> +struct cs_insn {
> + unsigned int id;
> + uint64_t address;
> + uint16_t size;
> + uint8_t bytes[16];
> + char mnemonic[32];
> + char op_str[160];
> + struct cs_detail *detail;
> +};
> +#endif
> +
> +#ifndef HAVE_LIBCAPSTONE_SUPPORT
> +static void *perf_cs_dll_handle(void)
> +{
> + static bool dll_handle_init;
> + static void *dll_handle;
> +
> + if (!dll_handle_init) {
> + dll_handle_init = true;
> + dll_handle = dlopen("libcapstone.so", RTLD_LAZY);
> + if (!dll_handle)
> + pr_debug("dlopen failed for libcapstone.so\n");
> + }
> + return dll_handle;
> +}
> +#endif
> +
> +static enum cs_err perf_cs_open(enum cs_arch arch, enum cs_mode mode, csh *handle)
> +{
> +#ifdef HAVE_LIBCAPSTONE_SUPPORT
> + return cs_open(arch, mode, handle);
> +#else
> + static bool fn_init;
> + static enum cs_err (*fn)(enum cs_arch arch, enum cs_mode mode, csh *handle);
> +
> + if (!fn_init) {
> + fn = dlsym(perf_cs_dll_handle(), "cs_open");
> + if (!fn)
> + pr_debug("dlsym failed for cs_open\n");
> + fn_init = true;
> + }
> + if (!fn)
> + return CS_ERR_HANDLE;
> + return fn(arch, mode, handle);
> +#endif
> +}
> +
> +static enum cs_err perf_cs_option(csh handle, enum cs_opt_type type, size_t value)
> +{
> +#ifdef HAVE_LIBCAPSTONE_SUPPORT
> + return cs_option(handle, type, value);
> +#else
> + static bool fn_init;
> + static enum cs_err (*fn)(csh handle, enum cs_opt_type type, size_t value);
> +
> + if (!fn_init) {
> + fn = dlsym(perf_cs_dll_handle(), "cs_option");
> + if (!fn)
> + pr_debug("dlsym failed for cs_option\n");
> + fn_init = true;
> + }
> + if (!fn)
> + return CS_ERR_HANDLE;
> + return fn(handle, type, value);
> +#endif
> +}
> +
> +static size_t perf_cs_disasm(csh handle, const uint8_t *code, size_t code_size,
> + uint64_t address, size_t count, struct cs_insn **insn)
> +{
> +#ifdef HAVE_LIBCAPSTONE_SUPPORT
> + return cs_disasm(handle, code, code_size, address, count, insn);
> +#else
> + static bool fn_init;
> + static enum cs_err (*fn)(csh handle, const uint8_t *code, size_t code_size,
> + uint64_t address, size_t count, struct cs_insn **insn);
> +
> + if (!fn_init) {
> + fn = dlsym(perf_cs_dll_handle(), "cs_disasm");
> + if (!fn)
> + pr_debug("dlsym failed for cs_disasm\n");
> + fn_init = true;
> + }
> + if (!fn)
> + return CS_ERR_HANDLE;
> + return fn(handle, code, code_size, address, count, insn);
> #endif
> +}
>
> +static void perf_cs_free(struct cs_insn *insn, size_t count)
> +{
> #ifdef HAVE_LIBCAPSTONE_SUPPORT
> + cs_free(insn, count);
> +#else
> + static bool fn_init;
> + static void (*fn)(struct cs_insn *insn, size_t count);
> +
> + if (!fn_init) {
> + fn = dlsym(perf_cs_dll_handle(), "cs_free");
> + if (!fn)
> + pr_debug("dlsym failed for cs_free\n");
> + fn_init = true;
> + }
> + if (!fn)
> + return;
> + fn(insn, count);
> +#endif
> +}
> +
> +static enum cs_err perf_cs_close(csh *handle)
> +{
> +#ifdef HAVE_LIBCAPSTONE_SUPPORT
> + return cs_close(handle);
> +#else
> + static bool fn_init;
> + static enum cs_err (*fn)(csh *handle);
> +
> + if (!fn_init) {
> + fn = dlsym(perf_cs_dll_handle(), "cs_close");
> + if (!fn)
> + pr_debug("dlsym failed for cs_close\n");
> + fn_init = true;
> + }
> + if (!fn)
> + return CS_ERR_HANDLE;
> + return fn(handle);
> +#endif
> +}
> +
> static int capstone_init(struct machine *machine, csh *cs_handle, bool is64,
> bool disassembler_style)
> {
> - cs_arch arch;
> - cs_mode mode;
> + enum cs_arch arch;
> + enum cs_mode mode;
>
> if (machine__is(machine, "x86_64") && is64) {
> arch = CS_ARCH_X86;
> @@ -44,7 +274,7 @@ static int capstone_init(struct machine *machine, csh *cs_handle, bool is64,
> return -1;
> }
>
> - if (cs_open(arch, mode, cs_handle) != CS_ERR_OK) {
> + if (perf_cs_open(arch, mode, cs_handle) != CS_ERR_OK) {
> pr_warning_once("cs_open failed\n");
> return -1;
> }
> @@ -56,27 +286,25 @@ static int capstone_init(struct machine *machine, csh *cs_handle, bool is64,
> * is set via annotation args
> */
> if (disassembler_style)
> - cs_option(*cs_handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_ATT);
> + perf_cs_option(*cs_handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_ATT);
> /*
> * Resolving address operands to symbols is implemented
> * on x86 by investigating instruction details.
> */
> - cs_option(*cs_handle, CS_OPT_DETAIL, CS_OPT_ON);
> + perf_cs_option(*cs_handle, CS_OPT_DETAIL, CS_OPT_ON);
> }
>
> return 0;
> }
> -#endif
>
> -#ifdef HAVE_LIBCAPSTONE_SUPPORT
> -static size_t print_insn_x86(struct thread *thread, u8 cpumode, cs_insn *insn,
> +static size_t print_insn_x86(struct thread *thread, u8 cpumode, struct cs_insn *insn,
> int print_opts, FILE *fp)
> {
> struct addr_location al;
> size_t printed = 0;
>
> if (insn->detail && insn->detail->x86.op_count == 1) {
> - cs_x86_op *op = &insn->detail->x86.operands[0];
> + struct cs_x86_op *op = &insn->detail->x86.operands[0];
>
> addr_location__init(&al);
> if (op->type == X86_OP_IMM &&
> @@ -94,7 +322,6 @@ static size_t print_insn_x86(struct thread *thread, u8 cpumode, cs_insn *insn,
> printed += fprintf(fp, "%s %s", insn[0].mnemonic, insn[0].op_str);
> return printed;
> }
> -#endif
>
>
> ssize_t capstone__fprintf_insn_asm(struct machine *machine __maybe_unused,
> @@ -105,9 +332,8 @@ ssize_t capstone__fprintf_insn_asm(struct machine *machine __maybe_unused,
> uint64_t ip __maybe_unused, int *lenp __maybe_unused,
> int print_opts __maybe_unused, FILE *fp __maybe_unused)
> {
> -#ifdef HAVE_LIBCAPSTONE_SUPPORT
> size_t printed;
> - cs_insn *insn;
> + struct cs_insn *insn;
> csh cs_handle;
> size_t count;
> int ret;
> @@ -117,7 +343,7 @@ ssize_t capstone__fprintf_insn_asm(struct machine *machine __maybe_unused,
> if (ret < 0)
> return ret;
>
> - count = cs_disasm(cs_handle, code, code_size, ip, 1, &insn);
> + count = perf_cs_disasm(cs_handle, code, code_size, ip, 1, &insn);
> if (count > 0) {
> if (machine__normalized_is(machine, "x86"))
> printed = print_insn_x86(thread, cpumode, &insn[0], print_opts, fp);
> @@ -125,20 +351,16 @@ ssize_t capstone__fprintf_insn_asm(struct machine *machine __maybe_unused,
> printed = fprintf(fp, "%s %s", insn[0].mnemonic, insn[0].op_str);
> if (lenp)
> *lenp = insn->size;
> - cs_free(insn, count);
> + perf_cs_free(insn, count);
> } else {
> printed = -1;
> }
>
> - cs_close(&cs_handle);
> + perf_cs_close(&cs_handle);
> return printed;
> -#else
> - return -1;
> -#endif
> }
>
> -#ifdef HAVE_LIBCAPSTONE_SUPPORT
> -static void print_capstone_detail(cs_insn *insn, char *buf, size_t len,
> +static void print_capstone_detail(struct cs_insn *insn, char *buf, size_t len,
> struct annotate_args *args, u64 addr)
> {
> int i;
> @@ -153,7 +375,7 @@ static void print_capstone_detail(cs_insn *insn, char *buf, size_t len,
> return;
>
> for (i = 0; i < insn->detail->x86.op_count; i++) {
> - cs_x86_op *op = &insn->detail->x86.operands[i];
> + struct cs_x86_op *op = &insn->detail->x86.operands[i];
> u64 orig_addr;
>
> if (op->type != X86_OP_MEM)
> @@ -194,9 +416,7 @@ static void print_capstone_detail(cs_insn *insn, char *buf, size_t len,
> break;
> }
> }
> -#endif
>
> -#ifdef HAVE_LIBCAPSTONE_SUPPORT
> struct find_file_offset_data {
> u64 ip;
> u64 offset;
> @@ -213,9 +433,7 @@ static int find_file_offset(u64 start, u64 len, u64 pgoff, void *arg)
> }
> return 0;
> }
> -#endif
>
> -#ifdef HAVE_LIBCAPSTONE_SUPPORT
> static u8 *
> read_symbol(const char *filename, struct map *map, struct symbol *sym,
> u64 *len, bool *is_64bit)
> @@ -262,13 +480,11 @@ read_symbol(const char *filename, struct map *map, struct symbol *sym,
> free(buf);
> return NULL;
> }
> -#endif
>
> int symbol__disassemble_capstone(const char *filename __maybe_unused,
> struct symbol *sym __maybe_unused,
> struct annotate_args *args __maybe_unused)
> {
> -#ifdef HAVE_LIBCAPSTONE_SUPPORT
> struct annotation *notes = symbol__annotation(sym);
> struct map *map = args->ms.map;
> u64 start = map__rip_2objdump(map, sym->start);
> @@ -279,7 +495,7 @@ int symbol__disassemble_capstone(const char *filename __maybe_unused,
> bool needs_cs_close = false;
> u8 *buf = NULL;
> csh handle;
> - cs_insn *insn = NULL;
> + struct cs_insn *insn = NULL;
> char disasm_buf[512];
> struct disasm_line *dl;
> bool disassembler_style = false;
> @@ -316,7 +532,7 @@ int symbol__disassemble_capstone(const char *filename __maybe_unused,
>
> needs_cs_close = true;
>
> - free_count = count = cs_disasm(handle, buf, len, start, len, &insn);
> + free_count = count = perf_cs_disasm(handle, buf, len, start, len, &insn);
> for (i = 0, offset = 0; i < count; i++) {
> int printed;
>
> @@ -355,9 +571,9 @@ int symbol__disassemble_capstone(const char *filename __maybe_unused,
>
> out:
> if (needs_cs_close) {
> - cs_close(&handle);
> + perf_cs_close(&handle);
> if (free_count > 0)
> - cs_free(insn, free_count);
> + perf_cs_free(insn, free_count);
> }
> free(buf);
> return count < 0 ? count : 0;
> @@ -377,16 +593,12 @@ int symbol__disassemble_capstone(const char *filename __maybe_unused,
> }
> count = -1;
> goto out;
> -#else
> - return -1;
> -#endif
> }
>
> int symbol__disassemble_capstone_powerpc(const char *filename __maybe_unused,
> struct symbol *sym __maybe_unused,
> struct annotate_args *args __maybe_unused)
> {
> -#ifdef HAVE_LIBCAPSTONE_SUPPORT
> struct annotation *notes = symbol__annotation(sym);
> struct map *map = args->ms.map;
> struct dso *dso = map__dso(map);
> @@ -499,7 +711,7 @@ int symbol__disassemble_capstone_powerpc(const char *filename __maybe_unused,
>
> out:
> if (needs_cs_close)
> - cs_close(&handle);
> + perf_cs_close(&handle);
> free(buf);
> return count < 0 ? count : 0;
>
> @@ -508,7 +720,4 @@ int symbol__disassemble_capstone_powerpc(const char *filename __maybe_unused,
> close(fd);
> count = -1;
> goto out;
> -#else
> - return -1;
> -#endif
> }
> --
> 2.48.0.rc2.279.g1de40edade-goog
>
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v1 4/5] perf capstone: Support for dlopen-ing libcapstone.so
2025-01-22 22:27 ` Namhyung Kim
@ 2025-01-22 22:42 ` Ian Rogers
2025-01-23 16:07 ` Ian Rogers
0 siblings, 1 reply; 12+ messages in thread
From: Ian Rogers @ 2025-01-22 22:42 UTC (permalink / raw)
To: Namhyung Kim
Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Mark Rutland, Alexander Shishkin, Jiri Olsa, Adrian Hunter,
Kan Liang, Nathan Chancellor, Nick Desaulniers, Bill Wendling,
Justin Stitt, Steinar H. Gunderson, Charlie Jenkins, Changbin Du,
Masami Hiramatsu (Google), James Clark, Kajol Jain, Athira Rajeev,
Li Huafei, Dmitry Vyukov, Andi Kleen, linux-kernel,
linux-perf-users, llvm
On Wed, Jan 22, 2025 at 2:27 PM Namhyung Kim <namhyung@kernel.org> wrote:
>
> Hi Ian,
>
> On Mon, Jan 20, 2025 at 09:32:07AM -0800, Ian Rogers wrote:
> > If perf wasn't built against libcapstone, no HAVE_LIBCAPSTONE_SUPPORT,
> > support dlopen-ing libcapstone.so and then calling the necessary
> > functions by looking them up using dlsym. Reverse engineer the types
> > in the API using pahole, adding only what's used in the perf code or
> > necessary for the sake of struct size and alignment.
>
> I think reverse engineering the types is fragile. Can we simply change
> to dlopen if libcapstone is available?
The current behavior is if HAVE_LIBCAPSTONE_SUPPORT then we will link
against libcapstone and can assume the header files are present.
The reverse engineering of structs and constants I agree is brittle,
but these things shouldn't change, it is used in this code when the
capstone/capstone.h isn't present. If capstone/capstone.h is required
we could change the ifdefs to indicate:
1) capstone is present and we're linking against it (this is
HAVE_LIBCAPSTONE_SUPPORT today);
2) capstone is present but use it by way of dlopen;
3) capstone isn't present don't #include, don't link and don't dlopen.
My thought was that this is less good than the reverse engineering.
(1) relies on the builder having libcapstone, which at build time is a
warning that flies by. (2) also relies on the builder having to have
libcapstone and to opt into a dlopen behavior, they'll need to read
Makefiles to discover this. (3) gives a less good perf tool with in
the source lots of __maybe_unused and ifdef-ed out functions all over
the place.
With the reverse engineered values if you have libcapstone then
HAVE_LIBCAPSTONE_SUPPORT works as before - no change. If you don't
then you will be opted into getting something better than objdump -
albeit it could break if the reverse engineered values drift from
those in capstone/capstone.h. Less build options also is less
complexity and cleaner code.
Thanks,
Ian
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v1 4/5] perf capstone: Support for dlopen-ing libcapstone.so
2025-01-22 22:42 ` Ian Rogers
@ 2025-01-23 16:07 ` Ian Rogers
2025-01-23 21:37 ` Ian Rogers
0 siblings, 1 reply; 12+ messages in thread
From: Ian Rogers @ 2025-01-23 16:07 UTC (permalink / raw)
To: Namhyung Kim
Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Mark Rutland, Alexander Shishkin, Jiri Olsa, Adrian Hunter,
Kan Liang, Nathan Chancellor, Nick Desaulniers, Bill Wendling,
Justin Stitt, Steinar H. Gunderson, Charlie Jenkins, Changbin Du,
Masami Hiramatsu (Google), James Clark, Kajol Jain, Athira Rajeev,
Li Huafei, Dmitry Vyukov, Andi Kleen, linux-kernel,
linux-perf-users, llvm
On Wed, Jan 22, 2025 at 2:42 PM Ian Rogers <irogers@google.com> wrote:
>
> On Wed, Jan 22, 2025 at 2:27 PM Namhyung Kim <namhyung@kernel.org> wrote:
> >
> > Hi Ian,
> >
> > On Mon, Jan 20, 2025 at 09:32:07AM -0800, Ian Rogers wrote:
> > > If perf wasn't built against libcapstone, no HAVE_LIBCAPSTONE_SUPPORT,
> > > support dlopen-ing libcapstone.so and then calling the necessary
> > > functions by looking them up using dlsym. Reverse engineer the types
> > > in the API using pahole, adding only what's used in the perf code or
> > > necessary for the sake of struct size and alignment.
> >
> > I think reverse engineering the types is fragile. Can we simply change
> > to dlopen if libcapstone is available?
>
> The current behavior is if HAVE_LIBCAPSTONE_SUPPORT then we will link
> against libcapstone and can assume the header files are present.
>
> The reverse engineering of structs and constants I agree is brittle,
> but these things shouldn't change, it is used in this code when the
> capstone/capstone.h isn't present. If capstone/capstone.h is required
> we could change the ifdefs to indicate:
> 1) capstone is present and we're linking against it (this is
> HAVE_LIBCAPSTONE_SUPPORT today);
> 2) capstone is present but use it by way of dlopen;
> 3) capstone isn't present don't #include, don't link and don't dlopen.
>
> My thought was that this is less good than the reverse engineering.
> (1) relies on the builder having libcapstone, which at build time is a
> warning that flies by. (2) also relies on the builder having to have
> libcapstone and to opt into a dlopen behavior, they'll need to read
> Makefiles to discover this. (3) gives a less good perf tool with in
> the source lots of __maybe_unused and ifdef-ed out functions all over
> the place.
>
> With the reverse engineered values if you have libcapstone then
> HAVE_LIBCAPSTONE_SUPPORT works as before - no change. If you don't
> then you will be opted into getting something better than objdump -
> albeit it could break if the reverse engineered values drift from
> those in capstone/capstone.h. Less build options also is less
> complexity and cleaner code.
To be clear, if the header file values/structs drift then libcapstone
will need to version values/structs, we'll need to update the perf
code to use these, etc. Otherwise a tool linked against an older
libcapstone will break when libcapstone is updated. The code handling
not having any libcapstone, dlopen-ed or not, would be significant as
every static function will need an #ifdef to avoid a not used warning,
every parameter will need a __maybe_unused and the overall code
quality in perf will be worse imo because of this. The no libcapstone
default when not detected would be crappy for users. The values/types
added here are fewer than 20, and so are not a huge maintenance
burden. I did a similar thing for the LLVM C++ symbolizer code and its
maintenance would have been a burden and so abandoned it in the
llvm-c-helpers case that mainly affects addr2line and not disassembly.
Hopefully later LLVM C APIs will make this unnecessary.
Anyway, because of this I won't be changing this in later versions. I
will be carrying these changes in Google's tree and think ideally it
would be carried in the regular tree.
Thanks,
Ian
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v1 4/5] perf capstone: Support for dlopen-ing libcapstone.so
2025-01-23 16:07 ` Ian Rogers
@ 2025-01-23 21:37 ` Ian Rogers
2025-01-24 21:31 ` Namhyung Kim
0 siblings, 1 reply; 12+ messages in thread
From: Ian Rogers @ 2025-01-23 21:37 UTC (permalink / raw)
To: Namhyung Kim
Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Mark Rutland, Alexander Shishkin, Jiri Olsa, Adrian Hunter,
Kan Liang, Nathan Chancellor, Nick Desaulniers, Bill Wendling,
Justin Stitt, Steinar H. Gunderson, Charlie Jenkins, Changbin Du,
Masami Hiramatsu (Google), James Clark, Kajol Jain, Athira Rajeev,
Li Huafei, Dmitry Vyukov, Andi Kleen, linux-kernel,
linux-perf-users, llvm
> > On Wed, Jan 22, 2025 at 2:27 PM Namhyung Kim <namhyung@kernel.org> wrote:
> > > I think reverse engineering the types is fragile. Can we simply change
> > > to dlopen if libcapstone is available?
To understand the scope of the problem, here is the LLVM code:
https://github.com/googleprodkernel/linux-perf/blob/google_tools_master/tools/perf/util/llvm.c#L22
3 typedefs and 5 #defines on 13 lines of code.
The capstone code is larger primarily due to needing to define cs_x86
(and its dependencies) which are used for extra detail in printing:
https://github.com/googleprodkernel/linux-perf/blob/google_tools_master/tools/perf/util/capstone.c#L23-L132
We could opt out of providing the extra detail if capstone.h isn't
present. I believe this can remove lines 49 to 123. This would mean
there would be 37 lines of reverse engineered types - 2 structs and 5
enums.
My feeling is that this isn't significant and better than returning
errors for capstone/llvm, having #ifdefs/__maybe_unused for the cases
we lack the types throughout the code and relying on people building
noticing a warning to realize they need to install a dependency - the
alternative of failing the build if the capstone/LLVM header files
aren't present, and unless some NO_... build option isn't passed,
likely won't fly with perf developers.
Thanks,
Ian
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v1 4/5] perf capstone: Support for dlopen-ing libcapstone.so
2025-01-23 21:37 ` Ian Rogers
@ 2025-01-24 21:31 ` Namhyung Kim
0 siblings, 0 replies; 12+ messages in thread
From: Namhyung Kim @ 2025-01-24 21:31 UTC (permalink / raw)
To: Ian Rogers
Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Mark Rutland, Alexander Shishkin, Jiri Olsa, Adrian Hunter,
Kan Liang, Nathan Chancellor, Nick Desaulniers, Bill Wendling,
Justin Stitt, Steinar H. Gunderson, Charlie Jenkins, Changbin Du,
Masami Hiramatsu (Google), James Clark, Kajol Jain, Athira Rajeev,
Li Huafei, Dmitry Vyukov, Andi Kleen, linux-kernel,
linux-perf-users, llvm
On Thu, Jan 23, 2025 at 01:37:16PM -0800, Ian Rogers wrote:
> > > On Wed, Jan 22, 2025 at 2:27 PM Namhyung Kim <namhyung@kernel.org> wrote:
> > > > I think reverse engineering the types is fragile. Can we simply change
> > > > to dlopen if libcapstone is available?
>
> To understand the scope of the problem, here is the LLVM code:
> https://github.com/googleprodkernel/linux-perf/blob/google_tools_master/tools/perf/util/llvm.c#L22
> 3 typedefs and 5 #defines on 13 lines of code.
It doesn't look that bad than I thought. :)
>
> The capstone code is larger primarily due to needing to define cs_x86
> (and its dependencies) which are used for extra detail in printing:
> https://github.com/googleprodkernel/linux-perf/blob/google_tools_master/tools/perf/util/capstone.c#L23-L132
> We could opt out of providing the extra detail if capstone.h isn't
> present. I believe this can remove lines 49 to 123. This would mean
> there would be 37 lines of reverse engineered types - 2 structs and 5
> enums.
Probably, we don't need to carry the full details of instructions when
the library support was not built.
>
> My feeling is that this isn't significant and better than returning
> errors for capstone/llvm, having #ifdefs/__maybe_unused for the cases
> we lack the types throughout the code and relying on people building
> noticing a warning to realize they need to install a dependency - the
> alternative of failing the build if the capstone/LLVM header files
> aren't present, and unless some NO_... build option isn't passed,
> likely won't fly with perf developers.
I don't think failing the build due to the optional dependencies is an
option. I'm not against the idea of dlopen but my concern is to carry
those external definitions in the perf code base. Maybe it'd be ok if
we could keep it minimally.
Thanks,
Namhyung
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2025-01-24 21:31 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-01-20 17:32 [PATCH v1 0/5] Support dynamic opening of capstone and libLLVM Ian Rogers
2025-01-20 17:32 ` [PATCH v1 1/5] perf capstone: Move capstone functionality into its own file Ian Rogers
2025-01-20 17:32 ` [PATCH v1 2/5] perf llvm: Move llvm " Ian Rogers
2025-01-20 17:32 ` [PATCH v1 3/5] perf capstone: Remove open_capstone_handle Ian Rogers
2025-01-20 17:32 ` [PATCH v1 4/5] perf capstone: Support for dlopen-ing libcapstone.so Ian Rogers
2025-01-22 22:27 ` Namhyung Kim
2025-01-22 22:42 ` Ian Rogers
2025-01-23 16:07 ` Ian Rogers
2025-01-23 21:37 ` Ian Rogers
2025-01-24 21:31 ` Namhyung Kim
2025-01-20 17:32 ` [PATCH v1 5/5] perf llvm: Support for dlopen-ing libLLVM.so Ian Rogers
2025-01-20 18:01 ` [PATCH v1 0/5] Support dynamic opening of capstone and libLLVM Ian Rogers
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).