Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf annotate: Fix source code annotate with objdump

Recently it uses llvm and capstone to speed up annotation or disassembly
of instructions. But they don't support source code view yet. Until it
fixed, we can force to use objdump for source code annotation.

To prevent performance loss, it's disabled by default and turned it on
when user requests it in TUI by pressing 's' key.

Acked-by: Ian Rogers <irogers@google.com>
Link: https://lore.kernel.org/r/20250625230339.702610-1-namhyung@kernel.org
Reported-by: Ingo Molnar <mingo@kernel.org>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>

+93 -3
+83 -3
tools/perf/ui/browsers/annotate.c
··· 345 345 browser->curr_hot = rb_last(&browser->entries); 346 346 } 347 347 348 + static struct annotation_line *annotate_browser__find_new_asm_line( 349 + struct annotate_browser *browser, 350 + int idx_asm) 351 + { 352 + struct annotation_line *al; 353 + struct list_head *head = browser->b.entries; 354 + 355 + /* find an annotation line in the new list with the same idx_asm */ 356 + list_for_each_entry(al, head, node) { 357 + if (al->idx_asm == idx_asm) 358 + return al; 359 + } 360 + 361 + /* There are no asm lines */ 362 + return NULL; 363 + } 364 + 348 365 static struct annotation_line *annotate_browser__find_next_asm_line( 349 366 struct annotate_browser *browser, 350 367 struct annotation_line *al) ··· 385 368 return NULL; 386 369 } 387 370 388 - static bool annotate_browser__toggle_source(struct annotate_browser *browser) 371 + static bool annotation__has_source(struct annotation *notes) 372 + { 373 + struct annotation_line *al; 374 + bool found_asm = false; 375 + 376 + /* Let's skip the first non-asm lines which present regardless of source. */ 377 + list_for_each_entry(al, &notes->src->source, node) { 378 + if (al->offset >= 0) { 379 + found_asm = true; 380 + break; 381 + } 382 + } 383 + 384 + if (found_asm) { 385 + /* After assembly lines, any line without offset means source. */ 386 + list_for_each_entry_continue(al, &notes->src->source, node) { 387 + if (al->offset == -1) 388 + return true; 389 + } 390 + } 391 + return false; 392 + } 393 + 394 + static bool annotate_browser__toggle_source(struct annotate_browser *browser, 395 + struct evsel *evsel) 389 396 { 390 397 struct annotation *notes = browser__annotation(&browser->b); 391 398 struct annotation_line *al; ··· 417 376 418 377 browser->b.seek(&browser->b, offset, SEEK_CUR); 419 378 al = list_entry(browser->b.top, struct annotation_line, node); 379 + 380 + if (!annotate_opts.annotate_src) 381 + annotate_opts.annotate_src = true; 382 + 383 + /* 384 + * It's about to get source code annotation for the first time. 385 + * Drop the existing annotation_lines and get the new one with source. 386 + * And then move to the original line at the same asm index. 387 + */ 388 + if (annotate_opts.hide_src_code && !notes->src->tried_source) { 389 + struct map_symbol *ms = browser->b.priv; 390 + int orig_idx_asm = al->idx_asm; 391 + 392 + /* annotate again with source code info */ 393 + annotate_opts.hide_src_code = false; 394 + annotated_source__purge(notes->src); 395 + symbol__annotate2(ms, evsel, &browser->arch); 396 + annotate_opts.hide_src_code = true; 397 + 398 + /* should be after annotated_source__purge() */ 399 + notes->src->tried_source = true; 400 + 401 + if (!annotation__has_source(notes)) 402 + ui__warning("Annotation has no source code."); 403 + 404 + browser->b.entries = &notes->src->source; 405 + al = annotate_browser__find_new_asm_line(browser, orig_idx_asm); 406 + if (unlikely(al == NULL)) { 407 + al = list_first_entry(&notes->src->source, 408 + struct annotation_line, node); 409 + } 410 + browser->b.seek(&browser->b, al->idx_asm, SEEK_SET); 411 + } 420 412 421 413 if (annotate_opts.hide_src_code) { 422 414 if (al->idx_asm < offset) ··· 907 833 nd = browser->curr_hot; 908 834 break; 909 835 case 's': 910 - if (annotate_browser__toggle_source(browser)) 836 + if (annotate_browser__toggle_source(browser, evsel)) 911 837 ui_helpline__puts(help); 912 838 annotate__scnprintf_title(hists, title, sizeof(title)); 913 839 annotate_browser__show(&browser->b, title, help); ··· 1085 1011 ui__error("Couldn't annotate %s:\n%s", sym->name, msg); 1086 1012 return -1; 1087 1013 } 1014 + 1015 + if (!annotate_opts.hide_src_code) { 1016 + notes->src->tried_source = true; 1017 + if (!annotation__has_source(notes)) 1018 + ui__warning("Annotation has no source code."); 1019 + } 1088 1020 } 1089 1021 1090 1022 ui_helpline__push("Press ESC to exit"); ··· 1105 1025 1106 1026 ret = annotate_browser__run(&browser, evsel, hbt); 1107 1027 1108 - if(not_annotated) 1028 + if (not_annotated && !notes->src->tried_source) 1109 1029 annotated_source__purge(notes->src); 1110 1030 1111 1031 return ret;
+2
tools/perf/util/annotate.c
··· 1451 1451 list_del_init(&al->node); 1452 1452 disasm_line__free(disasm_line(al)); 1453 1453 } 1454 + as->tried_source = false; 1454 1455 } 1455 1456 1456 1457 static size_t disasm_line__fprintf(struct disasm_line *dl, FILE *fp) ··· 2281 2280 opt->annotate_src = true; 2282 2281 opt->offset_level = ANNOTATION__OFFSET_JUMP_TARGETS; 2283 2282 opt->percent_type = PERCENT_PERIOD_LOCAL; 2283 + opt->hide_src_code = true; 2284 2284 opt->hide_src_code_on_title = true; 2285 2285 } 2286 2286
+1
tools/perf/util/annotate.h
··· 294 294 int nr_entries; 295 295 int nr_asm_entries; 296 296 int max_jump_sources; 297 + bool tried_source; 297 298 u64 start; 298 299 struct { 299 300 u8 addr;
+7
tools/perf/util/disasm.c
··· 2284 2284 } 2285 2285 } 2286 2286 2287 + /* FIXME: LLVM and CAPSTONE should support source code */ 2288 + if (options->annotate_src && !options->hide_src_code) { 2289 + err = symbol__disassemble_objdump(symfs_filename, sym, args); 2290 + if (err == 0) 2291 + goto out_remove_tmp; 2292 + } 2293 + 2287 2294 err = -1; 2288 2295 for (u8 i = 0; i < ARRAY_SIZE(options->disassemblers) && err != 0; i++) { 2289 2296 enum perf_disassembler dis = options->disassemblers[i];