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>
This commit is contained in:
Namhyung Kim 2025-06-25 16:03:39 -07:00
parent 8e63fd1e00
commit e201757f7a
4 changed files with 93 additions and 3 deletions

View File

@ -345,6 +345,23 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser,
browser->curr_hot = rb_last(&browser->entries);
}
static struct annotation_line *annotate_browser__find_new_asm_line(
struct annotate_browser *browser,
int idx_asm)
{
struct annotation_line *al;
struct list_head *head = browser->b.entries;
/* find an annotation line in the new list with the same idx_asm */
list_for_each_entry(al, head, node) {
if (al->idx_asm == idx_asm)
return al;
}
/* There are no asm lines */
return NULL;
}
static struct annotation_line *annotate_browser__find_next_asm_line(
struct annotate_browser *browser,
struct annotation_line *al)
@ -368,7 +385,31 @@ static struct annotation_line *annotate_browser__find_next_asm_line(
return NULL;
}
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
static bool annotation__has_source(struct annotation *notes)
{
struct annotation_line *al;
bool found_asm = false;
/* Let's skip the first non-asm lines which present regardless of source. */
list_for_each_entry(al, &notes->src->source, node) {
if (al->offset >= 0) {
found_asm = true;
break;
}
}
if (found_asm) {
/* After assembly lines, any line without offset means source. */
list_for_each_entry_continue(al, &notes->src->source, node) {
if (al->offset == -1)
return true;
}
}
return false;
}
static bool annotate_browser__toggle_source(struct annotate_browser *browser,
struct evsel *evsel)
{
struct annotation *notes = browser__annotation(&browser->b);
struct annotation_line *al;
@ -377,6 +418,39 @@ static bool annotate_browser__toggle_source(struct annotate_browser *browser)
browser->b.seek(&browser->b, offset, SEEK_CUR);
al = list_entry(browser->b.top, struct annotation_line, node);
if (!annotate_opts.annotate_src)
annotate_opts.annotate_src = true;
/*
* It's about to get source code annotation for the first time.
* Drop the existing annotation_lines and get the new one with source.
* And then move to the original line at the same asm index.
*/
if (annotate_opts.hide_src_code && !notes->src->tried_source) {
struct map_symbol *ms = browser->b.priv;
int orig_idx_asm = al->idx_asm;
/* annotate again with source code info */
annotate_opts.hide_src_code = false;
annotated_source__purge(notes->src);
symbol__annotate2(ms, evsel, &browser->arch);
annotate_opts.hide_src_code = true;
/* should be after annotated_source__purge() */
notes->src->tried_source = true;
if (!annotation__has_source(notes))
ui__warning("Annotation has no source code.");
browser->b.entries = &notes->src->source;
al = annotate_browser__find_new_asm_line(browser, orig_idx_asm);
if (unlikely(al == NULL)) {
al = list_first_entry(&notes->src->source,
struct annotation_line, node);
}
browser->b.seek(&browser->b, al->idx_asm, SEEK_SET);
}
if (annotate_opts.hide_src_code) {
if (al->idx_asm < offset)
offset = al->idx;
@ -833,7 +907,7 @@ static int annotate_browser__run(struct annotate_browser *browser,
nd = browser->curr_hot;
break;
case 's':
if (annotate_browser__toggle_source(browser))
if (annotate_browser__toggle_source(browser, evsel))
ui_helpline__puts(help);
annotate__scnprintf_title(hists, title, sizeof(title));
annotate_browser__show(&browser->b, title, help);
@ -1011,6 +1085,12 @@ int symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel,
ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
return -1;
}
if (!annotate_opts.hide_src_code) {
notes->src->tried_source = true;
if (!annotation__has_source(notes))
ui__warning("Annotation has no source code.");
}
}
ui_helpline__push("Press ESC to exit");
@ -1025,7 +1105,7 @@ int symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel,
ret = annotate_browser__run(&browser, evsel, hbt);
if(not_annotated)
if (not_annotated && !notes->src->tried_source)
annotated_source__purge(notes->src);
return ret;

View File

@ -1451,6 +1451,7 @@ void annotated_source__purge(struct annotated_source *as)
list_del_init(&al->node);
disasm_line__free(disasm_line(al));
}
as->tried_source = false;
}
static size_t disasm_line__fprintf(struct disasm_line *dl, FILE *fp)
@ -2280,6 +2281,7 @@ void annotation_options__init(void)
opt->annotate_src = true;
opt->offset_level = ANNOTATION__OFFSET_JUMP_TARGETS;
opt->percent_type = PERCENT_PERIOD_LOCAL;
opt->hide_src_code = true;
opt->hide_src_code_on_title = true;
}

View File

@ -294,6 +294,7 @@ struct annotated_source {
int nr_entries;
int nr_asm_entries;
int max_jump_sources;
bool tried_source;
u64 start;
struct {
u8 addr;

View File

@ -2284,6 +2284,13 @@ int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
}
}
/* FIXME: LLVM and CAPSTONE should support source code */
if (options->annotate_src && !options->hide_src_code) {
err = symbol__disassemble_objdump(symfs_filename, sym, args);
if (err == 0)
goto out_remove_tmp;
}
err = -1;
for (u8 i = 0; i < ARRAY_SIZE(options->disassemblers) && err != 0; i++) {
enum perf_disassembler dis = options->disassemblers[i];