Merge branch 'locking-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[cris-mirror.git] / tools / perf / ui / browsers / annotate.c
blob2864279751122aa8efc518778759cc04589e12ba
1 // SPDX-License-Identifier: GPL-2.0
2 #include "../../util/util.h"
3 #include "../browser.h"
4 #include "../helpline.h"
5 #include "../ui.h"
6 #include "../util.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"
14 #include <inttypes.h>
15 #include <pthread.h>
16 #include <linux/kernel.h>
17 #include <linux/string.h>
18 #include <sys/ttydefaults.h>
20 struct disasm_line_samples {
21 double percent;
22 struct sym_hist_entry he;
25 #define IPC_WIDTH 6
26 #define CYCLES_WIDTH 6
28 struct browser_line {
29 u32 idx;
30 int idx_asm;
31 int jump_sources;
34 static struct annotate_browser_opt {
35 bool hide_src_code,
36 use_offset,
37 jump_arrows,
38 show_linenr,
39 show_nr_jumps,
40 show_nr_samples,
41 show_total_period;
42 } annotate_browser__opts = {
43 .use_offset = true,
44 .jump_arrows = true,
47 struct arch;
49 struct annotate_browser {
50 struct ui_browser b;
51 struct rb_root entries;
52 struct rb_node *curr_hot;
53 struct annotation_line *selection;
54 struct annotation_line **offsets;
55 struct arch *arch;
56 int nr_events;
57 u64 start;
58 int nr_asm_entries;
59 int nr_entries;
60 int max_jump_sources;
61 int nr_jumps;
62 bool searching_backwards;
63 bool have_cycles;
64 u8 addr_width;
65 u8 jumps_width;
66 u8 target_width;
67 u8 min_addr_width;
68 u8 max_addr_width;
69 char search_bf[128];
72 static inline struct browser_line *browser_line(struct annotation_line *al)
74 void *ptr = al;
76 ptr = container_of(al, struct disasm_line, al);
77 return ptr - sizeof(struct browser_line);
80 static bool disasm_line__filter(struct ui_browser *browser __maybe_unused,
81 void *entry)
83 if (annotate_browser__opts.hide_src_code) {
84 struct annotation_line *al = list_entry(entry, struct annotation_line, node);
86 return al->offset == -1;
89 return false;
92 static int annotate_browser__jumps_percent_color(struct annotate_browser *browser,
93 int nr, bool current)
95 if (current && (!browser->b.use_navkeypressed || browser->b.navkeypressed))
96 return HE_COLORSET_SELECTED;
97 if (nr == browser->max_jump_sources)
98 return HE_COLORSET_TOP;
99 if (nr > 1)
100 return HE_COLORSET_MEDIUM;
101 return HE_COLORSET_NORMAL;
104 static int annotate_browser__set_jumps_percent_color(struct annotate_browser *browser,
105 int nr, bool current)
107 int color = annotate_browser__jumps_percent_color(browser, nr, current);
108 return ui_browser__set_color(&browser->b, color);
111 static int annotate_browser__pcnt_width(struct annotate_browser *ab)
113 return (annotate_browser__opts.show_total_period ? 12 : 7) * ab->nr_events;
116 static int annotate_browser__cycles_width(struct annotate_browser *ab)
118 return ab->have_cycles ? IPC_WIDTH + CYCLES_WIDTH : 0;
121 static void disasm_line__write(struct disasm_line *dl, struct ui_browser *browser,
122 char *bf, size_t size)
124 if (dl->ins.ops && dl->ins.ops->scnprintf) {
125 if (ins__is_jump(&dl->ins)) {
126 bool fwd = dl->ops.target.offset > dl->al.offset;
128 ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR :
129 SLSMG_UARROW_CHAR);
130 SLsmg_write_char(' ');
131 } else if (ins__is_call(&dl->ins)) {
132 ui_browser__write_graph(browser, SLSMG_RARROW_CHAR);
133 SLsmg_write_char(' ');
134 } else if (ins__is_ret(&dl->ins)) {
135 ui_browser__write_graph(browser, SLSMG_LARROW_CHAR);
136 SLsmg_write_char(' ');
137 } else {
138 ui_browser__write_nstring(browser, " ", 2);
140 } else {
141 ui_browser__write_nstring(browser, " ", 2);
144 disasm_line__scnprintf(dl, bf, size, !annotate_browser__opts.use_offset);
147 static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
149 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
150 struct annotation_line *al = list_entry(entry, struct annotation_line, node);
151 struct browser_line *bl = browser_line(al);
152 bool current_entry = ui_browser__is_current_entry(browser, row);
153 bool change_color = (!annotate_browser__opts.hide_src_code &&
154 (!current_entry || (browser->use_navkeypressed &&
155 !browser->navkeypressed)));
156 int width = browser->width, printed;
157 int i, pcnt_width = annotate_browser__pcnt_width(ab),
158 cycles_width = annotate_browser__cycles_width(ab);
159 double percent_max = 0.0;
160 char bf[256];
161 bool show_title = false;
163 for (i = 0; i < ab->nr_events; i++) {
164 if (al->samples[i].percent > percent_max)
165 percent_max = al->samples[i].percent;
168 if ((row == 0) && (al->offset == -1 || percent_max == 0.0)) {
169 if (ab->have_cycles) {
170 if (al->ipc == 0.0 && al->cycles == 0)
171 show_title = true;
172 } else
173 show_title = true;
176 if (al->offset != -1 && percent_max != 0.0) {
177 for (i = 0; i < ab->nr_events; i++) {
178 ui_browser__set_percent_color(browser,
179 al->samples[i].percent,
180 current_entry);
181 if (annotate_browser__opts.show_total_period) {
182 ui_browser__printf(browser, "%11" PRIu64 " ",
183 al->samples[i].he.period);
184 } else if (annotate_browser__opts.show_nr_samples) {
185 ui_browser__printf(browser, "%6" PRIu64 " ",
186 al->samples[i].he.nr_samples);
187 } else {
188 ui_browser__printf(browser, "%6.2f ",
189 al->samples[i].percent);
192 } else {
193 ui_browser__set_percent_color(browser, 0, current_entry);
195 if (!show_title)
196 ui_browser__write_nstring(browser, " ", pcnt_width);
197 else {
198 ui_browser__printf(browser, "%*s", pcnt_width,
199 annotate_browser__opts.show_total_period ? "Period" :
200 annotate_browser__opts.show_nr_samples ? "Samples" : "Percent");
203 if (ab->have_cycles) {
204 if (al->ipc)
205 ui_browser__printf(browser, "%*.2f ", IPC_WIDTH - 1, al->ipc);
206 else if (!show_title)
207 ui_browser__write_nstring(browser, " ", IPC_WIDTH);
208 else
209 ui_browser__printf(browser, "%*s ", IPC_WIDTH - 1, "IPC");
211 if (al->cycles)
212 ui_browser__printf(browser, "%*" PRIu64 " ",
213 CYCLES_WIDTH - 1, al->cycles);
214 else if (!show_title)
215 ui_browser__write_nstring(browser, " ", CYCLES_WIDTH);
216 else
217 ui_browser__printf(browser, "%*s ", CYCLES_WIDTH - 1, "Cycle");
220 SLsmg_write_char(' ');
222 /* The scroll bar isn't being used */
223 if (!browser->navkeypressed)
224 width += 1;
226 if (!*al->line)
227 ui_browser__write_nstring(browser, " ", width - pcnt_width - cycles_width);
228 else if (al->offset == -1) {
229 if (al->line_nr && annotate_browser__opts.show_linenr)
230 printed = scnprintf(bf, sizeof(bf), "%-*d ",
231 ab->addr_width + 1, al->line_nr);
232 else
233 printed = scnprintf(bf, sizeof(bf), "%*s ",
234 ab->addr_width, " ");
235 ui_browser__write_nstring(browser, bf, printed);
236 ui_browser__write_nstring(browser, al->line, width - printed - pcnt_width - cycles_width + 1);
237 } else {
238 u64 addr = al->offset;
239 int color = -1;
241 if (!annotate_browser__opts.use_offset)
242 addr += ab->start;
244 if (!annotate_browser__opts.use_offset) {
245 printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
246 } else {
247 if (bl->jump_sources) {
248 if (annotate_browser__opts.show_nr_jumps) {
249 int prev;
250 printed = scnprintf(bf, sizeof(bf), "%*d ",
251 ab->jumps_width,
252 bl->jump_sources);
253 prev = annotate_browser__set_jumps_percent_color(ab, bl->jump_sources,
254 current_entry);
255 ui_browser__write_nstring(browser, bf, printed);
256 ui_browser__set_color(browser, prev);
259 printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
260 ab->target_width, addr);
261 } else {
262 printed = scnprintf(bf, sizeof(bf), "%*s ",
263 ab->addr_width, " ");
267 if (change_color)
268 color = ui_browser__set_color(browser, HE_COLORSET_ADDR);
269 ui_browser__write_nstring(browser, bf, printed);
270 if (change_color)
271 ui_browser__set_color(browser, color);
273 disasm_line__write(disasm_line(al), browser, bf, sizeof(bf));
275 ui_browser__write_nstring(browser, bf, width - pcnt_width - cycles_width - 3 - printed);
278 if (current_entry)
279 ab->selection = al;
282 static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym)
284 if (!dl || !dl->ins.ops || !ins__is_jump(&dl->ins)
285 || !disasm_line__has_offset(dl)
286 || dl->ops.target.offset < 0
287 || dl->ops.target.offset >= (s64)symbol__size(sym))
288 return false;
290 return true;
293 static bool is_fused(struct annotate_browser *ab, struct disasm_line *cursor)
295 struct disasm_line *pos = list_prev_entry(cursor, al.node);
296 const char *name;
298 if (!pos)
299 return false;
301 if (ins__is_lock(&pos->ins))
302 name = pos->ops.locked.ins.name;
303 else
304 name = pos->ins.name;
306 if (!name || !cursor->ins.name)
307 return false;
309 return ins__is_fused(ab->arch, name, cursor->ins.name);
312 static void annotate_browser__draw_current_jump(struct ui_browser *browser)
314 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
315 struct disasm_line *cursor = disasm_line(ab->selection);
316 struct annotation_line *target;
317 struct browser_line *btarget, *bcursor;
318 unsigned int from, to;
319 struct map_symbol *ms = ab->b.priv;
320 struct symbol *sym = ms->sym;
321 u8 pcnt_width = annotate_browser__pcnt_width(ab);
323 /* PLT symbols contain external offsets */
324 if (strstr(sym->name, "@plt"))
325 return;
327 if (!disasm_line__is_valid_jump(cursor, sym))
328 return;
330 target = ab->offsets[cursor->ops.target.offset];
332 bcursor = browser_line(&cursor->al);
333 btarget = browser_line(target);
335 if (annotate_browser__opts.hide_src_code) {
336 from = bcursor->idx_asm;
337 to = btarget->idx_asm;
338 } else {
339 from = (u64)bcursor->idx;
340 to = (u64)btarget->idx;
343 ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
344 __ui_browser__line_arrow(browser, pcnt_width + 2 + ab->addr_width,
345 from, to);
347 if (is_fused(ab, cursor)) {
348 ui_browser__mark_fused(browser,
349 pcnt_width + 3 + ab->addr_width,
350 from - 1,
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);
366 return ret;
369 static int disasm__cmp(struct annotation_line *a, struct annotation_line *b)
371 int i;
373 for (i = 0; i < a->samples_nr; i++) {
374 if (a->samples[i].percent == b->samples[i].percent)
375 continue;
376 return a->samples[i].percent < b->samples[i].percent;
378 return 0;
381 static void disasm_rb_tree__insert(struct rb_root *root, struct annotation_line *al)
383 struct rb_node **p = &root->rb_node;
384 struct rb_node *parent = NULL;
385 struct annotation_line *l;
387 while (*p != NULL) {
388 parent = *p;
389 l = rb_entry(parent, struct annotation_line, rb_node);
391 if (disasm__cmp(al, l))
392 p = &(*p)->rb_left;
393 else
394 p = &(*p)->rb_right;
396 rb_link_node(&al->rb_node, parent, p);
397 rb_insert_color(&al->rb_node, root);
400 static void annotate_browser__set_top(struct annotate_browser *browser,
401 struct annotation_line *pos, u32 idx)
403 unsigned back;
405 ui_browser__refresh_dimensions(&browser->b);
406 back = browser->b.height / 2;
407 browser->b.top_idx = browser->b.index = idx;
409 while (browser->b.top_idx != 0 && back != 0) {
410 pos = list_entry(pos->node.prev, struct annotation_line, node);
412 if (disasm_line__filter(&browser->b, &pos->node))
413 continue;
415 --browser->b.top_idx;
416 --back;
419 browser->b.top = pos;
420 browser->b.navkeypressed = true;
423 static void annotate_browser__set_rb_top(struct annotate_browser *browser,
424 struct rb_node *nd)
426 struct browser_line *bpos;
427 struct annotation_line *pos;
428 u32 idx;
430 pos = rb_entry(nd, struct annotation_line, rb_node);
431 bpos = browser_line(pos);
433 idx = bpos->idx;
434 if (annotate_browser__opts.hide_src_code)
435 idx = bpos->idx_asm;
436 annotate_browser__set_top(browser, pos, idx);
437 browser->curr_hot = nd;
440 static void annotate_browser__calc_percent(struct annotate_browser *browser,
441 struct perf_evsel *evsel)
443 struct map_symbol *ms = browser->b.priv;
444 struct symbol *sym = ms->sym;
445 struct annotation *notes = symbol__annotation(sym);
446 struct disasm_line *pos;
448 browser->entries = RB_ROOT;
450 pthread_mutex_lock(&notes->lock);
452 symbol__calc_percent(sym, evsel);
454 list_for_each_entry(pos, &notes->src->source, al.node) {
455 double max_percent = 0.0;
456 int i;
458 if (pos->al.offset == -1) {
459 RB_CLEAR_NODE(&pos->al.rb_node);
460 continue;
463 for (i = 0; i < pos->al.samples_nr; i++) {
464 struct annotation_data *sample = &pos->al.samples[i];
466 if (max_percent < sample->percent)
467 max_percent = sample->percent;
470 if (max_percent < 0.01 && pos->al.ipc == 0) {
471 RB_CLEAR_NODE(&pos->al.rb_node);
472 continue;
474 disasm_rb_tree__insert(&browser->entries, &pos->al);
476 pthread_mutex_unlock(&notes->lock);
478 browser->curr_hot = rb_last(&browser->entries);
481 static bool annotate_browser__toggle_source(struct annotate_browser *browser)
483 struct annotation_line *al;
484 struct browser_line *bl;
485 off_t offset = browser->b.index - browser->b.top_idx;
487 browser->b.seek(&browser->b, offset, SEEK_CUR);
488 al = list_entry(browser->b.top, struct annotation_line, node);
489 bl = browser_line(al);
491 if (annotate_browser__opts.hide_src_code) {
492 if (bl->idx_asm < offset)
493 offset = bl->idx;
495 browser->b.nr_entries = browser->nr_entries;
496 annotate_browser__opts.hide_src_code = false;
497 browser->b.seek(&browser->b, -offset, SEEK_CUR);
498 browser->b.top_idx = bl->idx - offset;
499 browser->b.index = bl->idx;
500 } else {
501 if (bl->idx_asm < 0) {
502 ui_helpline__puts("Only available for assembly lines.");
503 browser->b.seek(&browser->b, -offset, SEEK_CUR);
504 return false;
507 if (bl->idx_asm < offset)
508 offset = bl->idx_asm;
510 browser->b.nr_entries = browser->nr_asm_entries;
511 annotate_browser__opts.hide_src_code = true;
512 browser->b.seek(&browser->b, -offset, SEEK_CUR);
513 browser->b.top_idx = bl->idx_asm - offset;
514 browser->b.index = bl->idx_asm;
517 return true;
520 static void annotate_browser__init_asm_mode(struct annotate_browser *browser)
522 ui_browser__reset_index(&browser->b);
523 browser->b.nr_entries = browser->nr_asm_entries;
526 #define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
528 static int sym_title(struct symbol *sym, struct map *map, char *title,
529 size_t sz)
531 return snprintf(title, sz, "%s %s", sym->name, map->dso->long_name);
534 static bool annotate_browser__callq(struct annotate_browser *browser,
535 struct perf_evsel *evsel,
536 struct hist_browser_timer *hbt)
538 struct map_symbol *ms = browser->b.priv;
539 struct disasm_line *dl = disasm_line(browser->selection);
540 struct annotation *notes;
541 struct addr_map_symbol target = {
542 .map = ms->map,
543 .addr = map__objdump_2mem(ms->map, dl->ops.target.addr),
545 char title[SYM_TITLE_MAX_SIZE];
547 if (!ins__is_call(&dl->ins))
548 return false;
550 if (map_groups__find_ams(&target) ||
551 map__rip_2objdump(target.map, target.map->map_ip(target.map,
552 target.addr)) !=
553 dl->ops.target.addr) {
554 ui_helpline__puts("The called function was not found.");
555 return true;
558 notes = symbol__annotation(target.sym);
559 pthread_mutex_lock(&notes->lock);
561 if (notes->src == NULL && symbol__alloc_hist(target.sym) < 0) {
562 pthread_mutex_unlock(&notes->lock);
563 ui__warning("Not enough memory for annotating '%s' symbol!\n",
564 target.sym->name);
565 return true;
568 pthread_mutex_unlock(&notes->lock);
569 symbol__tui_annotate(target.sym, target.map, evsel, hbt);
570 sym_title(ms->sym, ms->map, title, sizeof(title));
571 ui_browser__show_title(&browser->b, title);
572 return true;
575 static
576 struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
577 s64 offset, s64 *idx)
579 struct map_symbol *ms = browser->b.priv;
580 struct symbol *sym = ms->sym;
581 struct annotation *notes = symbol__annotation(sym);
582 struct disasm_line *pos;
584 *idx = 0;
585 list_for_each_entry(pos, &notes->src->source, al.node) {
586 if (pos->al.offset == offset)
587 return pos;
588 if (!disasm_line__filter(&browser->b, &pos->al.node))
589 ++*idx;
592 return NULL;
595 static bool annotate_browser__jump(struct annotate_browser *browser)
597 struct disasm_line *dl = disasm_line(browser->selection);
598 u64 offset;
599 s64 idx;
601 if (!ins__is_jump(&dl->ins))
602 return false;
604 offset = dl->ops.target.offset;
605 dl = annotate_browser__find_offset(browser, offset, &idx);
606 if (dl == NULL) {
607 ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
608 return true;
611 annotate_browser__set_top(browser, &dl->al, idx);
613 return true;
616 static
617 struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
618 char *s, s64 *idx)
620 struct map_symbol *ms = browser->b.priv;
621 struct symbol *sym = ms->sym;
622 struct annotation *notes = symbol__annotation(sym);
623 struct annotation_line *al = browser->selection;
625 *idx = browser->b.index;
626 list_for_each_entry_continue(al, &notes->src->source, node) {
627 if (disasm_line__filter(&browser->b, &al->node))
628 continue;
630 ++*idx;
632 if (al->line && strstr(al->line, s) != NULL)
633 return al;
636 return NULL;
639 static bool __annotate_browser__search(struct annotate_browser *browser)
641 struct annotation_line *al;
642 s64 idx;
644 al = annotate_browser__find_string(browser, browser->search_bf, &idx);
645 if (al == NULL) {
646 ui_helpline__puts("String not found!");
647 return false;
650 annotate_browser__set_top(browser, al, idx);
651 browser->searching_backwards = false;
652 return true;
655 static
656 struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
657 char *s, s64 *idx)
659 struct map_symbol *ms = browser->b.priv;
660 struct symbol *sym = ms->sym;
661 struct annotation *notes = symbol__annotation(sym);
662 struct annotation_line *al = browser->selection;
664 *idx = browser->b.index;
665 list_for_each_entry_continue_reverse(al, &notes->src->source, node) {
666 if (disasm_line__filter(&browser->b, &al->node))
667 continue;
669 --*idx;
671 if (al->line && strstr(al->line, s) != NULL)
672 return al;
675 return NULL;
678 static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
680 struct annotation_line *al;
681 s64 idx;
683 al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
684 if (al == NULL) {
685 ui_helpline__puts("String not found!");
686 return false;
689 annotate_browser__set_top(browser, al, idx);
690 browser->searching_backwards = true;
691 return true;
694 static bool annotate_browser__search_window(struct annotate_browser *browser,
695 int delay_secs)
697 if (ui_browser__input_window("Search", "String: ", browser->search_bf,
698 "ENTER: OK, ESC: Cancel",
699 delay_secs * 2) != K_ENTER ||
700 !*browser->search_bf)
701 return false;
703 return true;
706 static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
708 if (annotate_browser__search_window(browser, delay_secs))
709 return __annotate_browser__search(browser);
711 return false;
714 static bool annotate_browser__continue_search(struct annotate_browser *browser,
715 int delay_secs)
717 if (!*browser->search_bf)
718 return annotate_browser__search(browser, delay_secs);
720 return __annotate_browser__search(browser);
723 static bool annotate_browser__search_reverse(struct annotate_browser *browser,
724 int delay_secs)
726 if (annotate_browser__search_window(browser, delay_secs))
727 return __annotate_browser__search_reverse(browser);
729 return false;
732 static
733 bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
734 int delay_secs)
736 if (!*browser->search_bf)
737 return annotate_browser__search_reverse(browser, delay_secs);
739 return __annotate_browser__search_reverse(browser);
742 static void annotate_browser__update_addr_width(struct annotate_browser *browser)
744 if (annotate_browser__opts.use_offset)
745 browser->target_width = browser->min_addr_width;
746 else
747 browser->target_width = browser->max_addr_width;
749 browser->addr_width = browser->target_width;
751 if (annotate_browser__opts.show_nr_jumps)
752 browser->addr_width += browser->jumps_width + 1;
755 static int annotate_browser__run(struct annotate_browser *browser,
756 struct perf_evsel *evsel,
757 struct hist_browser_timer *hbt)
759 struct rb_node *nd = NULL;
760 struct map_symbol *ms = browser->b.priv;
761 struct symbol *sym = ms->sym;
762 const char *help = "Press 'h' for help on key bindings";
763 int delay_secs = hbt ? hbt->refresh : 0;
764 int key;
765 char title[SYM_TITLE_MAX_SIZE];
767 sym_title(sym, ms->map, title, sizeof(title));
768 if (ui_browser__show(&browser->b, title, help) < 0)
769 return -1;
771 annotate_browser__calc_percent(browser, evsel);
773 if (browser->curr_hot) {
774 annotate_browser__set_rb_top(browser, browser->curr_hot);
775 browser->b.navkeypressed = false;
778 nd = browser->curr_hot;
780 while (1) {
781 key = ui_browser__run(&browser->b, delay_secs);
783 if (delay_secs != 0) {
784 annotate_browser__calc_percent(browser, evsel);
786 * Current line focus got out of the list of most active
787 * lines, NULL it so that if TAB|UNTAB is pressed, we
788 * move to curr_hot (current hottest line).
790 if (nd != NULL && RB_EMPTY_NODE(nd))
791 nd = NULL;
794 switch (key) {
795 case K_TIMER:
796 if (hbt)
797 hbt->timer(hbt->arg);
799 if (delay_secs != 0)
800 symbol__annotate_decay_histogram(sym, evsel->idx);
801 continue;
802 case K_TAB:
803 if (nd != NULL) {
804 nd = rb_prev(nd);
805 if (nd == NULL)
806 nd = rb_last(&browser->entries);
807 } else
808 nd = browser->curr_hot;
809 break;
810 case K_UNTAB:
811 if (nd != NULL) {
812 nd = rb_next(nd);
813 if (nd == NULL)
814 nd = rb_first(&browser->entries);
815 } else
816 nd = browser->curr_hot;
817 break;
818 case K_F1:
819 case 'h':
820 ui_browser__help_window(&browser->b,
821 "UP/DOWN/PGUP\n"
822 "PGDN/SPACE Navigate\n"
823 "q/ESC/CTRL+C Exit\n\n"
824 "ENTER Go to target\n"
825 "ESC Exit\n"
826 "H Go to hottest instruction\n"
827 "TAB/shift+TAB Cycle thru hottest instructions\n"
828 "j Toggle showing jump to target arrows\n"
829 "J Toggle showing number of jump sources on targets\n"
830 "n Search next string\n"
831 "o Toggle disassembler output/simplified view\n"
832 "s Toggle source code view\n"
833 "t Circulate percent, total period, samples view\n"
834 "/ Search string\n"
835 "k Toggle line numbers\n"
836 "r Run available scripts\n"
837 "? Search string backwards\n");
838 continue;
839 case 'r':
841 script_browse(NULL);
842 continue;
844 case 'k':
845 annotate_browser__opts.show_linenr =
846 !annotate_browser__opts.show_linenr;
847 break;
848 case 'H':
849 nd = browser->curr_hot;
850 break;
851 case 's':
852 if (annotate_browser__toggle_source(browser))
853 ui_helpline__puts(help);
854 continue;
855 case 'o':
856 annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset;
857 annotate_browser__update_addr_width(browser);
858 continue;
859 case 'j':
860 annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows;
861 continue;
862 case 'J':
863 annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps;
864 annotate_browser__update_addr_width(browser);
865 continue;
866 case '/':
867 if (annotate_browser__search(browser, delay_secs)) {
868 show_help:
869 ui_helpline__puts(help);
871 continue;
872 case 'n':
873 if (browser->searching_backwards ?
874 annotate_browser__continue_search_reverse(browser, delay_secs) :
875 annotate_browser__continue_search(browser, delay_secs))
876 goto show_help;
877 continue;
878 case '?':
879 if (annotate_browser__search_reverse(browser, delay_secs))
880 goto show_help;
881 continue;
882 case 'D': {
883 static int seq;
884 ui_helpline__pop();
885 ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
886 seq++, browser->b.nr_entries,
887 browser->b.height,
888 browser->b.index,
889 browser->b.top_idx,
890 browser->nr_asm_entries);
892 continue;
893 case K_ENTER:
894 case K_RIGHT:
896 struct disasm_line *dl = disasm_line(browser->selection);
898 if (browser->selection == NULL)
899 ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
900 else if (browser->selection->offset == -1)
901 ui_helpline__puts("Actions are only available for assembly lines.");
902 else if (!dl->ins.ops)
903 goto show_sup_ins;
904 else if (ins__is_ret(&dl->ins))
905 goto out;
906 else if (!(annotate_browser__jump(browser) ||
907 annotate_browser__callq(browser, evsel, hbt))) {
908 show_sup_ins:
909 ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
911 continue;
913 case 't':
914 if (annotate_browser__opts.show_total_period) {
915 annotate_browser__opts.show_total_period = false;
916 annotate_browser__opts.show_nr_samples = true;
917 } else if (annotate_browser__opts.show_nr_samples)
918 annotate_browser__opts.show_nr_samples = false;
919 else
920 annotate_browser__opts.show_total_period = true;
921 annotate_browser__update_addr_width(browser);
922 continue;
923 case K_LEFT:
924 case K_ESC:
925 case 'q':
926 case CTRL('c'):
927 goto out;
928 default:
929 continue;
932 if (nd != NULL)
933 annotate_browser__set_rb_top(browser, nd);
935 out:
936 ui_browser__hide(&browser->b);
937 return key;
940 int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
941 struct hist_browser_timer *hbt)
943 /* Set default value for show_total_period and show_nr_samples */
944 annotate_browser__opts.show_total_period =
945 symbol_conf.show_total_period;
946 annotate_browser__opts.show_nr_samples =
947 symbol_conf.show_nr_samples;
949 return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
952 int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
953 struct hist_browser_timer *hbt)
955 /* reset abort key so that it can get Ctrl-C as a key */
956 SLang_reset_tty();
957 SLang_init_tty(0, 0, 0);
959 return map_symbol__tui_annotate(&he->ms, evsel, hbt);
963 static unsigned count_insn(struct annotate_browser *browser, u64 start, u64 end)
965 unsigned n_insn = 0;
966 u64 offset;
968 for (offset = start; offset <= end; offset++) {
969 if (browser->offsets[offset])
970 n_insn++;
972 return n_insn;
975 static void count_and_fill(struct annotate_browser *browser, u64 start, u64 end,
976 struct cyc_hist *ch)
978 unsigned n_insn;
979 u64 offset;
981 n_insn = count_insn(browser, start, end);
982 if (n_insn && ch->num && ch->cycles) {
983 float ipc = n_insn / ((double)ch->cycles / (double)ch->num);
985 /* Hide data when there are too many overlaps. */
986 if (ch->reset >= 0x7fff || ch->reset >= ch->num / 2)
987 return;
989 for (offset = start; offset <= end; offset++) {
990 struct annotation_line *al = browser->offsets[offset];
992 if (al)
993 al->ipc = ipc;
999 * This should probably be in util/annotate.c to share with the tty
1000 * annotate, but right now we need the per byte offsets arrays,
1001 * which are only here.
1003 static void annotate__compute_ipc(struct annotate_browser *browser, size_t size,
1004 struct symbol *sym)
1006 u64 offset;
1007 struct annotation *notes = symbol__annotation(sym);
1009 if (!notes->src || !notes->src->cycles_hist)
1010 return;
1012 pthread_mutex_lock(&notes->lock);
1013 for (offset = 0; offset < size; ++offset) {
1014 struct cyc_hist *ch;
1016 ch = &notes->src->cycles_hist[offset];
1017 if (ch && ch->cycles) {
1018 struct annotation_line *al;
1020 if (ch->have_start)
1021 count_and_fill(browser, ch->start, offset, ch);
1022 al = browser->offsets[offset];
1023 if (al && ch->num_aggr)
1024 al->cycles = ch->cycles_aggr / ch->num_aggr;
1025 browser->have_cycles = true;
1028 pthread_mutex_unlock(&notes->lock);
1031 static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
1032 size_t size)
1034 u64 offset;
1035 struct map_symbol *ms = browser->b.priv;
1036 struct symbol *sym = ms->sym;
1038 /* PLT symbols contain external offsets */
1039 if (strstr(sym->name, "@plt"))
1040 return;
1042 for (offset = 0; offset < size; ++offset) {
1043 struct annotation_line *al = browser->offsets[offset];
1044 struct disasm_line *dl;
1045 struct browser_line *blt;
1047 dl = disasm_line(al);
1049 if (!disasm_line__is_valid_jump(dl, sym))
1050 continue;
1052 al = browser->offsets[dl->ops.target.offset];
1055 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
1056 * have to adjust to the previous offset?
1058 if (al == NULL)
1059 continue;
1061 blt = browser_line(al);
1062 if (++blt->jump_sources > browser->max_jump_sources)
1063 browser->max_jump_sources = blt->jump_sources;
1065 ++browser->nr_jumps;
1069 static inline int width_jumps(int n)
1071 if (n >= 100)
1072 return 5;
1073 if (n / 10)
1074 return 2;
1075 return 1;
1078 int symbol__tui_annotate(struct symbol *sym, struct map *map,
1079 struct perf_evsel *evsel,
1080 struct hist_browser_timer *hbt)
1082 struct annotation_line *al;
1083 struct annotation *notes;
1084 size_t size;
1085 struct map_symbol ms = {
1086 .map = map,
1087 .sym = sym,
1089 struct annotate_browser browser = {
1090 .b = {
1091 .refresh = annotate_browser__refresh,
1092 .seek = ui_browser__list_head_seek,
1093 .write = annotate_browser__write,
1094 .filter = disasm_line__filter,
1095 .priv = &ms,
1096 .use_navkeypressed = true,
1099 int ret = -1, err;
1100 int nr_pcnt = 1;
1102 if (sym == NULL)
1103 return -1;
1105 size = symbol__size(sym);
1107 if (map->dso->annotate_warned)
1108 return -1;
1110 browser.offsets = zalloc(size * sizeof(struct annotation_line *));
1111 if (browser.offsets == NULL) {
1112 ui__error("Not enough memory!");
1113 return -1;
1116 if (perf_evsel__is_group_event(evsel))
1117 nr_pcnt = evsel->nr_members;
1119 err = symbol__annotate(sym, map, evsel, sizeof(struct browser_line), &browser.arch);
1120 if (err) {
1121 char msg[BUFSIZ];
1122 symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg));
1123 ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
1124 goto out_free_offsets;
1127 symbol__calc_percent(sym, evsel);
1129 ui_helpline__push("Press ESC to exit");
1131 notes = symbol__annotation(sym);
1132 browser.start = map__rip_2objdump(map, sym->start);
1134 list_for_each_entry(al, &notes->src->source, node) {
1135 struct browser_line *bpos;
1136 size_t line_len = strlen(al->line);
1138 if (browser.b.width < line_len)
1139 browser.b.width = line_len;
1140 bpos = browser_line(al);
1141 bpos->idx = browser.nr_entries++;
1142 if (al->offset != -1) {
1143 bpos->idx_asm = browser.nr_asm_entries++;
1145 * FIXME: short term bandaid to cope with assembly
1146 * routines that comes with labels in the same column
1147 * as the address in objdump, sigh.
1149 * E.g. copy_user_generic_unrolled
1151 if (al->offset < (s64)size)
1152 browser.offsets[al->offset] = al;
1153 } else
1154 bpos->idx_asm = -1;
1157 annotate_browser__mark_jump_targets(&browser, size);
1158 annotate__compute_ipc(&browser, size, sym);
1160 browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
1161 browser.max_addr_width = hex_width(sym->end);
1162 browser.jumps_width = width_jumps(browser.max_jump_sources);
1163 browser.nr_events = nr_pcnt;
1164 browser.b.nr_entries = browser.nr_entries;
1165 browser.b.entries = &notes->src->source,
1166 browser.b.width += 18; /* Percentage */
1168 if (annotate_browser__opts.hide_src_code)
1169 annotate_browser__init_asm_mode(&browser);
1171 annotate_browser__update_addr_width(&browser);
1173 ret = annotate_browser__run(&browser, evsel, hbt);
1175 annotated_source__purge(notes->src);
1177 out_free_offsets:
1178 free(browser.offsets);
1179 return ret;
1182 #define ANNOTATE_CFG(n) \
1183 { .name = #n, .value = &annotate_browser__opts.n, }
1186 * Keep the entries sorted, they are bsearch'ed
1188 static struct annotate_config {
1189 const char *name;
1190 bool *value;
1191 } annotate__configs[] = {
1192 ANNOTATE_CFG(hide_src_code),
1193 ANNOTATE_CFG(jump_arrows),
1194 ANNOTATE_CFG(show_linenr),
1195 ANNOTATE_CFG(show_nr_jumps),
1196 ANNOTATE_CFG(show_nr_samples),
1197 ANNOTATE_CFG(show_total_period),
1198 ANNOTATE_CFG(use_offset),
1201 #undef ANNOTATE_CFG
1203 static int annotate_config__cmp(const void *name, const void *cfgp)
1205 const struct annotate_config *cfg = cfgp;
1207 return strcmp(name, cfg->name);
1210 static int annotate__config(const char *var, const char *value,
1211 void *data __maybe_unused)
1213 struct annotate_config *cfg;
1214 const char *name;
1216 if (!strstarts(var, "annotate."))
1217 return 0;
1219 name = var + 9;
1220 cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1221 sizeof(struct annotate_config), annotate_config__cmp);
1223 if (cfg == NULL)
1224 ui__warning("%s variable unknown, ignoring...", var);
1225 else
1226 *cfg->value = perf_config_bool(name, value);
1227 return 0;
1230 void annotate_browser__init(void)
1232 perf_config(annotate__config, NULL);