Skip to content

Commit a6e8a58

Browse files
committed
perf disasm: Allow configuring what disassemblers to use
The perf tools annotation code used for a long time parsing the output of binutils's objdump (or its reimplementations, like llvm's) to then parse and augment it with samples, allow navigation, etc. More recently disassemblers from the capstone and llvm (libraries, not parsing the output of tools using those libraries to mimic binutils's objdump output) were introduced. So when all those methods are available, there is a static preference for a series of attempts of disassembling a binary, with the 'llvm, capstone, objdump' sequence being hard coded. This patch allows users to change that sequence, specifying via a 'perf config' 'annotate.disassemblers' entry which and in what order disassemblers should be attempted. As alluded to in the comments in the source code of this series, this flexibility is useful for users and developers alike, elliminating the requirement to rebuild the tool with some specific set of libraries to see how the output of disassembling would be for one of these methods. root@x1:~# rm -f ~/.perfconfig root@x1:~# perf annotate -v --stdio2 update_load_avg <SNIP> symbol__disassemble: filename=/usr/lib/debug/lib/modules/6.11.4-201.fc40.x86_64/vmlinux, sym=update_load_avg, start=0xffffffffb6148fe0, en> annotating [0x6ff7170] /usr/lib/debug/lib/modules/6.11.4-201.fc40.x86_64/vmlinux : [0x7407ca0] update_load_avg Disassembled with llvm annotate.disassemblers=llvm,capstone,objdump Samples: 66 of event 'cpu_atom/cycles/P', 10000 Hz, Event count (approx.): 5185444, [percent: local period] update_load_avg() /usr/lib/debug/lib/modules/6.11.4-201.fc40.x86_64/vmlinux Percent 0xffffffff81148fe0 <update_load_avg>: 1.61 pushq %r15 pushq %r14 1.00 pushq %r13 movl %edx,%r13d 1.90 pushq %r12 pushq %rbp movq %rsi,%rbp pushq %rbx movq %rdi,%rbx subq $0x18,%rsp 15.14 movl 0x1a4(%rdi),%eax root@x1:~# perf config annotate.disassemblers=capstone root@x1:~# cat ~/.perfconfig # this file is auto-generated. [annotate] disassemblers = capstone root@x1:~# root@x1:~# perf annotate -v --stdio2 update_load_avg <SNIP> Disassembled with capstone annotate.disassemblers=capstone Samples: 66 of event 'cpu_atom/cycles/P', 10000 Hz, Event count (approx.): 5185444, [percent: local period] update_load_avg() /usr/lib/debug/lib/modules/6.11.4-201.fc40.x86_64/vmlinux Percent 0xffffffff81148fe0 <update_load_avg>: 1.61 pushq %r15 pushq %r14 1.00 pushq %r13 movl %edx,%r13d 1.90 pushq %r12 pushq %rbp movq %rsi,%rbp pushq %rbx movq %rdi,%rbx subq $0x18,%rsp 15.14 movl 0x1a4(%rdi),%eax root@x1:~# perf config annotate.disassemblers=objdump,capstone root@x1:~# perf config annotate.disassemblers annotate.disassemblers=objdump,capstone root@x1:~# cat ~/.perfconfig # this file is auto-generated. [annotate] disassemblers = objdump,capstone root@x1:~# perf annotate -v --stdio2 update_load_avg Executing: objdump --start-address=0xffffffff81148fe0 \ --stop-address=0xffffffff811497aa \ -d --no-show-raw-insn -S -C "$1" Disassembled with objdump annotate.disassemblers=objdump,capstone Samples: 66 of event 'cpu_atom/cycles/P', 10000 Hz, Event count (approx.): 5185444, [percent: local period] update_load_avg() /usr/lib/debug/lib/modules/6.11.4-201.fc40.x86_64/vmlinux Percent Disassembly of section .text: ffffffff81148fe0 <update_load_avg>: #define DO_ATTACH 0x4 ffffffff81148fe0 <update_load_avg>: #define DO_ATTACH 0x4 #define DO_DETACH 0x8 /* Update task and its cfs_rq load average */ static inline void update_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) { 1.61 push %r15 push %r14 1.00 push %r13 mov %edx,%r13d 1.90 push %r12 push %rbp mov %rsi,%rbp push %rbx mov %rdi,%rbx sub $0x18,%rsp } /* rq->task_clock normalized against any time this cfs_rq has spent throttled */ static inline u64 cfs_rq_clock_pelt(struct cfs_rq *cfs_rq) { if (unlikely(cfs_rq->throttle_count)) 15.14 mov 0x1a4(%rdi),%eax root@x1:~# After adding a way to select the disassembler from the command line a 'perf test' comparing the output of the various diassemblers should be introduced, to test these codebases. Acked-by: Ian Rogers <irogers@google.com> Cc: Adrian Hunter <adrian.hunter@intel.com> Cc: Athira Rajeev <atrajeev@linux.vnet.ibm.com> Cc: Jiri Olsa <jolsa@kernel.org> Cc: Kan Liang <kan.liang@linux.intel.com> Cc: Namhyung Kim <namhyung@kernel.org> Cc: Steinar H. Gunderson <sesse@google.com> Link: https://lore.kernel.org/r/20241111151734.1018476-4-acme@kernel.org Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
1 parent 1f7393a commit a6e8a58

File tree

4 files changed

+96
-6
lines changed

4 files changed

+96
-6
lines changed

tools/perf/Documentation/perf-config.txt

+13
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,19 @@ annotate.*::
247247
These are in control of addresses, jump function, source code
248248
in lines of assembly code from a specific program.
249249

250+
annotate.disassemblers::
251+
Choose the disassembler to use: "objdump", "llvm", "capstone",
252+
if not specified it will first try, if available, the "llvm" one,
253+
then, if it fails, "capstone", and finally the original "objdump"
254+
based one.
255+
256+
Choosing a different one is useful when handling some feature that
257+
is known to be best support at some point by one of the options,
258+
to compare the output when in doubt about some bug, etc.
259+
260+
This can be a list, in order of preference, the first one that works
261+
finishes the process.
262+
250263
annotate.addr2line::
251264
addr2line binary to use for file names and line numbers.
252265

tools/perf/util/annotate.c

+6
Original file line numberDiff line numberDiff line change
@@ -2116,6 +2116,12 @@ static int annotation__config(const char *var, const char *value, void *data)
21162116
opt->offset_level = ANNOTATION__MAX_OFFSET_LEVEL;
21172117
else if (opt->offset_level < ANNOTATION__MIN_OFFSET_LEVEL)
21182118
opt->offset_level = ANNOTATION__MIN_OFFSET_LEVEL;
2119+
} else if (!strcmp(var, "annotate.disassemblers")) {
2120+
opt->disassemblers_str = strdup(value);
2121+
if (!opt->disassemblers_str) {
2122+
pr_err("Not enough memory for annotate.disassemblers\n");
2123+
return -1;
2124+
}
21192125
} else if (!strcmp(var, "annotate.hide_src_code")) {
21202126
opt->hide_src_code = perf_config_bool("hide_src_code", value);
21212127
} else if (!strcmp(var, "annotate.jump_arrows")) {

tools/perf/util/annotate.h

+6
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ struct annotated_data_type;
3434
#define ANNOTATION__BR_CNTR_WIDTH 30
3535
#define ANNOTATION_DUMMY_LEN 256
3636

37+
// llvm, capstone, objdump
38+
#define MAX_DISASSEMBLERS 3
39+
3740
struct annotation_options {
3841
bool hide_src_code,
3942
use_offset,
@@ -49,11 +52,14 @@ struct annotation_options {
4952
annotate_src,
5053
full_addr;
5154
u8 offset_level;
55+
u8 nr_disassemblers;
5256
int min_pcnt;
5357
int max_lines;
5458
int context;
5559
char *objdump_path;
5660
char *disassembler_style;
61+
const char *disassemblers_str;
62+
const char *disassemblers[MAX_DISASSEMBLERS];
5763
const char *prefix;
5864
const char *prefix_strip;
5965
unsigned int percent_type;

tools/perf/util/disasm.c

+71-6
Original file line numberDiff line numberDiff line change
@@ -2213,13 +2213,65 @@ static int symbol__disassemble_objdump(const char *filename, struct symbol *sym,
22132213
return err;
22142214
}
22152215

2216+
static int annotation_options__init_disassemblers(struct annotation_options *options)
2217+
{
2218+
char *disassembler;
2219+
2220+
if (options->disassemblers_str == NULL) {
2221+
const char *default_disassemblers_str =
2222+
#ifdef HAVE_LIBLLVM_SUPPORT
2223+
"llvm,"
2224+
#endif
2225+
#ifdef HAVE_LIBCAPSTONE_SUPPORT
2226+
"capstone,"
2227+
#endif
2228+
"objdump";
2229+
2230+
options->disassemblers_str = strdup(default_disassemblers_str);
2231+
if (!options->disassemblers_str)
2232+
goto out_enomem;
2233+
}
2234+
2235+
disassembler = strdup(options->disassemblers_str);
2236+
if (disassembler == NULL)
2237+
goto out_enomem;
2238+
2239+
while (1) {
2240+
char *comma = strchr(disassembler, ',');
2241+
2242+
if (comma != NULL)
2243+
*comma = '\0';
2244+
2245+
options->disassemblers[options->nr_disassemblers++] = strim(disassembler);
2246+
2247+
if (comma == NULL)
2248+
break;
2249+
2250+
disassembler = comma + 1;
2251+
2252+
if (options->nr_disassemblers >= MAX_DISASSEMBLERS) {
2253+
pr_debug("annotate.disassemblers can have at most %d entries, ignoring \"%s\"\n",
2254+
MAX_DISASSEMBLERS, disassembler);
2255+
break;
2256+
}
2257+
}
2258+
2259+
return 0;
2260+
2261+
out_enomem:
2262+
pr_err("Not enough memory for annotate.disassemblers\n");
2263+
return -1;
2264+
}
2265+
22162266
int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
22172267
{
2268+
struct annotation_options *options = args->options;
22182269
struct map *map = args->ms.map;
22192270
struct dso *dso = map__dso(map);
22202271
char symfs_filename[PATH_MAX];
22212272
bool delete_extract = false;
22222273
struct kcore_extract kce;
2274+
const char *disassembler;
22232275
bool decomp = false;
22242276
int err = dso__disassemble_filename(dso, symfs_filename, sizeof(symfs_filename));
22252277

@@ -2279,16 +2331,29 @@ int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
22792331
}
22802332
}
22812333

2282-
err = symbol__disassemble_llvm(symfs_filename, sym, args);
2283-
if (err == 0)
2334+
err = annotation_options__init_disassemblers(options);
2335+
if (err)
22842336
goto out_remove_tmp;
22852337

2286-
err = symbol__disassemble_capstone(symfs_filename, sym, args);
2287-
if (err == 0)
2288-
goto out_remove_tmp;
2338+
err = -1;
22892339

2290-
err = symbol__disassemble_objdump(symfs_filename, sym, args);
2340+
for (int i = 0; i < options->nr_disassemblers && err != 0; ++i) {
2341+
disassembler = options->disassemblers[i];
22912342

2343+
if (!strcmp(disassembler, "llvm"))
2344+
err = symbol__disassemble_llvm(symfs_filename, sym, args);
2345+
else if (!strcmp(disassembler, "capstone"))
2346+
err = symbol__disassemble_capstone(symfs_filename, sym, args);
2347+
else if (!strcmp(disassembler, "objdump"))
2348+
err = symbol__disassemble_objdump(symfs_filename, sym, args);
2349+
else
2350+
pr_debug("Unknown disassembler %s, skipping...\n", disassembler);
2351+
}
2352+
2353+
if (err == 0) {
2354+
pr_debug("Disassembled with %s\nannotate.disassemblers=%s\n",
2355+
disassembler, options->disassemblers_str);
2356+
}
22922357
out_remove_tmp:
22932358
if (decomp)
22942359
unlink(symfs_filename);

0 commit comments

Comments
 (0)