diff --git a/tools/perf/Documentation/perf-annotate.txt b/tools/perf/Documentation/perf-annotate.txt index 292809c3c0ca..749cc6055dac 100644 --- a/tools/perf/Documentation/perf-annotate.txt +++ b/tools/perf/Documentation/perf-annotate.txt @@ -55,6 +55,9 @@ OPTIONS --vmlinux=:: vmlinux pathname. +--ignore-vmlinux:: + Ignore vmlinux files. + -m:: --modules:: Load module symbols. WARNING: use only with -k and LIVE kernel. @@ -69,6 +72,8 @@ OPTIONS --stdio:: Use the stdio interface. +--stdio2:: Use the stdio2 interface, non-interactive, uses the TUI formatting. + --stdio-color=:: 'always', 'never' or 'auto', allowing configuring color output via the command line, in addition to via "color.ui" .perfconfig. diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index cba16d8a970e..e1a660e60849 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt @@ -296,6 +296,9 @@ OPTIONS --vmlinux=:: vmlinux pathname +--ignore-vmlinux:: + Ignore vmlinux files. + --kallsyms=:: kallsyms pathname diff --git a/tools/perf/arch/s390/annotate/instructions.c b/tools/perf/arch/s390/annotate/instructions.c index 46c21831f2ac..cee4e2f7c057 100644 --- a/tools/perf/arch/s390/annotate/instructions.c +++ b/tools/perf/arch/s390/annotate/instructions.c @@ -2,9 +2,10 @@ #include static int s390_call__parse(struct arch *arch, struct ins_operands *ops, - struct map *map) + struct map_symbol *ms) { char *endptr, *tok, *name; + struct map *map = ms->map; struct addr_map_symbol target = { .map = map, }; @@ -54,7 +55,7 @@ static struct ins_ops s390_call_ops = { static int s390_mov__parse(struct arch *arch __maybe_unused, struct ins_operands *ops, - struct map *map __maybe_unused) + struct map_symbol *ms __maybe_unused) { char *s = strchr(ops->raw, ','), *target, *endptr; diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index ead6ae4549e5..51709a961496 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -40,7 +40,7 @@ struct perf_annotate { struct perf_tool tool; struct perf_session *session; - bool use_tui, use_stdio, use_gtk; + bool use_tui, use_stdio, use_stdio2, use_gtk; bool full_paths; bool print_line; bool skip_missing; @@ -202,6 +202,11 @@ static int process_branch_callback(struct perf_evsel *evsel, return ret; } +static bool has_annotation(struct perf_annotate *ann) +{ + return ui__has_annotation() || ann->use_stdio2; +} + static int perf_evsel__add_sample(struct perf_evsel *evsel, struct perf_sample *sample, struct addr_location *al, @@ -212,7 +217,7 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel, struct hist_entry *he; int ret; - if ((!ann->has_br_stack || !ui__has_annotation()) && + if ((!ann->has_br_stack || !has_annotation(ann)) && ann->sym_hist_filter != NULL && (al->sym == NULL || strcmp(ann->sym_hist_filter, al->sym->name) != 0)) { @@ -236,7 +241,7 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel, */ process_branch_stack(sample->branch_stack, al, sample); - if (ann->has_br_stack && ui__has_annotation()) + if (ann->has_br_stack && has_annotation(ann)) return process_branch_callback(evsel, sample, al, ann, machine); he = hists__add_entry(hists, al, NULL, NULL, NULL, sample, true); @@ -282,8 +287,11 @@ static int hist_entry__tty_annotate(struct hist_entry *he, struct perf_evsel *evsel, struct perf_annotate *ann) { - return symbol__tty_annotate(he->ms.sym, he->ms.map, evsel, - ann->print_line, ann->full_paths, 0, 0); + if (!ann->use_stdio2) + return symbol__tty_annotate(he->ms.sym, he->ms.map, evsel, + ann->print_line, ann->full_paths, 0, 0); + return symbol__tty_annotate2(he->ms.sym, he->ms.map, evsel, + ann->print_line, ann->full_paths); } static void hists__find_annotations(struct hists *hists, @@ -487,6 +495,9 @@ int cmd_annotate(int argc, const char **argv) OPT_BOOLEAN(0, "gtk", &annotate.use_gtk, "Use the GTK interface"), OPT_BOOLEAN(0, "tui", &annotate.use_tui, "Use the TUI interface"), OPT_BOOLEAN(0, "stdio", &annotate.use_stdio, "Use the stdio interface"), + OPT_BOOLEAN(0, "stdio2", &annotate.use_stdio2, "Use the stdio interface"), + OPT_BOOLEAN(0, "ignore-vmlinux", &symbol_conf.ignore_vmlinux, + "don't load vmlinux even if found"), OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, "file", "vmlinux pathname"), OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules, @@ -563,13 +574,15 @@ int cmd_annotate(int argc, const char **argv) if (ret < 0) goto out_delete; + annotation_config__init(); + symbol_conf.try_vmlinux_path = true; ret = symbol__init(&annotate.session->header.env); if (ret < 0) goto out_delete; - if (annotate.use_stdio) + if (annotate.use_stdio || annotate.use_stdio2) use_browser = 0; else if (annotate.use_tui) use_browser = 1; @@ -578,7 +591,7 @@ int cmd_annotate(int argc, const char **argv) setup_browser(true); - if (use_browser == 1 && annotate.has_br_stack) { + if ((use_browser == 1 || annotate.use_stdio2) && annotate.has_br_stack) { sort__mode = SORT_MODE__BRANCH; if (setup_sorting(annotate.session->evlist) < 0) usage_with_options(annotate_usage, options); diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 91da12975642..0f198f6d9b77 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -1018,6 +1018,8 @@ int cmd_report(int argc, const char **argv) OPT_BOOLEAN(0, "mmaps", &report.mmaps_mode, "Display recorded tasks memory maps"), OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, "file", "vmlinux pathname"), + OPT_BOOLEAN(0, "ignore-vmlinux", &symbol_conf.ignore_vmlinux, + "don't load vmlinux even if found"), OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, "file", "kallsyms pathname"), OPT_BOOLEAN('f', "force", &symbol_conf.force, "don't complain, do it"), @@ -1340,6 +1342,7 @@ repeat: symbol_conf.priv_size += sizeof(u32); symbol_conf.sort_by_name = true; } + annotation_config__init(); } if (symbol__init(&session->header.env) < 0) diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 113c298ed38b..f39bd60d2708 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -1493,6 +1493,8 @@ int cmd_top(int argc, const char **argv) if (status < 0) goto out_delete_evlist; + annotation_config__init(); + symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); if (symbol__init(NULL) < 0) return -1; diff --git a/tools/perf/ui/browser.c b/tools/perf/ui/browser.c index 63399af3049f..9f6ce29b83b4 100644 --- a/tools/perf/ui/browser.c +++ b/tools/perf/ui/browser.c @@ -56,12 +56,17 @@ void ui_browser__write_nstring(struct ui_browser *browser __maybe_unused, const slsmg_write_nstring(msg, width); } +void ui_browser__vprintf(struct ui_browser *browser __maybe_unused, const char *fmt, va_list args) +{ + slsmg_vprintf(fmt, args); +} + void ui_browser__printf(struct ui_browser *browser __maybe_unused, const char *fmt, ...) { va_list args; va_start(args, fmt); - slsmg_vprintf(fmt, args); + ui_browser__vprintf(browser, fmt, args); va_end(args); } @@ -779,6 +784,4 @@ void ui_browser__init(void) struct ui_browser_colorset *c = &ui_browser__colorsets[i++]; sltt_set_color(c->colorset, c->name, c->fg, c->bg); } - - annotate_browser__init(); } diff --git a/tools/perf/ui/browser.h b/tools/perf/ui/browser.h index 03e1734412b9..70057178ee34 100644 --- a/tools/perf/ui/browser.h +++ b/tools/perf/ui/browser.h @@ -3,6 +3,7 @@ #define _PERF_UI_BROWSER_H_ 1 #include +#include #define HE_COLORSET_TOP 50 #define HE_COLORSET_MEDIUM 51 @@ -40,6 +41,7 @@ void ui_browser__reset_index(struct ui_browser *browser); void ui_browser__gotorc(struct ui_browser *browser, int y, int x); void ui_browser__write_nstring(struct ui_browser *browser, const char *msg, unsigned int width); +void ui_browser__vprintf(struct ui_browser *browser, const char *fmt, va_list args); void ui_browser__printf(struct ui_browser *browser, const char *fmt, ...); void ui_browser__write_graph(struct ui_browser *browser, int graph); void __ui_browser__line_arrow(struct ui_browser *browser, unsigned int column, @@ -77,5 +79,4 @@ void ui_browser__list_head_seek(struct ui_browser *browser, off_t offset, int wh unsigned int ui_browser__list_head_refresh(struct ui_browser *browser); void ui_browser__init(void); -void annotate_browser__init(void); #endif /* _PERF_UI_BROWSER_H_ */ diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index 618edf96353c..c02fb437ac8e 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c @@ -9,7 +9,6 @@ #include "../../util/sort.h" #include "../../util/symbol.h" #include "../../util/evsel.h" -#include "../../util/config.h" #include "../../util/evlist.h" #include #include @@ -22,28 +21,6 @@ struct disasm_line_samples { struct sym_hist_entry he; }; -#define IPC_WIDTH 6 -#define CYCLES_WIDTH 6 - -struct browser_line { - u32 idx; - int idx_asm; - int jump_sources; -}; - -static struct annotate_browser_opt { - bool hide_src_code, - use_offset, - jump_arrows, - show_linenr, - show_nr_jumps, - show_nr_samples, - show_total_period; -} annotate_browser__opts = { - .use_offset = true, - .jump_arrows = true, -}; - struct arch; struct annotate_browser { @@ -51,245 +28,98 @@ struct annotate_browser { struct rb_root entries; struct rb_node *curr_hot; struct annotation_line *selection; - struct annotation_line **offsets; struct arch *arch; - int nr_events; - u64 start; - int nr_asm_entries; - int nr_entries; - int max_jump_sources; - int nr_jumps; bool searching_backwards; - bool have_cycles; - u8 addr_width; - u8 jumps_width; - u8 target_width; - u8 min_addr_width; - u8 max_addr_width; char search_bf[128]; }; -static inline struct browser_line *browser_line(struct annotation_line *al) +static inline struct annotation *browser__annotation(struct ui_browser *browser) { - void *ptr = al; - - ptr = container_of(al, struct disasm_line, al); - return ptr - sizeof(struct browser_line); + struct map_symbol *ms = browser->priv; + return symbol__annotation(ms->sym); } -static bool disasm_line__filter(struct ui_browser *browser __maybe_unused, - void *entry) +static bool disasm_line__filter(struct ui_browser *browser, void *entry) { - if (annotate_browser__opts.hide_src_code) { - struct annotation_line *al = list_entry(entry, struct annotation_line, node); - - return al->offset == -1; - } - - return false; + struct annotation *notes = browser__annotation(browser); + struct annotation_line *al = list_entry(entry, struct annotation_line, node); + return annotation_line__filter(al, notes); } -static int annotate_browser__jumps_percent_color(struct annotate_browser *browser, - int nr, bool current) +static int ui_browser__jumps_percent_color(struct ui_browser *browser, int nr, bool current) { - if (current && (!browser->b.use_navkeypressed || browser->b.navkeypressed)) + struct annotation *notes = browser__annotation(browser); + + if (current && (!browser->use_navkeypressed || browser->navkeypressed)) return HE_COLORSET_SELECTED; - if (nr == browser->max_jump_sources) + if (nr == notes->max_jump_sources) return HE_COLORSET_TOP; if (nr > 1) return HE_COLORSET_MEDIUM; return HE_COLORSET_NORMAL; } -static int annotate_browser__set_jumps_percent_color(struct annotate_browser *browser, - int nr, bool current) +static int ui_browser__set_jumps_percent_color(void *browser, int nr, bool current) { - int color = annotate_browser__jumps_percent_color(browser, nr, current); - return ui_browser__set_color(&browser->b, color); + int color = ui_browser__jumps_percent_color(browser, nr, current); + return ui_browser__set_color(browser, color); } -static int annotate_browser__pcnt_width(struct annotate_browser *ab) +static int annotate_browser__set_color(void *browser, int color) { - return (annotate_browser__opts.show_total_period ? 12 : 7) * ab->nr_events; + return ui_browser__set_color(browser, color); } -static int annotate_browser__cycles_width(struct annotate_browser *ab) +static void annotate_browser__write_graph(void *browser, int graph) { - return ab->have_cycles ? IPC_WIDTH + CYCLES_WIDTH : 0; + ui_browser__write_graph(browser, graph); } -static void disasm_line__write(struct disasm_line *dl, struct ui_browser *browser, - char *bf, size_t size) +static void annotate_browser__set_percent_color(void *browser, double percent, bool current) { - if (dl->ins.ops && dl->ins.ops->scnprintf) { - if (ins__is_jump(&dl->ins)) { - bool fwd = dl->ops.target.offset > dl->al.offset; + ui_browser__set_percent_color(browser, percent, current); +} - ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR : - SLSMG_UARROW_CHAR); - SLsmg_write_char(' '); - } else if (ins__is_call(&dl->ins)) { - ui_browser__write_graph(browser, SLSMG_RARROW_CHAR); - SLsmg_write_char(' '); - } else if (ins__is_ret(&dl->ins)) { - ui_browser__write_graph(browser, SLSMG_LARROW_CHAR); - SLsmg_write_char(' '); - } else { - ui_browser__write_nstring(browser, " ", 2); - } - } else { - ui_browser__write_nstring(browser, " ", 2); - } +static void annotate_browser__printf(void *browser, const char *fmt, ...) +{ + va_list args; - disasm_line__scnprintf(dl, bf, size, !annotate_browser__opts.use_offset); + va_start(args, fmt); + ui_browser__vprintf(browser, fmt, args); + va_end(args); } static void annotate_browser__write(struct ui_browser *browser, void *entry, int row) { struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); + struct annotation *notes = browser__annotation(browser); struct annotation_line *al = list_entry(entry, struct annotation_line, node); - struct browser_line *bl = browser_line(al); - bool current_entry = ui_browser__is_current_entry(browser, row); - bool change_color = (!annotate_browser__opts.hide_src_code && - (!current_entry || (browser->use_navkeypressed && - !browser->navkeypressed))); - int width = browser->width, printed; - int i, pcnt_width = annotate_browser__pcnt_width(ab), - cycles_width = annotate_browser__cycles_width(ab); - double percent_max = 0.0; - char bf[256]; - bool show_title = false; - - for (i = 0; i < ab->nr_events; i++) { - if (al->samples[i].percent > percent_max) - percent_max = al->samples[i].percent; - } - - if ((row == 0) && (al->offset == -1 || percent_max == 0.0)) { - if (ab->have_cycles) { - if (al->ipc == 0.0 && al->cycles == 0) - show_title = true; - } else - show_title = true; - } - - if (al->offset != -1 && percent_max != 0.0) { - for (i = 0; i < ab->nr_events; i++) { - ui_browser__set_percent_color(browser, - al->samples[i].percent, - current_entry); - if (annotate_browser__opts.show_total_period) { - ui_browser__printf(browser, "%11" PRIu64 " ", - al->samples[i].he.period); - } else if (annotate_browser__opts.show_nr_samples) { - ui_browser__printf(browser, "%6" PRIu64 " ", - al->samples[i].he.nr_samples); - } else { - ui_browser__printf(browser, "%6.2f ", - al->samples[i].percent); - } - } - } else { - ui_browser__set_percent_color(browser, 0, current_entry); - - if (!show_title) - ui_browser__write_nstring(browser, " ", pcnt_width); - else { - ui_browser__printf(browser, "%*s", pcnt_width, - annotate_browser__opts.show_total_period ? "Period" : - annotate_browser__opts.show_nr_samples ? "Samples" : "Percent"); - } - } - if (ab->have_cycles) { - if (al->ipc) - ui_browser__printf(browser, "%*.2f ", IPC_WIDTH - 1, al->ipc); - else if (!show_title) - ui_browser__write_nstring(browser, " ", IPC_WIDTH); - else - ui_browser__printf(browser, "%*s ", IPC_WIDTH - 1, "IPC"); - - if (al->cycles) - ui_browser__printf(browser, "%*" PRIu64 " ", - CYCLES_WIDTH - 1, al->cycles); - else if (!show_title) - ui_browser__write_nstring(browser, " ", CYCLES_WIDTH); - else - ui_browser__printf(browser, "%*s ", CYCLES_WIDTH - 1, "Cycle"); - } - - SLsmg_write_char(' '); + struct annotation_write_ops ops = { + .first_line = row == 0, + .current_entry = ui_browser__is_current_entry(browser, row), + .change_color = (!notes->options->hide_src_code && + (!ops.current_entry || + (browser->use_navkeypressed && + !browser->navkeypressed))), + .width = browser->width, + .obj = browser, + .set_color = annotate_browser__set_color, + .set_percent_color = annotate_browser__set_percent_color, + .set_jumps_percent_color = ui_browser__set_jumps_percent_color, + .printf = annotate_browser__printf, + .write_graph = annotate_browser__write_graph, + }; /* The scroll bar isn't being used */ if (!browser->navkeypressed) - width += 1; + ops.width += 1; - if (!*al->line) - ui_browser__write_nstring(browser, " ", width - pcnt_width - cycles_width); - else if (al->offset == -1) { - if (al->line_nr && annotate_browser__opts.show_linenr) - printed = scnprintf(bf, sizeof(bf), "%-*d ", - ab->addr_width + 1, al->line_nr); - else - printed = scnprintf(bf, sizeof(bf), "%*s ", - ab->addr_width, " "); - ui_browser__write_nstring(browser, bf, printed); - ui_browser__write_nstring(browser, al->line, width - printed - pcnt_width - cycles_width + 1); - } else { - u64 addr = al->offset; - int color = -1; + annotation_line__write(al, notes, &ops); - if (!annotate_browser__opts.use_offset) - addr += ab->start; - - if (!annotate_browser__opts.use_offset) { - printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr); - } else { - if (bl->jump_sources) { - if (annotate_browser__opts.show_nr_jumps) { - int prev; - printed = scnprintf(bf, sizeof(bf), "%*d ", - ab->jumps_width, - bl->jump_sources); - prev = annotate_browser__set_jumps_percent_color(ab, bl->jump_sources, - current_entry); - ui_browser__write_nstring(browser, bf, printed); - ui_browser__set_color(browser, prev); - } - - printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ", - ab->target_width, addr); - } else { - printed = scnprintf(bf, sizeof(bf), "%*s ", - ab->addr_width, " "); - } - } - - if (change_color) - color = ui_browser__set_color(browser, HE_COLORSET_ADDR); - ui_browser__write_nstring(browser, bf, printed); - if (change_color) - ui_browser__set_color(browser, color); - - disasm_line__write(disasm_line(al), browser, bf, sizeof(bf)); - - ui_browser__write_nstring(browser, bf, width - pcnt_width - cycles_width - 3 - printed); - } - - if (current_entry) + if (ops.current_entry) ab->selection = al; } -static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym) -{ - if (!dl || !dl->ins.ops || !ins__is_jump(&dl->ins) - || !disasm_line__has_offset(dl) - || dl->ops.target.offset < 0 - || dl->ops.target.offset >= (s64)symbol__size(sym)) - return false; - - return true; -} - static bool is_fused(struct annotate_browser *ab, struct disasm_line *cursor) { struct disasm_line *pos = list_prev_entry(cursor, al.node); @@ -314,18 +144,18 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser) struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); struct disasm_line *cursor = disasm_line(ab->selection); struct annotation_line *target; - struct browser_line *btarget, *bcursor; unsigned int from, to; struct map_symbol *ms = ab->b.priv; struct symbol *sym = ms->sym; - u8 pcnt_width = annotate_browser__pcnt_width(ab); - int width = 0; + struct annotation *notes = symbol__annotation(sym); + u8 pcnt_width = annotation__pcnt_width(notes); + int width; /* PLT symbols contain external offsets */ if (strstr(sym->name, "@plt")) return; - if (!disasm_line__is_valid_jump(cursor, sym)) + if (!disasm_line__is_valid_local_jump(cursor, sym)) return; /* @@ -348,35 +178,31 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser) * name right after the '<' token and probably treating this like a * 'call' instruction. */ - target = ab->offsets[cursor->ops.target.offset]; + target = notes->offsets[cursor->ops.target.offset]; if (target == NULL) { - ui_helpline__printf("WARN: jump target inconsistency, press 'o', ab->offsets[%#x] = NULL\n", + ui_helpline__printf("WARN: jump target inconsistency, press 'o', notes->offsets[%#x] = NULL\n", cursor->ops.target.offset); return; } - bcursor = browser_line(&cursor->al); - btarget = browser_line(target); - - if (annotate_browser__opts.hide_src_code) { - from = bcursor->idx_asm; - to = btarget->idx_asm; + if (notes->options->hide_src_code) { + from = cursor->al.idx_asm; + to = target->idx_asm; } else { - from = (u64)bcursor->idx; - to = (u64)btarget->idx; + from = (u64)cursor->al.idx; + to = (u64)target->idx; } - if (ab->have_cycles) - width = IPC_WIDTH + CYCLES_WIDTH; + width = annotation__cycles_width(notes); ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS); __ui_browser__line_arrow(browser, - pcnt_width + 2 + ab->addr_width + width, + pcnt_width + 2 + notes->widths.addr + width, from, to); if (is_fused(ab, cursor)) { ui_browser__mark_fused(browser, - pcnt_width + 3 + ab->addr_width + width, + pcnt_width + 3 + notes->widths.addr + width, from - 1, to > from ? true : false); } @@ -384,11 +210,11 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser) static unsigned int annotate_browser__refresh(struct ui_browser *browser) { - struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); + struct annotation *notes = browser__annotation(browser); int ret = ui_browser__list_head_refresh(browser); - int pcnt_width = annotate_browser__pcnt_width(ab); + int pcnt_width = annotation__pcnt_width(notes); - if (annotate_browser__opts.jump_arrows) + if (notes->options->jump_arrows) annotate_browser__draw_current_jump(browser); ui_browser__set_color(browser, HE_COLORSET_NORMAL); @@ -430,6 +256,7 @@ static void disasm_rb_tree__insert(struct rb_root *root, struct annotation_line static void annotate_browser__set_top(struct annotate_browser *browser, struct annotation_line *pos, u32 idx) { + struct annotation *notes = browser__annotation(&browser->b); unsigned back; ui_browser__refresh_dimensions(&browser->b); @@ -439,7 +266,7 @@ static void annotate_browser__set_top(struct annotate_browser *browser, while (browser->b.top_idx != 0 && back != 0) { pos = list_entry(pos->node.prev, struct annotation_line, node); - if (disasm_line__filter(&browser->b, &pos->node)) + if (annotation_line__filter(pos, notes)) continue; --browser->b.top_idx; @@ -453,16 +280,12 @@ static void annotate_browser__set_top(struct annotate_browser *browser, static void annotate_browser__set_rb_top(struct annotate_browser *browser, struct rb_node *nd) { - struct browser_line *bpos; - struct annotation_line *pos; - u32 idx; + struct annotation *notes = browser__annotation(&browser->b); + struct annotation_line * pos = rb_entry(nd, struct annotation_line, rb_node); + u32 idx = pos->idx; - pos = rb_entry(nd, struct annotation_line, rb_node); - bpos = browser_line(pos); - - idx = bpos->idx; - if (annotate_browser__opts.hide_src_code) - idx = bpos->idx_asm; + if (notes->options->hide_src_code) + idx = pos->idx_asm; annotate_browser__set_top(browser, pos, idx); browser->curr_hot = nd; } @@ -510,47 +333,47 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser, static bool annotate_browser__toggle_source(struct annotate_browser *browser) { + struct annotation *notes = browser__annotation(&browser->b); struct annotation_line *al; - struct browser_line *bl; off_t offset = browser->b.index - browser->b.top_idx; browser->b.seek(&browser->b, offset, SEEK_CUR); al = list_entry(browser->b.top, struct annotation_line, node); - bl = browser_line(al); - if (annotate_browser__opts.hide_src_code) { - if (bl->idx_asm < offset) - offset = bl->idx; + if (notes->options->hide_src_code) { + if (al->idx_asm < offset) + offset = al->idx; - browser->b.nr_entries = browser->nr_entries; - annotate_browser__opts.hide_src_code = false; + browser->b.nr_entries = notes->nr_entries; + notes->options->hide_src_code = false; browser->b.seek(&browser->b, -offset, SEEK_CUR); - browser->b.top_idx = bl->idx - offset; - browser->b.index = bl->idx; + browser->b.top_idx = al->idx - offset; + browser->b.index = al->idx; } else { - if (bl->idx_asm < 0) { + if (al->idx_asm < 0) { ui_helpline__puts("Only available for assembly lines."); browser->b.seek(&browser->b, -offset, SEEK_CUR); return false; } - if (bl->idx_asm < offset) - offset = bl->idx_asm; + if (al->idx_asm < offset) + offset = al->idx_asm; - browser->b.nr_entries = browser->nr_asm_entries; - annotate_browser__opts.hide_src_code = true; + browser->b.nr_entries = notes->nr_asm_entries; + notes->options->hide_src_code = true; browser->b.seek(&browser->b, -offset, SEEK_CUR); - browser->b.top_idx = bl->idx_asm - offset; - browser->b.index = bl->idx_asm; + browser->b.top_idx = al->idx_asm - offset; + browser->b.index = al->idx_asm; } return true; } -static void annotate_browser__init_asm_mode(struct annotate_browser *browser) +static void ui_browser__init_asm_mode(struct ui_browser *browser) { - ui_browser__reset_index(&browser->b); - browser->b.nr_entries = browser->nr_asm_entries; + struct annotation *notes = browser__annotation(browser); + ui_browser__reset_index(browser); + browser->nr_entries = notes->nr_asm_entries; } #define SYM_TITLE_MAX_SIZE (PATH_MAX + 64) @@ -561,6 +384,15 @@ static int sym_title(struct symbol *sym, struct map *map, char *title, return snprintf(title, sz, "%s %s", sym->name, map->dso->long_name); } +/* + * This can be called from external jumps, i.e. jumps from one functon + * to another, like from the kernel's entry_SYSCALL_64 function to the + * swapgs_restore_regs_and_return_to_usermode() function. + * + * So all we check here is that dl->ops.target.sym is set, if it is, just + * go to that function and when exiting from its disassembly, come back + * to the calling function. + */ static bool annotate_browser__callq(struct annotate_browser *browser, struct perf_evsel *evsel, struct hist_browser_timer *hbt) @@ -570,9 +402,6 @@ static bool annotate_browser__callq(struct annotate_browser *browser, struct annotation *notes; char title[SYM_TITLE_MAX_SIZE]; - if (!ins__is_call(&dl->ins)) - return false; - if (!dl->ops.target.sym) { ui_helpline__puts("The called function was not found."); return true; @@ -599,23 +428,23 @@ static struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser, s64 offset, s64 *idx) { - struct map_symbol *ms = browser->b.priv; - struct symbol *sym = ms->sym; - struct annotation *notes = symbol__annotation(sym); + struct annotation *notes = browser__annotation(&browser->b); struct disasm_line *pos; *idx = 0; list_for_each_entry(pos, ¬es->src->source, al.node) { if (pos->al.offset == offset) return pos; - if (!disasm_line__filter(&browser->b, &pos->al.node)) + if (!annotation_line__filter(&pos->al, notes)) ++*idx; } return NULL; } -static bool annotate_browser__jump(struct annotate_browser *browser) +static bool annotate_browser__jump(struct annotate_browser *browser, + struct perf_evsel *evsel, + struct hist_browser_timer *hbt) { struct disasm_line *dl = disasm_line(browser->selection); u64 offset; @@ -624,6 +453,11 @@ static bool annotate_browser__jump(struct annotate_browser *browser) if (!ins__is_jump(&dl->ins)) return false; + if (dl->ops.target.outside) { + annotate_browser__callq(browser, evsel, hbt); + return true; + } + offset = dl->ops.target.offset; dl = annotate_browser__find_offset(browser, offset, &idx); if (dl == NULL) { @@ -640,14 +474,12 @@ static struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser, char *s, s64 *idx) { - struct map_symbol *ms = browser->b.priv; - struct symbol *sym = ms->sym; - struct annotation *notes = symbol__annotation(sym); + struct annotation *notes = browser__annotation(&browser->b); struct annotation_line *al = browser->selection; *idx = browser->b.index; list_for_each_entry_continue(al, ¬es->src->source, node) { - if (disasm_line__filter(&browser->b, &al->node)) + if (annotation_line__filter(al, notes)) continue; ++*idx; @@ -679,14 +511,12 @@ static struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser, char *s, s64 *idx) { - struct map_symbol *ms = browser->b.priv; - struct symbol *sym = ms->sym; - struct annotation *notes = symbol__annotation(sym); + struct annotation *notes = browser__annotation(&browser->b); struct annotation_line *al = browser->selection; *idx = browser->b.index; list_for_each_entry_continue_reverse(al, ¬es->src->source, node) { - if (disasm_line__filter(&browser->b, &al->node)) + if (annotation_line__filter(al, notes)) continue; --*idx; @@ -762,19 +592,6 @@ bool annotate_browser__continue_search_reverse(struct annotate_browser *browser, return __annotate_browser__search_reverse(browser); } -static void annotate_browser__update_addr_width(struct annotate_browser *browser) -{ - if (annotate_browser__opts.use_offset) - browser->target_width = browser->min_addr_width; - else - browser->target_width = browser->max_addr_width; - - browser->addr_width = browser->target_width; - - if (annotate_browser__opts.show_nr_jumps) - browser->addr_width += browser->jumps_width + 1; -} - static int annotate_browser__run(struct annotate_browser *browser, struct perf_evsel *evsel, struct hist_browser_timer *hbt) @@ -782,6 +599,7 @@ static int annotate_browser__run(struct annotate_browser *browser, struct rb_node *nd = NULL; struct map_symbol *ms = browser->b.priv; struct symbol *sym = ms->sym; + struct annotation *notes = symbol__annotation(ms->sym); const char *help = "Press 'h' for help on key bindings"; int delay_secs = hbt ? hbt->refresh : 0; int key; @@ -856,6 +674,7 @@ static int annotate_browser__run(struct annotate_browser *browser, "t Circulate percent, total period, samples view\n" "/ Search string\n" "k Toggle line numbers\n" + "P Print to [symbol_name].annotation file.\n" "r Run available scripts\n" "? Search string backwards\n"); continue; @@ -865,8 +684,7 @@ static int annotate_browser__run(struct annotate_browser *browser, continue; } case 'k': - annotate_browser__opts.show_linenr = - !annotate_browser__opts.show_linenr; + notes->options->show_linenr = !notes->options->show_linenr; break; case 'H': nd = browser->curr_hot; @@ -876,15 +694,15 @@ static int annotate_browser__run(struct annotate_browser *browser, ui_helpline__puts(help); continue; case 'o': - annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset; - annotate_browser__update_addr_width(browser); + notes->options->use_offset = !notes->options->use_offset; + annotation__update_column_widths(notes); continue; case 'j': - annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows; + notes->options->jump_arrows = !notes->options->jump_arrows; continue; case 'J': - annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps; - annotate_browser__update_addr_width(browser); + notes->options->show_nr_jumps = !notes->options->show_nr_jumps; + annotation__update_column_widths(notes); continue; case '/': if (annotate_browser__search(browser, delay_secs)) { @@ -910,7 +728,7 @@ show_help: browser->b.height, browser->b.index, browser->b.top_idx, - browser->nr_asm_entries); + notes->nr_asm_entries); } continue; case K_ENTER: @@ -926,22 +744,25 @@ show_help: goto show_sup_ins; else if (ins__is_ret(&dl->ins)) goto out; - else if (!(annotate_browser__jump(browser) || + else if (!(annotate_browser__jump(browser, evsel, hbt) || annotate_browser__callq(browser, evsel, hbt))) { show_sup_ins: ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions."); } continue; } + case 'P': + map_symbol__annotation_dump(ms, evsel); + continue; case 't': - if (annotate_browser__opts.show_total_period) { - annotate_browser__opts.show_total_period = false; - annotate_browser__opts.show_nr_samples = true; - } else if (annotate_browser__opts.show_nr_samples) - annotate_browser__opts.show_nr_samples = false; + if (notes->options->show_total_period) { + notes->options->show_total_period = false; + notes->options->show_nr_samples = true; + } else if (notes->options->show_nr_samples) + notes->options->show_nr_samples = false; else - annotate_browser__opts.show_total_period = true; - annotate_browser__update_addr_width(browser); + notes->options->show_total_period = true; + annotation__update_column_widths(notes); continue; case K_LEFT: case K_ESC: @@ -963,12 +784,6 @@ out: int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel, struct hist_browser_timer *hbt) { - /* Set default value for show_total_period and show_nr_samples */ - annotate_browser__opts.show_total_period = - symbol_conf.show_total_period; - annotate_browser__opts.show_nr_samples = - symbol_conf.show_nr_samples; - return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt); } @@ -982,129 +797,11 @@ int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel, return map_symbol__tui_annotate(&he->ms, evsel, hbt); } - -static unsigned count_insn(struct annotate_browser *browser, u64 start, u64 end) -{ - unsigned n_insn = 0; - u64 offset; - - for (offset = start; offset <= end; offset++) { - if (browser->offsets[offset]) - n_insn++; - } - return n_insn; -} - -static void count_and_fill(struct annotate_browser *browser, u64 start, u64 end, - struct cyc_hist *ch) -{ - unsigned n_insn; - u64 offset; - - n_insn = count_insn(browser, start, end); - if (n_insn && ch->num && ch->cycles) { - float ipc = n_insn / ((double)ch->cycles / (double)ch->num); - - /* Hide data when there are too many overlaps. */ - if (ch->reset >= 0x7fff || ch->reset >= ch->num / 2) - return; - - for (offset = start; offset <= end; offset++) { - struct annotation_line *al = browser->offsets[offset]; - - if (al) - al->ipc = ipc; - } - } -} - -/* - * This should probably be in util/annotate.c to share with the tty - * annotate, but right now we need the per byte offsets arrays, - * which are only here. - */ -static void annotate__compute_ipc(struct annotate_browser *browser, size_t size, - struct symbol *sym) -{ - u64 offset; - struct annotation *notes = symbol__annotation(sym); - - if (!notes->src || !notes->src->cycles_hist) - return; - - pthread_mutex_lock(¬es->lock); - for (offset = 0; offset < size; ++offset) { - struct cyc_hist *ch; - - ch = ¬es->src->cycles_hist[offset]; - if (ch && ch->cycles) { - struct annotation_line *al; - - if (ch->have_start) - count_and_fill(browser, ch->start, offset, ch); - al = browser->offsets[offset]; - if (al && ch->num_aggr) - al->cycles = ch->cycles_aggr / ch->num_aggr; - browser->have_cycles = true; - } - } - pthread_mutex_unlock(¬es->lock); -} - -static void annotate_browser__mark_jump_targets(struct annotate_browser *browser, - size_t size) -{ - u64 offset; - struct map_symbol *ms = browser->b.priv; - struct symbol *sym = ms->sym; - - /* PLT symbols contain external offsets */ - if (strstr(sym->name, "@plt")) - return; - - for (offset = 0; offset < size; ++offset) { - struct annotation_line *al = browser->offsets[offset]; - struct disasm_line *dl; - struct browser_line *blt; - - dl = disasm_line(al); - - if (!disasm_line__is_valid_jump(dl, sym)) - continue; - - al = browser->offsets[dl->ops.target.offset]; - - /* - * FIXME: Oops, no jump target? Buggy disassembler? Or do we - * have to adjust to the previous offset? - */ - if (al == NULL) - continue; - - blt = browser_line(al); - if (++blt->jump_sources > browser->max_jump_sources) - browser->max_jump_sources = blt->jump_sources; - - ++browser->nr_jumps; - } -} - -static inline int width_jumps(int n) -{ - if (n >= 100) - return 5; - if (n / 10) - return 2; - return 1; -} - int symbol__tui_annotate(struct symbol *sym, struct map *map, struct perf_evsel *evsel, struct hist_browser_timer *hbt) { - struct annotation_line *al; - struct annotation *notes; - size_t size; + struct annotation *notes = symbol__annotation(sym); struct map_symbol ms = { .map = map, .sym = sym, @@ -1120,26 +817,14 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, }, }; int ret = -1, err; - int nr_pcnt = 1; if (sym == NULL) return -1; - size = symbol__size(sym); - if (map->dso->annotate_warned) return -1; - browser.offsets = zalloc(size * sizeof(struct annotation_line *)); - if (browser.offsets == NULL) { - ui__error("Not enough memory!"); - return -1; - } - - if (perf_evsel__is_group_event(evsel)) - nr_pcnt = evsel->nr_members; - - err = symbol__annotate(sym, map, evsel, sizeof(struct browser_line), &browser.arch); + err = symbol__annotate2(sym, map, evsel, &annotation__default_options, &browser.arch); if (err) { char msg[BUFSIZ]; symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg)); @@ -1147,110 +832,21 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, goto out_free_offsets; } - symbol__calc_percent(sym, evsel); - ui_helpline__push("Press ESC to exit"); - notes = symbol__annotation(sym); - browser.start = map__rip_2objdump(map, sym->start); - - list_for_each_entry(al, ¬es->src->source, node) { - struct browser_line *bpos; - size_t line_len = strlen(al->line); - - if (browser.b.width < line_len) - browser.b.width = line_len; - bpos = browser_line(al); - bpos->idx = browser.nr_entries++; - if (al->offset != -1) { - bpos->idx_asm = browser.nr_asm_entries++; - /* - * FIXME: short term bandaid to cope with assembly - * routines that comes with labels in the same column - * as the address in objdump, sigh. - * - * E.g. copy_user_generic_unrolled - */ - if (al->offset < (s64)size) - browser.offsets[al->offset] = al; - } else - bpos->idx_asm = -1; - } - - annotate_browser__mark_jump_targets(&browser, size); - annotate__compute_ipc(&browser, size, sym); - - browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size); - browser.max_addr_width = hex_width(sym->end); - browser.jumps_width = width_jumps(browser.max_jump_sources); - browser.nr_events = nr_pcnt; - browser.b.nr_entries = browser.nr_entries; + browser.b.width = notes->max_line_len; + browser.b.nr_entries = notes->nr_entries; browser.b.entries = ¬es->src->source, browser.b.width += 18; /* Percentage */ - if (annotate_browser__opts.hide_src_code) - annotate_browser__init_asm_mode(&browser); - - annotate_browser__update_addr_width(&browser); + if (notes->options->hide_src_code) + ui_browser__init_asm_mode(&browser.b); ret = annotate_browser__run(&browser, evsel, hbt); annotated_source__purge(notes->src); out_free_offsets: - free(browser.offsets); + zfree(¬es->offsets); return ret; } - -#define ANNOTATE_CFG(n) \ - { .name = #n, .value = &annotate_browser__opts.n, } - -/* - * Keep the entries sorted, they are bsearch'ed - */ -static struct annotate_config { - const char *name; - bool *value; -} annotate__configs[] = { - ANNOTATE_CFG(hide_src_code), - ANNOTATE_CFG(jump_arrows), - ANNOTATE_CFG(show_linenr), - ANNOTATE_CFG(show_nr_jumps), - ANNOTATE_CFG(show_nr_samples), - ANNOTATE_CFG(show_total_period), - ANNOTATE_CFG(use_offset), -}; - -#undef ANNOTATE_CFG - -static int annotate_config__cmp(const void *name, const void *cfgp) -{ - const struct annotate_config *cfg = cfgp; - - return strcmp(name, cfg->name); -} - -static int annotate__config(const char *var, const char *value, - void *data __maybe_unused) -{ - struct annotate_config *cfg; - const char *name; - - if (!strstarts(var, "annotate.")) - return 0; - - name = var + 9; - cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs), - sizeof(struct annotate_config), annotate_config__cmp); - - if (cfg == NULL) - ui__warning("%s variable unknown, ignoring...", var); - else - *cfg->value = perf_config_bool(name, value); - return 0; -} - -void annotate_browser__init(void) -{ - perf_config(annotate__config, NULL); -} diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 535357c6ce02..3a428d7c59b9 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -14,6 +14,7 @@ #include "sort.h" #include "build-id.h" #include "color.h" +#include "config.h" #include "cache.h" #include "symbol.h" #include "debug.h" @@ -27,8 +28,25 @@ #include #include +/* FIXME: For the HE_COLORSET */ +#include "ui/browser.h" + +/* + * FIXME: Using the same values as slang.h, + * but that header may not be available everywhere + */ +#define LARROW_CHAR ((unsigned char)',') +#define RARROW_CHAR ((unsigned char)'+') +#define DARROW_CHAR ((unsigned char)'.') +#define UARROW_CHAR ((unsigned char)'-') + #include "sane_ctype.h" +struct annotation_options annotation__default_options = { + .use_offset = true, + .jump_arrows = true, +}; + const char *disassembler_style; const char *objdump_path; static regex_t file_lineno; @@ -184,9 +202,10 @@ bool ins__is_fused(struct arch *arch, const char *ins1, const char *ins2) return arch->ins_is_fused(arch, ins1, ins2); } -static int call__parse(struct arch *arch, struct ins_operands *ops, struct map *map) +static int call__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms) { char *endptr, *tok, *name; + struct map *map = ms->map; struct addr_map_symbol target = { .map = map, }; @@ -254,11 +273,26 @@ bool ins__is_call(const struct ins *ins) return ins->ops == &call_ops || ins->ops == &s390_call_ops; } -static int jump__parse(struct arch *arch __maybe_unused, struct ins_operands *ops, struct map *map __maybe_unused) +static int jump__parse(struct arch *arch __maybe_unused, struct ins_operands *ops, struct map_symbol *ms) { - const char *s = strchr(ops->raw, '+'); + struct map *map = ms->map; + struct symbol *sym = ms->sym; + struct addr_map_symbol target = { + .map = map, + }; const char *c = strchr(ops->raw, ','); - + u64 start, end; + /* + * Examples of lines to parse for the _cpp_lex_token@@Base + * function: + * + * 1159e6c: jne 115aa32 <_cpp_lex_token@@Base+0xf92> + * 1159e8b: jne c469be + * + * The first is a jump to an offset inside the same function, + * the second is to another function, i.e. that 0xa72 is an + * offset in the cpp_named_operator2name@@base function. + */ /* * skip over possible up to 2 operands to get to address, e.g.: * tbnz w0, #26, ffff0000083cd190 @@ -274,8 +308,36 @@ static int jump__parse(struct arch *arch __maybe_unused, struct ins_operands *op ops->target.addr = strtoull(ops->raw, NULL, 16); } - if (s++ != NULL) { - ops->target.offset = strtoull(s, NULL, 16); + target.addr = map__objdump_2mem(map, ops->target.addr); + start = map->unmap_ip(map, sym->start), + end = map->unmap_ip(map, sym->end); + + ops->target.outside = target.addr < start || target.addr > end; + + /* + * FIXME: things like this in _cpp_lex_token (gcc's cc1 program): + + cpp_named_operator2name@@Base+0xa72 + + * Point to a place that is after the cpp_named_operator2name + * boundaries, i.e. in the ELF symbol table for cc1 + * cpp_named_operator2name is marked as being 32-bytes long, but it in + * fact is much larger than that, so we seem to need a symbols__find() + * routine that looks for >= current->start and < next_symbol->start, + * possibly just for C++ objects? + * + * For now lets just make some progress by marking jumps to outside the + * current function as call like. + * + * Actual navigation will come next, with further understanding of how + * the symbol searching and disassembly should be done. + */ + if (map_groups__find_ams(&target) == 0 && + map__rip_2objdump(target.map, map->map_ip(target.map, target.addr)) == ops->target.addr) + ops->target.sym = target.sym; + + if (!ops->target.outside) { + ops->target.offset = target.addr - start; ops->target.offset_avail = true; } else { ops->target.offset_avail = false; @@ -287,11 +349,15 @@ static int jump__parse(struct arch *arch __maybe_unused, struct ins_operands *op static int jump__scnprintf(struct ins *ins, char *bf, size_t size, struct ins_operands *ops) { - const char *c = strchr(ops->raw, ','); + const char *c; if (!ops->target.addr || ops->target.offset < 0) return ins__raw_scnprintf(ins, bf, size, ops); + if (ops->target.outside && ops->target.sym != NULL) + return scnprintf(bf, size, "%-6s %s", ins->name, ops->target.sym->name); + + c = strchr(ops->raw, ','); if (c != NULL) { const char *c2 = strchr(c + 1, ','); @@ -347,7 +413,7 @@ static int comment__symbol(char *raw, char *comment, u64 *addrp, char **namep) return 0; } -static int lock__parse(struct arch *arch, struct ins_operands *ops, struct map *map) +static int lock__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms) { ops->locked.ops = zalloc(sizeof(*ops->locked.ops)); if (ops->locked.ops == NULL) @@ -362,7 +428,7 @@ static int lock__parse(struct arch *arch, struct ins_operands *ops, struct map * goto out_free_ops; if (ops->locked.ins.ops->parse && - ops->locked.ins.ops->parse(arch, ops->locked.ops, map) < 0) + ops->locked.ins.ops->parse(arch, ops->locked.ops, ms) < 0) goto out_free_ops; return 0; @@ -405,7 +471,7 @@ static struct ins_ops lock_ops = { .scnprintf = lock__scnprintf, }; -static int mov__parse(struct arch *arch, struct ins_operands *ops, struct map *map __maybe_unused) +static int mov__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms __maybe_unused) { char *s = strchr(ops->raw, ','), *target, *comment, prev; @@ -466,7 +532,7 @@ static struct ins_ops mov_ops = { .scnprintf = mov__scnprintf, }; -static int dec__parse(struct arch *arch __maybe_unused, struct ins_operands *ops, struct map *map __maybe_unused) +static int dec__parse(struct arch *arch __maybe_unused, struct ins_operands *ops, struct map_symbol *ms __maybe_unused) { char *target, *comment, *s, prev; @@ -833,6 +899,66 @@ int addr_map_symbol__account_cycles(struct addr_map_symbol *ams, return err; } +static unsigned annotation__count_insn(struct annotation *notes, u64 start, u64 end) +{ + unsigned n_insn = 0; + u64 offset; + + for (offset = start; offset <= end; offset++) { + if (notes->offsets[offset]) + n_insn++; + } + return n_insn; +} + +static void annotation__count_and_fill(struct annotation *notes, u64 start, u64 end, struct cyc_hist *ch) +{ + unsigned n_insn; + u64 offset; + + n_insn = annotation__count_insn(notes, start, end); + if (n_insn && ch->num && ch->cycles) { + float ipc = n_insn / ((double)ch->cycles / (double)ch->num); + + /* Hide data when there are too many overlaps. */ + if (ch->reset >= 0x7fff || ch->reset >= ch->num / 2) + return; + + for (offset = start; offset <= end; offset++) { + struct annotation_line *al = notes->offsets[offset]; + + if (al) + al->ipc = ipc; + } + } +} + +void annotation__compute_ipc(struct annotation *notes, size_t size) +{ + u64 offset; + + if (!notes->src || !notes->src->cycles_hist) + return; + + pthread_mutex_lock(¬es->lock); + for (offset = 0; offset < size; ++offset) { + struct cyc_hist *ch; + + ch = ¬es->src->cycles_hist[offset]; + if (ch && ch->cycles) { + struct annotation_line *al; + + if (ch->have_start) + annotation__count_and_fill(notes, ch->start, offset, ch); + al = notes->offsets[offset]; + if (al && ch->num_aggr) + al->cycles = ch->cycles_aggr / ch->num_aggr; + notes->have_cycles = true; + } + } + pthread_mutex_unlock(¬es->lock); +} + int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, struct perf_sample *sample, int evidx) { @@ -845,14 +971,14 @@ int hist_entry__inc_addr_samples(struct hist_entry *he, struct perf_sample *samp return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip, sample); } -static void disasm_line__init_ins(struct disasm_line *dl, struct arch *arch, struct map *map) +static void disasm_line__init_ins(struct disasm_line *dl, struct arch *arch, struct map_symbol *ms) { dl->ins.ops = ins__find(arch, dl->ins.name); if (!dl->ins.ops) return; - if (dl->ins.ops->parse && dl->ins.ops->parse(arch, &dl->ops, map) < 0) + if (dl->ins.ops->parse && dl->ins.ops->parse(arch, &dl->ops, ms) < 0) dl->ins.ops = NULL; } @@ -889,7 +1015,7 @@ out_free_name: struct annotate_args { size_t privsize; struct arch *arch; - struct map *map; + struct map_symbol ms; struct perf_evsel *evsel; s64 offset; char *line; @@ -971,7 +1097,7 @@ static struct disasm_line *disasm_line__new(struct annotate_args *args) if (disasm_line__parse(dl->al.line, &dl->ins.name, &dl->ops.raw) < 0) goto out_free_line; - disasm_line__init_ins(dl, args->arch, args->map); + disasm_line__init_ins(dl, args->arch, &args->ms); } } @@ -1229,7 +1355,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, FILE *file, struct annotate_args *args, int *line_nr) { - struct map *map = args->map; + struct map *map = args->ms.map; struct annotation *notes = symbol__annotation(sym); struct disasm_line *dl; char *line = NULL, *parsed_line, *tmp, *tmp2; @@ -1276,6 +1402,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, FILE *file, args->offset = offset; args->line = parsed_line; args->line_nr = *line_nr; + args->ms.sym = sym; dl = disasm_line__new(args); free(line); @@ -1284,7 +1411,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, FILE *file, if (dl == NULL) return -1; - if (!disasm_line__has_offset(dl)) { + if (!disasm_line__has_local_offset(dl)) { dl->ops.target.offset = dl->ops.target.addr - map__rip_2objdump(map, sym->start); dl->ops.target.offset_avail = true; @@ -1428,7 +1555,7 @@ fallback: static int symbol__disassemble(struct symbol *sym, struct annotate_args *args) { - struct map *map = args->map; + struct map *map = args->ms.map; struct dso *dso = map->dso; char *command; FILE *file; @@ -1627,7 +1754,6 @@ int symbol__annotate(struct symbol *sym, struct map *map, { struct annotate_args args = { .privsize = privsize, - .map = map, .evsel = evsel, }; struct perf_env *env = perf_evsel__env(evsel); @@ -1653,6 +1779,9 @@ int symbol__annotate(struct symbol *sym, struct map *map, } } + args.ms.map = map; + args.ms.sym = sym; + return symbol__disassemble(sym, &args); } @@ -1893,6 +2022,103 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, return more; } +static void FILE__set_percent_color(void *fp __maybe_unused, + double percent __maybe_unused, + bool current __maybe_unused) +{ +} + +static int FILE__set_jumps_percent_color(void *fp __maybe_unused, + int nr __maybe_unused, bool current __maybe_unused) +{ + return 0; +} + +static int FILE__set_color(void *fp __maybe_unused, int color __maybe_unused) +{ + return 0; +} + +static void FILE__printf(void *fp, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vfprintf(fp, fmt, args); + va_end(args); +} + +static void FILE__write_graph(void *fp, int graph) +{ + const char *s; + switch (graph) { + + case DARROW_CHAR: s = "↓"; break; + case UARROW_CHAR: s = "↑"; break; + case LARROW_CHAR: s = "←"; break; + case RARROW_CHAR: s = "→"; break; + default: s = "?"; break; + } + + fputs(s, fp); +} + +int symbol__annotate_fprintf2(struct symbol *sym, FILE *fp) +{ + struct annotation *notes = symbol__annotation(sym); + struct annotation_write_ops ops = { + .first_line = true, + .obj = fp, + .set_color = FILE__set_color, + .set_percent_color = FILE__set_percent_color, + .set_jumps_percent_color = FILE__set_jumps_percent_color, + .printf = FILE__printf, + .write_graph = FILE__write_graph, + }; + struct annotation_line *al; + + list_for_each_entry(al, ¬es->src->source, node) { + if (annotation_line__filter(al, notes)) + continue; + annotation_line__write(al, notes, &ops); + fputc('\n', fp); + ops.first_line = false; + } + + return 0; +} + +int map_symbol__annotation_dump(struct map_symbol *ms, struct perf_evsel *evsel) +{ + const char *ev_name = perf_evsel__name(evsel); + char buf[1024]; + char *filename; + int err = -1; + FILE *fp; + + if (asprintf(&filename, "%s.annotation", ms->sym->name) < 0) + return -1; + + fp = fopen(filename, "w"); + if (fp == NULL) + goto out_free_filename; + + if (perf_evsel__is_group_event(evsel)) { + perf_evsel__group_desc(evsel, buf, sizeof(buf)); + ev_name = buf; + } + + fprintf(fp, "%s() %s\nEvent: %s\n\n", + ms->sym->name, ms->map->dso->long_name, ev_name); + symbol__annotate_fprintf2(ms->sym, fp); + + fclose(fp); + err = 0; +out_free_filename: + free(filename); + return err; +} + void symbol__annotate_zero_histogram(struct symbol *sym, int evidx) { struct annotation *notes = symbol__annotation(sym); @@ -1952,8 +2178,109 @@ size_t disasm__fprintf(struct list_head *head, FILE *fp) return printed; } +bool disasm_line__is_valid_local_jump(struct disasm_line *dl, struct symbol *sym) +{ + if (!dl || !dl->ins.ops || !ins__is_jump(&dl->ins) || + !disasm_line__has_local_offset(dl) || dl->ops.target.offset < 0 || + dl->ops.target.offset >= (s64)symbol__size(sym)) + return false; + + return true; +} + +void annotation__mark_jump_targets(struct annotation *notes, struct symbol *sym) +{ + u64 offset, size = symbol__size(sym); + + /* PLT symbols contain external offsets */ + if (strstr(sym->name, "@plt")) + return; + + for (offset = 0; offset < size; ++offset) { + struct annotation_line *al = notes->offsets[offset]; + struct disasm_line *dl; + + dl = disasm_line(al); + + if (!disasm_line__is_valid_local_jump(dl, sym)) + continue; + + al = notes->offsets[dl->ops.target.offset]; + + /* + * FIXME: Oops, no jump target? Buggy disassembler? Or do we + * have to adjust to the previous offset? + */ + if (al == NULL) + continue; + + if (++al->jump_sources > notes->max_jump_sources) + notes->max_jump_sources = al->jump_sources; + + ++notes->nr_jumps; + } +} + +void annotation__set_offsets(struct annotation *notes, s64 size) +{ + struct annotation_line *al; + + notes->max_line_len = 0; + + list_for_each_entry(al, ¬es->src->source, node) { + size_t line_len = strlen(al->line); + + if (notes->max_line_len < line_len) + notes->max_line_len = line_len; + al->idx = notes->nr_entries++; + if (al->offset != -1) { + al->idx_asm = notes->nr_asm_entries++; + /* + * FIXME: short term bandaid to cope with assembly + * routines that comes with labels in the same column + * as the address in objdump, sigh. + * + * E.g. copy_user_generic_unrolled + */ + if (al->offset < size) + notes->offsets[al->offset] = al; + } else + al->idx_asm = -1; + } +} + +static inline int width_jumps(int n) +{ + if (n >= 100) + return 5; + if (n / 10) + return 2; + return 1; +} + +void annotation__init_column_widths(struct annotation *notes, struct symbol *sym) +{ + notes->widths.addr = notes->widths.target = + notes->widths.min_addr = hex_width(symbol__size(sym)); + notes->widths.max_addr = hex_width(sym->end); + notes->widths.jumps = width_jumps(notes->max_jump_sources); +} + +void annotation__update_column_widths(struct annotation *notes) +{ + if (notes->options->use_offset) + notes->widths.target = notes->widths.min_addr; + else + notes->widths.target = notes->widths.max_addr; + + notes->widths.addr = notes->widths.target; + + if (notes->options->show_nr_jumps) + notes->widths.addr += notes->widths.jumps + 1; +} + static void annotation__calc_lines(struct annotation *notes, struct map *map, - struct rb_root *root, u64 start) + struct rb_root *root) { struct annotation_line *al; struct rb_root tmp_root = RB_ROOT; @@ -1974,8 +2301,8 @@ static void annotation__calc_lines(struct annotation *notes, struct map *map, if (percent_max <= 0.5) continue; - al->path = get_srcline(map->dso, start + al->offset, NULL, - false, true, start + al->offset); + al->path = get_srcline(map->dso, notes->start + al->offset, NULL, + false, true, notes->start + al->offset); insert_source_line(&tmp_root, al); } @@ -1986,9 +2313,40 @@ static void symbol__calc_lines(struct symbol *sym, struct map *map, struct rb_root *root) { struct annotation *notes = symbol__annotation(sym); - u64 start = map__rip_2objdump(map, sym->start); - annotation__calc_lines(notes, map, root, start); + annotation__calc_lines(notes, map, root); +} + +int symbol__tty_annotate2(struct symbol *sym, struct map *map, + struct perf_evsel *evsel, bool print_lines, + bool full_paths) +{ + struct dso *dso = map->dso; + struct rb_root source_line = RB_ROOT; + struct annotation_options opts = annotation__default_options; + const char *ev_name = perf_evsel__name(evsel); + char buf[1024]; + + if (symbol__annotate2(sym, map, evsel, &opts, NULL) < 0) + return -1; + + if (print_lines) { + srcline_full_filename = full_paths; + symbol__calc_lines(sym, map, &source_line); + print_summary(&source_line, dso->long_name); + } + + if (perf_evsel__is_group_event(evsel)) { + perf_evsel__group_desc(evsel, buf, sizeof(buf)); + ev_name = buf; + } + + fprintf(stdout, "%s() %s\nEvent: %s\n\n", sym->name, dso->long_name, ev_name); + symbol__annotate_fprintf2(sym, stdout); + + annotated_source__purge(symbol__annotation(sym)->src); + + return 0; } int symbol__tty_annotate(struct symbol *sym, struct map *map, @@ -2021,3 +2379,276 @@ bool ui__has_annotation(void) { return use_browser == 1 && perf_hpp_list.sym; } + + +double annotation_line__max_percent(struct annotation_line *al, struct annotation *notes) +{ + double percent_max = 0.0; + int i; + + for (i = 0; i < notes->nr_events; i++) { + if (al->samples[i].percent > percent_max) + percent_max = al->samples[i].percent; + } + + return percent_max; +} + +static void disasm_line__write(struct disasm_line *dl, struct annotation *notes, + void *obj, char *bf, size_t size, + void (*obj__printf)(void *obj, const char *fmt, ...), + void (*obj__write_graph)(void *obj, int graph)) +{ + if (dl->ins.ops && dl->ins.ops->scnprintf) { + if (ins__is_jump(&dl->ins)) { + bool fwd; + + if (dl->ops.target.outside) + goto call_like; + fwd = dl->ops.target.offset > dl->al.offset; + obj__write_graph(obj, fwd ? DARROW_CHAR : UARROW_CHAR); + obj__printf(obj, " "); + } else if (ins__is_call(&dl->ins)) { +call_like: + obj__write_graph(obj, RARROW_CHAR); + obj__printf(obj, " "); + } else if (ins__is_ret(&dl->ins)) { + obj__write_graph(obj, LARROW_CHAR); + obj__printf(obj, " "); + } else { + obj__printf(obj, " "); + } + } else { + obj__printf(obj, " "); + } + + disasm_line__scnprintf(dl, bf, size, !notes->options->use_offset); +} + +static void __annotation_line__write(struct annotation_line *al, struct annotation *notes, + bool first_line, bool current_entry, bool change_color, int width, + void *obj, + int (*obj__set_color)(void *obj, int color), + void (*obj__set_percent_color)(void *obj, double percent, bool current), + int (*obj__set_jumps_percent_color)(void *obj, int nr, bool current), + void (*obj__printf)(void *obj, const char *fmt, ...), + void (*obj__write_graph)(void *obj, int graph)) + +{ + double percent_max = annotation_line__max_percent(al, notes); + int pcnt_width = annotation__pcnt_width(notes), + cycles_width = annotation__cycles_width(notes); + bool show_title = false; + char bf[256]; + int printed; + + if (first_line && (al->offset == -1 || percent_max == 0.0)) { + if (notes->have_cycles) { + if (al->ipc == 0.0 && al->cycles == 0) + show_title = true; + } else + show_title = true; + } + + if (al->offset != -1 && percent_max != 0.0) { + int i; + + for (i = 0; i < notes->nr_events; i++) { + obj__set_percent_color(obj, al->samples[i].percent, current_entry); + if (notes->options->show_total_period) { + obj__printf(obj, "%11" PRIu64 " ", al->samples[i].he.period); + } else if (notes->options->show_nr_samples) { + obj__printf(obj, "%6" PRIu64 " ", + al->samples[i].he.nr_samples); + } else { + obj__printf(obj, "%6.2f ", + al->samples[i].percent); + } + } + } else { + obj__set_percent_color(obj, 0, current_entry); + + if (!show_title) + obj__printf(obj, "%-*s", pcnt_width, " "); + else { + obj__printf(obj, "%-*s", pcnt_width, + notes->options->show_total_period ? "Period" : + notes->options->show_nr_samples ? "Samples" : "Percent"); + } + } + + if (notes->have_cycles) { + if (al->ipc) + obj__printf(obj, "%*.2f ", ANNOTATION__IPC_WIDTH - 1, al->ipc); + else if (!show_title) + obj__printf(obj, "%*s", ANNOTATION__IPC_WIDTH, " "); + else + obj__printf(obj, "%*s ", ANNOTATION__IPC_WIDTH - 1, "IPC"); + + if (al->cycles) + obj__printf(obj, "%*" PRIu64 " ", + ANNOTATION__CYCLES_WIDTH - 1, al->cycles); + else if (!show_title) + obj__printf(obj, "%*s", ANNOTATION__CYCLES_WIDTH, " "); + else + obj__printf(obj, "%*s ", ANNOTATION__CYCLES_WIDTH - 1, "Cycle"); + } + + obj__printf(obj, " "); + + if (!*al->line) + obj__printf(obj, "%-*s", width - pcnt_width - cycles_width, " "); + else if (al->offset == -1) { + if (al->line_nr && notes->options->show_linenr) + printed = scnprintf(bf, sizeof(bf), "%-*d ", notes->widths.addr + 1, al->line_nr); + else + printed = scnprintf(bf, sizeof(bf), "%-*s ", notes->widths.addr, " "); + obj__printf(obj, bf); + obj__printf(obj, "%-*s", width - printed - pcnt_width - cycles_width + 1, al->line); + } else { + u64 addr = al->offset; + int color = -1; + + if (!notes->options->use_offset) + addr += notes->start; + + if (!notes->options->use_offset) { + printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr); + } else { + if (al->jump_sources) { + if (notes->options->show_nr_jumps) { + int prev; + printed = scnprintf(bf, sizeof(bf), "%*d ", + notes->widths.jumps, + al->jump_sources); + prev = obj__set_jumps_percent_color(obj, al->jump_sources, + current_entry); + obj__printf(obj, bf); + obj__set_color(obj, prev); + } + + printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ", + notes->widths.target, addr); + } else { + printed = scnprintf(bf, sizeof(bf), "%-*s ", + notes->widths.addr, " "); + } + } + + if (change_color) + color = obj__set_color(obj, HE_COLORSET_ADDR); + obj__printf(obj, bf); + if (change_color) + obj__set_color(obj, color); + + disasm_line__write(disasm_line(al), notes, obj, bf, sizeof(bf), obj__printf, obj__write_graph); + + obj__printf(obj, "%-*s", width - pcnt_width - cycles_width - 3 - printed, bf); + } + +} + +void annotation_line__write(struct annotation_line *al, struct annotation *notes, + struct annotation_write_ops *ops) +{ + __annotation_line__write(al, notes, ops->first_line, ops->current_entry, + ops->change_color, ops->width, ops->obj, + ops->set_color, ops->set_percent_color, + ops->set_jumps_percent_color, ops->printf, + ops->write_graph); +} + +int symbol__annotate2(struct symbol *sym, struct map *map, struct perf_evsel *evsel, + struct annotation_options *options, struct arch **parch) +{ + struct annotation *notes = symbol__annotation(sym); + size_t size = symbol__size(sym); + int nr_pcnt = 1, err; + + notes->offsets = zalloc(size * sizeof(struct annotation_line *)); + if (notes->offsets == NULL) + return -1; + + if (perf_evsel__is_group_event(evsel)) + nr_pcnt = evsel->nr_members; + + err = symbol__annotate(sym, map, evsel, 0, parch); + if (err) + goto out_free_offsets; + + notes->options = options; + + symbol__calc_percent(sym, evsel); + + notes->start = map__rip_2objdump(map, sym->start); + + annotation__set_offsets(notes, size); + annotation__mark_jump_targets(notes, sym); + annotation__compute_ipc(notes, size); + annotation__init_column_widths(notes, sym); + notes->nr_events = nr_pcnt; + + annotation__update_column_widths(notes); + + return 0; + +out_free_offsets: + zfree(¬es->offsets); + return -1; +} + +#define ANNOTATION__CFG(n) \ + { .name = #n, .value = &annotation__default_options.n, } + +/* + * Keep the entries sorted, they are bsearch'ed + */ +static struct annotation_config { + const char *name; + bool *value; +} annotation__configs[] = { + ANNOTATION__CFG(hide_src_code), + ANNOTATION__CFG(jump_arrows), + ANNOTATION__CFG(show_linenr), + ANNOTATION__CFG(show_nr_jumps), + ANNOTATION__CFG(show_nr_samples), + ANNOTATION__CFG(show_total_period), + ANNOTATION__CFG(use_offset), +}; + +#undef ANNOTATION__CFG + +static int annotation_config__cmp(const void *name, const void *cfgp) +{ + const struct annotation_config *cfg = cfgp; + + return strcmp(name, cfg->name); +} + +static int annotation__config(const char *var, const char *value, + void *data __maybe_unused) +{ + struct annotation_config *cfg; + const char *name; + + if (!strstarts(var, "annotate.")) + return 0; + + name = var + 9; + cfg = bsearch(name, annotation__configs, ARRAY_SIZE(annotation__configs), + sizeof(struct annotation_config), annotation_config__cmp); + + if (cfg == NULL) + pr_debug("%s variable unknown, ignoring...", var); + else + *cfg->value = perf_config_bool(name, value); + return 0; +} + +void annotation_config__init(void) +{ + perf_config(annotation__config, NULL); + + annotation__default_options.show_total_period = symbol_conf.show_total_period; + annotation__default_options.show_nr_samples = symbol_conf.show_nr_samples; +} diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index 7e914e834101..ff7e3df31efa 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -28,6 +28,7 @@ struct ins_operands { u64 addr; s64 offset; bool offset_avail; + bool outside; } target; union { struct { @@ -46,7 +47,7 @@ struct arch; struct ins_ops { void (*free)(struct ins_operands *ops); - int (*parse)(struct arch *arch, struct ins_operands *ops, struct map *map); + int (*parse)(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms); int (*scnprintf)(struct ins *ins, char *bf, size_t size, struct ins_operands *ops); }; @@ -58,6 +59,21 @@ bool ins__is_lock(const struct ins *ins); int ins__scnprintf(struct ins *ins, char *bf, size_t size, struct ins_operands *ops); bool ins__is_fused(struct arch *arch, const char *ins1, const char *ins2); +#define ANNOTATION__IPC_WIDTH 6 +#define ANNOTATION__CYCLES_WIDTH 6 + +struct annotation_options { + bool hide_src_code, + use_offset, + jump_arrows, + show_linenr, + show_nr_jumps, + show_nr_samples, + show_total_period; +}; + +extern struct annotation_options annotation__default_options; + struct annotation; struct sym_hist_entry { @@ -77,10 +93,13 @@ struct annotation_line { s64 offset; char *line; int line_nr; + int jump_sources; float ipc; u64 cycles; size_t privsize; char *path; + u32 idx; + int idx_asm; int samples_nr; struct annotation_data samples[0]; }; @@ -98,14 +117,40 @@ static inline struct disasm_line *disasm_line(struct annotation_line *al) return al ? container_of(al, struct disasm_line, al) : NULL; } -static inline bool disasm_line__has_offset(const struct disasm_line *dl) +/* + * Is this offset in the same function as the line it is used? + * asm functions jump to other functions, for instance. + */ +static inline bool disasm_line__has_local_offset(const struct disasm_line *dl) { - return dl->ops.target.offset_avail; + return dl->ops.target.offset_avail && !dl->ops.target.outside; } +/* + * Can we draw an arrow from the jump to its target, for instance? I.e. + * is the jump and its target in the same function? + */ +bool disasm_line__is_valid_local_jump(struct disasm_line *dl, struct symbol *sym); + void disasm_line__free(struct disasm_line *dl); struct annotation_line * annotation_line__next(struct annotation_line *pos, struct list_head *head); + +struct annotation_write_ops { + bool first_line, current_entry, change_color; + int width; + void *obj; + int (*set_color)(void *obj, int color); + void (*set_percent_color)(void *obj, double percent, bool current); + int (*set_jumps_percent_color)(void *obj, int nr, bool current); + void (*printf)(void *obj, const char *fmt, ...); + void (*write_graph)(void *obj, int graph); +}; + +double annotation_line__max_percent(struct annotation_line *al, struct annotation *notes); +void annotation_line__write(struct annotation_line *al, struct annotation *notes, + struct annotation_write_ops *ops); + int disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size, bool raw); size_t disasm__fprintf(struct list_head *head, FILE *fp); void symbol__calc_percent(struct symbol *sym, struct perf_evsel *evsel); @@ -151,9 +196,47 @@ struct annotated_source { struct annotation { pthread_mutex_t lock; u64 max_coverage; + u64 start; + struct annotation_options *options; + struct annotation_line **offsets; + int nr_events; + int nr_jumps; + int max_jump_sources; + int nr_entries; + int nr_asm_entries; + u16 max_line_len; + struct { + u8 addr; + u8 jumps; + u8 target; + u8 min_addr; + u8 max_addr; + } widths; + bool have_cycles; struct annotated_source *src; }; +static inline int annotation__cycles_width(struct annotation *notes) +{ + return notes->have_cycles ? ANNOTATION__IPC_WIDTH + ANNOTATION__CYCLES_WIDTH : 0; +} + +static inline int annotation__pcnt_width(struct annotation *notes) +{ + return (notes->options->show_total_period ? 12 : 7) * notes->nr_events; +} + +static inline bool annotation_line__filter(struct annotation_line *al, struct annotation *notes) +{ + return notes->options->hide_src_code && al->offset == -1; +} + +void annotation__set_offsets(struct annotation *notes, s64 size); +void annotation__compute_ipc(struct annotation *notes, size_t size); +void annotation__mark_jump_targets(struct annotation *notes, struct symbol *sym); +void annotation__update_column_widths(struct annotation *notes); +void annotation__init_column_widths(struct annotation *notes, struct symbol *sym); + static inline struct sym_hist *annotation__histogram(struct annotation *notes, int idx) { return (((void *)¬es->src->histograms) + @@ -181,6 +264,10 @@ void symbol__annotate_zero_histograms(struct symbol *sym); int symbol__annotate(struct symbol *sym, struct map *map, struct perf_evsel *evsel, size_t privsize, struct arch **parch); +int symbol__annotate2(struct symbol *sym, struct map *map, + struct perf_evsel *evsel, + struct annotation_options *options, + struct arch **parch); enum symbol_disassemble_errno { SYMBOL_ANNOTATE_ERRNO__SUCCESS = 0, @@ -205,16 +292,23 @@ int symbol__strerror_disassemble(struct symbol *sym, struct map *map, int symbol__annotate_printf(struct symbol *sym, struct map *map, struct perf_evsel *evsel, bool full_paths, int min_pcnt, int max_lines, int context); +int symbol__annotate_fprintf2(struct symbol *sym, FILE *fp); void symbol__annotate_zero_histogram(struct symbol *sym, int evidx); void symbol__annotate_decay_histogram(struct symbol *sym, int evidx); void annotated_source__purge(struct annotated_source *as); +int map_symbol__annotation_dump(struct map_symbol *ms, struct perf_evsel *evsel); + bool ui__has_annotation(void); int symbol__tty_annotate(struct symbol *sym, struct map *map, struct perf_evsel *evsel, bool print_lines, bool full_paths, int min_pcnt, int max_lines); +int symbol__tty_annotate2(struct symbol *sym, struct map *map, + struct perf_evsel *evsel, bool print_lines, + bool full_paths); + #ifdef HAVE_SLANG_SUPPORT int symbol__tui_annotate(struct symbol *sym, struct map *map, struct perf_evsel *evsel, @@ -232,4 +326,6 @@ static inline int symbol__tui_annotate(struct symbol *sym __maybe_unused, extern const char *disassembler_style; +void annotation_config__init(void); + #endif /* __PERF_ANNOTATE_H */ diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index b956868fd445..863b61478edd 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -1004,8 +1004,10 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist, return PyErr_NoMemory(); evsel = perf_evlist__event2evsel(evlist, event); - if (!evsel) + if (!evsel) { + Py_INCREF(Py_None); return Py_None; + } pevent->evsel = evsel; diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c index 8e969f28cc59..7bdd239c795c 100644 --- a/tools/perf/util/unwind-libdw.c +++ b/tools/perf/util/unwind-libdw.c @@ -236,7 +236,8 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, if (err) goto out; - if (!dwfl_attach_state(ui->dwfl, EM_NONE, thread->tid, &callbacks, ui)) + err = !dwfl_attach_state(ui->dwfl, EM_NONE, thread->tid, &callbacks, ui); + if (err) goto out; err = dwfl_getthread_frames(ui->dwfl, thread->tid, frame_callback, ui);