1 // SPDX-License-Identifier: GPL-2.0
2 #include "../../util/util.h"
3 #include "../browser.h"
4 #include "../helpline.h"
7 #include "../../util/annotate.h"
8 #include "../../util/hist.h"
9 #include "../../util/sort.h"
10 #include "../../util/symbol.h"
11 #include "../../util/evsel.h"
12 #include "../../util/config.h"
13 #include "../../util/evlist.h"
16 #include <linux/kernel.h>
17 #include <linux/string.h>
18 #include <sys/ttydefaults.h>
20 struct disasm_line_samples {
22 struct sym_hist_entry he;
26 #define CYCLES_WIDTH 6
28 struct browser_disasm_line {
29 struct rb_node rb_node;
34 * actual length of this array is saved on the nr_events field
35 * of the struct annotate_browser
37 struct disasm_line_samples samples[1];
40 static struct annotate_browser_opt {
48 } annotate_browser__opts = {
55 struct annotate_browser {
57 struct rb_root entries;
58 struct rb_node *curr_hot;
59 struct disasm_line *selection;
60 struct disasm_line **offsets;
68 bool searching_backwards;
78 static inline struct browser_disasm_line *disasm_line__browser(struct disasm_line *dl)
80 return (struct browser_disasm_line *)(dl + 1);
83 static bool disasm_line__filter(struct ui_browser *browser __maybe_unused,
86 if (annotate_browser__opts.hide_src_code) {
87 struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
88 return dl->offset == -1;
94 static int annotate_browser__jumps_percent_color(struct annotate_browser *browser,
97 if (current && (!browser->b.use_navkeypressed || browser->b.navkeypressed))
98 return HE_COLORSET_SELECTED;
99 if (nr == browser->max_jump_sources)
100 return HE_COLORSET_TOP;
102 return HE_COLORSET_MEDIUM;
103 return HE_COLORSET_NORMAL;
106 static int annotate_browser__set_jumps_percent_color(struct annotate_browser *browser,
107 int nr, bool current)
109 int color = annotate_browser__jumps_percent_color(browser, nr, current);
110 return ui_browser__set_color(&browser->b, color);
113 static int annotate_browser__pcnt_width(struct annotate_browser *ab)
115 return (annotate_browser__opts.show_total_period ? 12 : 7) * ab->nr_events;
118 static int annotate_browser__cycles_width(struct annotate_browser *ab)
120 return ab->have_cycles ? IPC_WIDTH + CYCLES_WIDTH : 0;
123 static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
125 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
126 struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
127 struct browser_disasm_line *bdl = disasm_line__browser(dl);
128 bool current_entry = ui_browser__is_current_entry(browser, row);
129 bool change_color = (!annotate_browser__opts.hide_src_code &&
130 (!current_entry || (browser->use_navkeypressed &&
131 !browser->navkeypressed)));
132 int width = browser->width, printed;
133 int i, pcnt_width = annotate_browser__pcnt_width(ab),
134 cycles_width = annotate_browser__cycles_width(ab);
135 double percent_max = 0.0;
137 bool show_title = false;
139 for (i = 0; i < ab->nr_events; i++) {
140 if (bdl->samples[i].percent > percent_max)
141 percent_max = bdl->samples[i].percent;
144 if ((row == 0) && (dl->offset == -1 || percent_max == 0.0)) {
145 if (ab->have_cycles) {
146 if (dl->ipc == 0.0 && dl->cycles == 0)
152 if (dl->offset != -1 && percent_max != 0.0) {
153 for (i = 0; i < ab->nr_events; i++) {
154 ui_browser__set_percent_color(browser,
155 bdl->samples[i].percent,
157 if (annotate_browser__opts.show_total_period) {
158 ui_browser__printf(browser, "%11" PRIu64 " ",
159 bdl->samples[i].he.period);
160 } else if (annotate_browser__opts.show_nr_samples) {
161 ui_browser__printf(browser, "%6" PRIu64 " ",
162 bdl->samples[i].he.nr_samples);
164 ui_browser__printf(browser, "%6.2f ",
165 bdl->samples[i].percent);
169 ui_browser__set_percent_color(browser, 0, current_entry);
172 ui_browser__write_nstring(browser, " ", pcnt_width);
174 ui_browser__printf(browser, "%*s", pcnt_width,
175 annotate_browser__opts.show_total_period ? "Period" :
176 annotate_browser__opts.show_nr_samples ? "Samples" : "Percent");
179 if (ab->have_cycles) {
181 ui_browser__printf(browser, "%*.2f ", IPC_WIDTH - 1, dl->ipc);
182 else if (!show_title)
183 ui_browser__write_nstring(browser, " ", IPC_WIDTH);
185 ui_browser__printf(browser, "%*s ", IPC_WIDTH - 1, "IPC");
188 ui_browser__printf(browser, "%*" PRIu64 " ",
189 CYCLES_WIDTH - 1, dl->cycles);
190 else if (!show_title)
191 ui_browser__write_nstring(browser, " ", CYCLES_WIDTH);
193 ui_browser__printf(browser, "%*s ", CYCLES_WIDTH - 1, "Cycle");
196 SLsmg_write_char(' ');
198 /* The scroll bar isn't being used */
199 if (!browser->navkeypressed)
203 ui_browser__write_nstring(browser, " ", width - pcnt_width - cycles_width);
204 else if (dl->offset == -1) {
205 if (dl->line_nr && annotate_browser__opts.show_linenr)
206 printed = scnprintf(bf, sizeof(bf), "%-*d ",
207 ab->addr_width + 1, dl->line_nr);
209 printed = scnprintf(bf, sizeof(bf), "%*s ",
210 ab->addr_width, " ");
211 ui_browser__write_nstring(browser, bf, printed);
212 ui_browser__write_nstring(browser, dl->line, width - printed - pcnt_width - cycles_width + 1);
214 u64 addr = dl->offset;
217 if (!annotate_browser__opts.use_offset)
220 if (!annotate_browser__opts.use_offset) {
221 printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
223 if (bdl->jump_sources) {
224 if (annotate_browser__opts.show_nr_jumps) {
226 printed = scnprintf(bf, sizeof(bf), "%*d ",
229 prev = annotate_browser__set_jumps_percent_color(ab, bdl->jump_sources,
231 ui_browser__write_nstring(browser, bf, printed);
232 ui_browser__set_color(browser, prev);
235 printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
236 ab->target_width, addr);
238 printed = scnprintf(bf, sizeof(bf), "%*s ",
239 ab->addr_width, " ");
244 color = ui_browser__set_color(browser, HE_COLORSET_ADDR);
245 ui_browser__write_nstring(browser, bf, printed);
247 ui_browser__set_color(browser, color);
248 if (dl->ins.ops && dl->ins.ops->scnprintf) {
249 if (ins__is_jump(&dl->ins)) {
250 bool fwd = dl->ops.target.offset > dl->offset;
252 ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR :
254 SLsmg_write_char(' ');
255 } else if (ins__is_call(&dl->ins)) {
256 ui_browser__write_graph(browser, SLSMG_RARROW_CHAR);
257 SLsmg_write_char(' ');
258 } else if (ins__is_ret(&dl->ins)) {
259 ui_browser__write_graph(browser, SLSMG_LARROW_CHAR);
260 SLsmg_write_char(' ');
262 ui_browser__write_nstring(browser, " ", 2);
265 ui_browser__write_nstring(browser, " ", 2);
268 disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset);
269 ui_browser__write_nstring(browser, bf, width - pcnt_width - cycles_width - 3 - printed);
276 static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym)
278 if (!dl || !dl->ins.ops || !ins__is_jump(&dl->ins)
279 || !disasm_line__has_offset(dl)
280 || dl->ops.target.offset < 0
281 || dl->ops.target.offset >= (s64)symbol__size(sym))
287 static bool is_fused(struct annotate_browser *ab, struct disasm_line *cursor)
289 struct disasm_line *pos = list_prev_entry(cursor, node);
295 if (ins__is_lock(&pos->ins))
296 name = pos->ops.locked.ins.name;
298 name = pos->ins.name;
300 if (!name || !cursor->ins.name)
303 return ins__is_fused(ab->arch, name, cursor->ins.name);
306 static void annotate_browser__draw_current_jump(struct ui_browser *browser)
308 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
309 struct disasm_line *cursor = ab->selection, *target;
310 struct browser_disasm_line *btarget, *bcursor;
311 unsigned int from, to;
312 struct map_symbol *ms = ab->b.priv;
313 struct symbol *sym = ms->sym;
314 u8 pcnt_width = annotate_browser__pcnt_width(ab);
317 /* PLT symbols contain external offsets */
318 if (strstr(sym->name, "@plt"))
321 if (!disasm_line__is_valid_jump(cursor, sym))
324 target = ab->offsets[cursor->ops.target.offset];
328 bcursor = disasm_line__browser(cursor);
329 btarget = disasm_line__browser(target);
331 if (annotate_browser__opts.hide_src_code) {
332 from = bcursor->idx_asm;
333 to = btarget->idx_asm;
335 from = (u64)bcursor->idx;
336 to = (u64)btarget->idx;
340 width = IPC_WIDTH + CYCLES_WIDTH;
342 ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
343 __ui_browser__line_arrow(browser,
344 pcnt_width + 2 + ab->addr_width + width,
347 if (is_fused(ab, cursor)) {
348 ui_browser__mark_fused(browser,
349 pcnt_width + 3 + ab->addr_width + width,
351 to > from ? true : false);
355 static unsigned int annotate_browser__refresh(struct ui_browser *browser)
357 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
358 int ret = ui_browser__list_head_refresh(browser);
359 int pcnt_width = annotate_browser__pcnt_width(ab);
361 if (annotate_browser__opts.jump_arrows)
362 annotate_browser__draw_current_jump(browser);
364 ui_browser__set_color(browser, HE_COLORSET_NORMAL);
365 __ui_browser__vline(browser, pcnt_width, 0, browser->height - 1);
369 static int disasm__cmp(struct browser_disasm_line *a,
370 struct browser_disasm_line *b, int nr_pcnt)
374 for (i = 0; i < nr_pcnt; i++) {
375 if (a->samples[i].percent == b->samples[i].percent)
377 return a->samples[i].percent < b->samples[i].percent;
382 static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl,
385 struct rb_node **p = &root->rb_node;
386 struct rb_node *parent = NULL;
387 struct browser_disasm_line *l;
391 l = rb_entry(parent, struct browser_disasm_line, rb_node);
393 if (disasm__cmp(bdl, l, nr_events))
398 rb_link_node(&bdl->rb_node, parent, p);
399 rb_insert_color(&bdl->rb_node, root);
402 static void annotate_browser__set_top(struct annotate_browser *browser,
403 struct disasm_line *pos, u32 idx)
407 ui_browser__refresh_dimensions(&browser->b);
408 back = browser->b.height / 2;
409 browser->b.top_idx = browser->b.index = idx;
411 while (browser->b.top_idx != 0 && back != 0) {
412 pos = list_entry(pos->node.prev, struct disasm_line, node);
414 if (disasm_line__filter(&browser->b, &pos->node))
417 --browser->b.top_idx;
421 browser->b.top = pos;
422 browser->b.navkeypressed = true;
425 static void annotate_browser__set_rb_top(struct annotate_browser *browser,
428 struct browser_disasm_line *bpos;
429 struct disasm_line *pos;
432 bpos = rb_entry(nd, struct browser_disasm_line, rb_node);
433 pos = ((struct disasm_line *)bpos) - 1;
435 if (annotate_browser__opts.hide_src_code)
437 annotate_browser__set_top(browser, pos, idx);
438 browser->curr_hot = nd;
441 static void annotate_browser__calc_percent(struct annotate_browser *browser,
442 struct perf_evsel *evsel)
444 struct map_symbol *ms = browser->b.priv;
445 struct symbol *sym = ms->sym;
446 struct annotation *notes = symbol__annotation(sym);
447 struct disasm_line *pos, *next;
448 s64 len = symbol__size(sym);
450 browser->entries = RB_ROOT;
452 pthread_mutex_lock(¬es->lock);
454 list_for_each_entry(pos, ¬es->src->source, node) {
455 struct browser_disasm_line *bpos = disasm_line__browser(pos);
456 const char *path = NULL;
457 double max_percent = 0.0;
460 if (pos->offset == -1) {
461 RB_CLEAR_NODE(&bpos->rb_node);
465 next = disasm__get_next_ip_line(¬es->src->source, pos);
467 for (i = 0; i < browser->nr_events; i++) {
468 struct sym_hist_entry sample;
470 bpos->samples[i].percent = disasm__calc_percent(notes,
473 next ? next->offset : len,
475 bpos->samples[i].he = sample;
477 if (max_percent < bpos->samples[i].percent)
478 max_percent = bpos->samples[i].percent;
481 if (max_percent < 0.01 && pos->ipc == 0) {
482 RB_CLEAR_NODE(&bpos->rb_node);
485 disasm_rb_tree__insert(&browser->entries, bpos,
488 pthread_mutex_unlock(¬es->lock);
490 browser->curr_hot = rb_last(&browser->entries);
493 static bool annotate_browser__toggle_source(struct annotate_browser *browser)
495 struct disasm_line *dl;
496 struct browser_disasm_line *bdl;
497 off_t offset = browser->b.index - browser->b.top_idx;
499 browser->b.seek(&browser->b, offset, SEEK_CUR);
500 dl = list_entry(browser->b.top, struct disasm_line, node);
501 bdl = disasm_line__browser(dl);
503 if (annotate_browser__opts.hide_src_code) {
504 if (bdl->idx_asm < offset)
507 browser->b.nr_entries = browser->nr_entries;
508 annotate_browser__opts.hide_src_code = false;
509 browser->b.seek(&browser->b, -offset, SEEK_CUR);
510 browser->b.top_idx = bdl->idx - offset;
511 browser->b.index = bdl->idx;
513 if (bdl->idx_asm < 0) {
514 ui_helpline__puts("Only available for assembly lines.");
515 browser->b.seek(&browser->b, -offset, SEEK_CUR);
519 if (bdl->idx_asm < offset)
520 offset = bdl->idx_asm;
522 browser->b.nr_entries = browser->nr_asm_entries;
523 annotate_browser__opts.hide_src_code = true;
524 browser->b.seek(&browser->b, -offset, SEEK_CUR);
525 browser->b.top_idx = bdl->idx_asm - offset;
526 browser->b.index = bdl->idx_asm;
532 static void annotate_browser__init_asm_mode(struct annotate_browser *browser)
534 ui_browser__reset_index(&browser->b);
535 browser->b.nr_entries = browser->nr_asm_entries;
538 #define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
540 static int sym_title(struct symbol *sym, struct map *map, char *title,
543 return snprintf(title, sz, "%s %s", sym->name, map->dso->long_name);
546 static bool annotate_browser__callq(struct annotate_browser *browser,
547 struct perf_evsel *evsel,
548 struct hist_browser_timer *hbt)
550 struct map_symbol *ms = browser->b.priv;
551 struct disasm_line *dl = browser->selection;
552 struct annotation *notes;
553 struct addr_map_symbol target = {
555 .addr = map__objdump_2mem(ms->map, dl->ops.target.addr),
557 char title[SYM_TITLE_MAX_SIZE];
559 if (!ins__is_call(&dl->ins))
562 if (map_groups__find_ams(&target) ||
563 map__rip_2objdump(target.map, target.map->map_ip(target.map,
565 dl->ops.target.addr) {
566 ui_helpline__puts("The called function was not found.");
570 notes = symbol__annotation(target.sym);
571 pthread_mutex_lock(¬es->lock);
573 if (notes->src == NULL && symbol__alloc_hist(target.sym) < 0) {
574 pthread_mutex_unlock(¬es->lock);
575 ui__warning("Not enough memory for annotating '%s' symbol!\n",
580 pthread_mutex_unlock(¬es->lock);
581 symbol__tui_annotate(target.sym, target.map, evsel, hbt);
582 sym_title(ms->sym, ms->map, title, sizeof(title));
583 ui_browser__show_title(&browser->b, title);
588 struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
589 s64 offset, s64 *idx)
591 struct map_symbol *ms = browser->b.priv;
592 struct symbol *sym = ms->sym;
593 struct annotation *notes = symbol__annotation(sym);
594 struct disasm_line *pos;
597 list_for_each_entry(pos, ¬es->src->source, node) {
598 if (pos->offset == offset)
600 if (!disasm_line__filter(&browser->b, &pos->node))
607 static bool annotate_browser__jump(struct annotate_browser *browser)
609 struct disasm_line *dl = browser->selection;
613 if (!ins__is_jump(&dl->ins))
616 offset = dl->ops.target.offset;
617 dl = annotate_browser__find_offset(browser, offset, &idx);
619 ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
623 annotate_browser__set_top(browser, dl, idx);
629 struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
632 struct map_symbol *ms = browser->b.priv;
633 struct symbol *sym = ms->sym;
634 struct annotation *notes = symbol__annotation(sym);
635 struct disasm_line *pos = browser->selection;
637 *idx = browser->b.index;
638 list_for_each_entry_continue(pos, ¬es->src->source, node) {
639 if (disasm_line__filter(&browser->b, &pos->node))
644 if (pos->line && strstr(pos->line, s) != NULL)
651 static bool __annotate_browser__search(struct annotate_browser *browser)
653 struct disasm_line *dl;
656 dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
658 ui_helpline__puts("String not found!");
662 annotate_browser__set_top(browser, dl, idx);
663 browser->searching_backwards = false;
668 struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
671 struct map_symbol *ms = browser->b.priv;
672 struct symbol *sym = ms->sym;
673 struct annotation *notes = symbol__annotation(sym);
674 struct disasm_line *pos = browser->selection;
676 *idx = browser->b.index;
677 list_for_each_entry_continue_reverse(pos, ¬es->src->source, node) {
678 if (disasm_line__filter(&browser->b, &pos->node))
683 if (pos->line && strstr(pos->line, s) != NULL)
690 static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
692 struct disasm_line *dl;
695 dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
697 ui_helpline__puts("String not found!");
701 annotate_browser__set_top(browser, dl, idx);
702 browser->searching_backwards = true;
706 static bool annotate_browser__search_window(struct annotate_browser *browser,
709 if (ui_browser__input_window("Search", "String: ", browser->search_bf,
710 "ENTER: OK, ESC: Cancel",
711 delay_secs * 2) != K_ENTER ||
712 !*browser->search_bf)
718 static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
720 if (annotate_browser__search_window(browser, delay_secs))
721 return __annotate_browser__search(browser);
726 static bool annotate_browser__continue_search(struct annotate_browser *browser,
729 if (!*browser->search_bf)
730 return annotate_browser__search(browser, delay_secs);
732 return __annotate_browser__search(browser);
735 static bool annotate_browser__search_reverse(struct annotate_browser *browser,
738 if (annotate_browser__search_window(browser, delay_secs))
739 return __annotate_browser__search_reverse(browser);
745 bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
748 if (!*browser->search_bf)
749 return annotate_browser__search_reverse(browser, delay_secs);
751 return __annotate_browser__search_reverse(browser);
754 static void annotate_browser__update_addr_width(struct annotate_browser *browser)
756 if (annotate_browser__opts.use_offset)
757 browser->target_width = browser->min_addr_width;
759 browser->target_width = browser->max_addr_width;
761 browser->addr_width = browser->target_width;
763 if (annotate_browser__opts.show_nr_jumps)
764 browser->addr_width += browser->jumps_width + 1;
767 static int annotate_browser__run(struct annotate_browser *browser,
768 struct perf_evsel *evsel,
769 struct hist_browser_timer *hbt)
771 struct rb_node *nd = NULL;
772 struct map_symbol *ms = browser->b.priv;
773 struct symbol *sym = ms->sym;
774 const char *help = "Press 'h' for help on key bindings";
775 int delay_secs = hbt ? hbt->refresh : 0;
777 char title[SYM_TITLE_MAX_SIZE];
779 sym_title(sym, ms->map, title, sizeof(title));
780 if (ui_browser__show(&browser->b, title, help) < 0)
783 annotate_browser__calc_percent(browser, evsel);
785 if (browser->curr_hot) {
786 annotate_browser__set_rb_top(browser, browser->curr_hot);
787 browser->b.navkeypressed = false;
790 nd = browser->curr_hot;
793 key = ui_browser__run(&browser->b, delay_secs);
795 if (delay_secs != 0) {
796 annotate_browser__calc_percent(browser, evsel);
798 * Current line focus got out of the list of most active
799 * lines, NULL it so that if TAB|UNTAB is pressed, we
800 * move to curr_hot (current hottest line).
802 if (nd != NULL && RB_EMPTY_NODE(nd))
809 hbt->timer(hbt->arg);
812 symbol__annotate_decay_histogram(sym, evsel->idx);
818 nd = rb_last(&browser->entries);
820 nd = browser->curr_hot;
826 nd = rb_first(&browser->entries);
828 nd = browser->curr_hot;
832 ui_browser__help_window(&browser->b,
834 "PGDN/SPACE Navigate\n"
835 "q/ESC/CTRL+C Exit\n\n"
836 "ENTER Go to target\n"
838 "H Go to hottest instruction\n"
839 "TAB/shift+TAB Cycle thru hottest instructions\n"
840 "j Toggle showing jump to target arrows\n"
841 "J Toggle showing number of jump sources on targets\n"
842 "n Search next string\n"
843 "o Toggle disassembler output/simplified view\n"
844 "s Toggle source code view\n"
845 "t Circulate percent, total period, samples view\n"
847 "k Toggle line numbers\n"
848 "r Run available scripts\n"
849 "? Search string backwards\n");
857 annotate_browser__opts.show_linenr =
858 !annotate_browser__opts.show_linenr;
861 nd = browser->curr_hot;
864 if (annotate_browser__toggle_source(browser))
865 ui_helpline__puts(help);
868 annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset;
869 annotate_browser__update_addr_width(browser);
872 annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows;
875 annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps;
876 annotate_browser__update_addr_width(browser);
879 if (annotate_browser__search(browser, delay_secs)) {
881 ui_helpline__puts(help);
885 if (browser->searching_backwards ?
886 annotate_browser__continue_search_reverse(browser, delay_secs) :
887 annotate_browser__continue_search(browser, delay_secs))
891 if (annotate_browser__search_reverse(browser, delay_secs))
897 ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
898 seq++, browser->b.nr_entries,
902 browser->nr_asm_entries);
907 if (browser->selection == NULL)
908 ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
909 else if (browser->selection->offset == -1)
910 ui_helpline__puts("Actions are only available for assembly lines.");
911 else if (!browser->selection->ins.ops)
913 else if (ins__is_ret(&browser->selection->ins))
915 else if (!(annotate_browser__jump(browser) ||
916 annotate_browser__callq(browser, evsel, hbt))) {
918 ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
922 if (annotate_browser__opts.show_total_period) {
923 annotate_browser__opts.show_total_period = false;
924 annotate_browser__opts.show_nr_samples = true;
925 } else if (annotate_browser__opts.show_nr_samples)
926 annotate_browser__opts.show_nr_samples = false;
928 annotate_browser__opts.show_total_period = true;
929 annotate_browser__update_addr_width(browser);
941 annotate_browser__set_rb_top(browser, nd);
944 ui_browser__hide(&browser->b);
948 int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
949 struct hist_browser_timer *hbt)
951 /* Set default value for show_total_period and show_nr_samples */
952 annotate_browser__opts.show_total_period =
953 symbol_conf.show_total_period;
954 annotate_browser__opts.show_nr_samples =
955 symbol_conf.show_nr_samples;
957 return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
960 int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
961 struct hist_browser_timer *hbt)
963 /* reset abort key so that it can get Ctrl-C as a key */
965 SLang_init_tty(0, 0, 0);
967 return map_symbol__tui_annotate(&he->ms, evsel, hbt);
971 static unsigned count_insn(struct annotate_browser *browser, u64 start, u64 end)
976 for (offset = start; offset <= end; offset++) {
977 if (browser->offsets[offset])
983 static void count_and_fill(struct annotate_browser *browser, u64 start, u64 end,
989 n_insn = count_insn(browser, start, end);
990 if (n_insn && ch->num && ch->cycles) {
991 float ipc = n_insn / ((double)ch->cycles / (double)ch->num);
993 /* Hide data when there are too many overlaps. */
994 if (ch->reset >= 0x7fff || ch->reset >= ch->num / 2)
997 for (offset = start; offset <= end; offset++) {
998 struct disasm_line *dl = browser->offsets[offset];
1007 * This should probably be in util/annotate.c to share with the tty
1008 * annotate, but right now we need the per byte offsets arrays,
1009 * which are only here.
1011 static void annotate__compute_ipc(struct annotate_browser *browser, size_t size,
1015 struct annotation *notes = symbol__annotation(sym);
1017 if (!notes->src || !notes->src->cycles_hist)
1020 pthread_mutex_lock(¬es->lock);
1021 for (offset = 0; offset < size; ++offset) {
1022 struct cyc_hist *ch;
1024 ch = ¬es->src->cycles_hist[offset];
1025 if (ch && ch->cycles) {
1026 struct disasm_line *dl;
1029 count_and_fill(browser, ch->start, offset, ch);
1030 dl = browser->offsets[offset];
1031 if (dl && ch->num_aggr)
1032 dl->cycles = ch->cycles_aggr / ch->num_aggr;
1033 browser->have_cycles = true;
1036 pthread_mutex_unlock(¬es->lock);
1039 static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
1043 struct map_symbol *ms = browser->b.priv;
1044 struct symbol *sym = ms->sym;
1046 /* PLT symbols contain external offsets */
1047 if (strstr(sym->name, "@plt"))
1050 for (offset = 0; offset < size; ++offset) {
1051 struct disasm_line *dl = browser->offsets[offset], *dlt;
1052 struct browser_disasm_line *bdlt;
1054 if (!disasm_line__is_valid_jump(dl, sym))
1057 dlt = browser->offsets[dl->ops.target.offset];
1059 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
1060 * have to adjust to the previous offset?
1065 bdlt = disasm_line__browser(dlt);
1066 if (++bdlt->jump_sources > browser->max_jump_sources)
1067 browser->max_jump_sources = bdlt->jump_sources;
1069 ++browser->nr_jumps;
1073 static inline int width_jumps(int n)
1082 int symbol__tui_annotate(struct symbol *sym, struct map *map,
1083 struct perf_evsel *evsel,
1084 struct hist_browser_timer *hbt)
1086 struct disasm_line *pos, *n;
1087 struct annotation *notes;
1089 struct map_symbol ms = {
1093 struct annotate_browser browser = {
1095 .refresh = annotate_browser__refresh,
1096 .seek = ui_browser__list_head_seek,
1097 .write = annotate_browser__write,
1098 .filter = disasm_line__filter,
1100 .use_navkeypressed = true,
1105 size_t sizeof_bdl = sizeof(struct browser_disasm_line);
1110 size = symbol__size(sym);
1112 if (map->dso->annotate_warned)
1115 browser.offsets = zalloc(size * sizeof(struct disasm_line *));
1116 if (browser.offsets == NULL) {
1117 ui__error("Not enough memory!");
1121 if (perf_evsel__is_group_event(evsel)) {
1122 nr_pcnt = evsel->nr_members;
1123 sizeof_bdl += sizeof(struct disasm_line_samples) *
1127 err = symbol__disassemble(sym, map, perf_evsel__env_arch(evsel),
1128 sizeof_bdl, &browser.arch,
1129 perf_evsel__env_cpuid(evsel));
1132 symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg));
1133 ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
1134 goto out_free_offsets;
1137 ui_helpline__push("Press ESC to exit");
1139 notes = symbol__annotation(sym);
1140 browser.start = map__rip_2objdump(map, sym->start);
1142 list_for_each_entry(pos, ¬es->src->source, node) {
1143 struct browser_disasm_line *bpos;
1144 size_t line_len = strlen(pos->line);
1146 if (browser.b.width < line_len)
1147 browser.b.width = line_len;
1148 bpos = disasm_line__browser(pos);
1149 bpos->idx = browser.nr_entries++;
1150 if (pos->offset != -1) {
1151 bpos->idx_asm = browser.nr_asm_entries++;
1153 * FIXME: short term bandaid to cope with assembly
1154 * routines that comes with labels in the same column
1155 * as the address in objdump, sigh.
1157 * E.g. copy_user_generic_unrolled
1159 if (pos->offset < (s64)size)
1160 browser.offsets[pos->offset] = pos;
1165 annotate_browser__mark_jump_targets(&browser, size);
1166 annotate__compute_ipc(&browser, size, sym);
1168 browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
1169 browser.max_addr_width = hex_width(sym->end);
1170 browser.jumps_width = width_jumps(browser.max_jump_sources);
1171 browser.nr_events = nr_pcnt;
1172 browser.b.nr_entries = browser.nr_entries;
1173 browser.b.entries = ¬es->src->source,
1174 browser.b.width += 18; /* Percentage */
1176 if (annotate_browser__opts.hide_src_code)
1177 annotate_browser__init_asm_mode(&browser);
1179 annotate_browser__update_addr_width(&browser);
1181 ret = annotate_browser__run(&browser, evsel, hbt);
1182 list_for_each_entry_safe(pos, n, ¬es->src->source, node) {
1183 list_del(&pos->node);
1184 disasm_line__free(pos);
1188 free(browser.offsets);
1192 #define ANNOTATE_CFG(n) \
1193 { .name = #n, .value = &annotate_browser__opts.n, }
1196 * Keep the entries sorted, they are bsearch'ed
1198 static struct annotate_config {
1201 } annotate__configs[] = {
1202 ANNOTATE_CFG(hide_src_code),
1203 ANNOTATE_CFG(jump_arrows),
1204 ANNOTATE_CFG(show_linenr),
1205 ANNOTATE_CFG(show_nr_jumps),
1206 ANNOTATE_CFG(show_nr_samples),
1207 ANNOTATE_CFG(show_total_period),
1208 ANNOTATE_CFG(use_offset),
1213 static int annotate_config__cmp(const void *name, const void *cfgp)
1215 const struct annotate_config *cfg = cfgp;
1217 return strcmp(name, cfg->name);
1220 static int annotate__config(const char *var, const char *value,
1221 void *data __maybe_unused)
1223 struct annotate_config *cfg;
1226 if (!strstarts(var, "annotate."))
1230 cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1231 sizeof(struct annotate_config), annotate_config__cmp);
1234 ui__warning("%s variable unknown, ignoring...", var);
1236 *cfg->value = perf_config_bool(name, value);
1240 void annotate_browser__init(void)
1242 perf_config(annotate__config, NULL);