1#include "../../util/util.h"
2#include "../browser.h"
3#include "../helpline.h"
4#include "../ui.h"
5#include "../util.h"
6#include "../../util/annotate.h"
7#include "../../util/hist.h"
8#include "../../util/sort.h"
9#include "../../util/symbol.h"
10#include "../../util/evsel.h"
11#include "../../util/config.h"
12#include <inttypes.h>
13#include <pthread.h>
14#include <linux/kernel.h>
15#include <sys/ttydefaults.h>
16
17struct disasm_line_samples {
18 double percent;
19 u64 nr;
20};
21
22#define IPC_WIDTH 6
23#define CYCLES_WIDTH 6
24
25struct browser_disasm_line {
26 struct rb_node rb_node;
27 u32 idx;
28 int idx_asm;
29 int jump_sources;
30
31
32
33
34 struct disasm_line_samples samples[1];
35};
36
37static struct annotate_browser_opt {
38 bool hide_src_code,
39 use_offset,
40 jump_arrows,
41 show_linenr,
42 show_nr_jumps,
43 show_total_period;
44} annotate_browser__opts = {
45 .use_offset = true,
46 .jump_arrows = true,
47};
48
49struct arch;
50
51struct annotate_browser {
52 struct ui_browser b;
53 struct rb_root entries;
54 struct rb_node *curr_hot;
55 struct disasm_line *selection;
56 struct disasm_line **offsets;
57 struct arch *arch;
58 int nr_events;
59 u64 start;
60 int nr_asm_entries;
61 int nr_entries;
62 int max_jump_sources;
63 int nr_jumps;
64 bool searching_backwards;
65 bool have_cycles;
66 u8 addr_width;
67 u8 jumps_width;
68 u8 target_width;
69 u8 min_addr_width;
70 u8 max_addr_width;
71 char search_bf[128];
72};
73
74static inline struct browser_disasm_line *disasm_line__browser(struct disasm_line *dl)
75{
76 return (struct browser_disasm_line *)(dl + 1);
77}
78
79static bool disasm_line__filter(struct ui_browser *browser __maybe_unused,
80 void *entry)
81{
82 if (annotate_browser__opts.hide_src_code) {
83 struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
84 return dl->offset == -1;
85 }
86
87 return false;
88}
89
90static int annotate_browser__jumps_percent_color(struct annotate_browser *browser,
91 int nr, bool current)
92{
93 if (current && (!browser->b.use_navkeypressed || browser->b.navkeypressed))
94 return HE_COLORSET_SELECTED;
95 if (nr == browser->max_jump_sources)
96 return HE_COLORSET_TOP;
97 if (nr > 1)
98 return HE_COLORSET_MEDIUM;
99 return HE_COLORSET_NORMAL;
100}
101
102static int annotate_browser__set_jumps_percent_color(struct annotate_browser *browser,
103 int nr, bool current)
104{
105 int color = annotate_browser__jumps_percent_color(browser, nr, current);
106 return ui_browser__set_color(&browser->b, color);
107}
108
109static int annotate_browser__pcnt_width(struct annotate_browser *ab)
110{
111 int w = 7 * ab->nr_events;
112
113 if (ab->have_cycles)
114 w += IPC_WIDTH + CYCLES_WIDTH;
115 return w;
116}
117
118static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
119{
120 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
121 struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
122 struct browser_disasm_line *bdl = disasm_line__browser(dl);
123 bool current_entry = ui_browser__is_current_entry(browser, row);
124 bool change_color = (!annotate_browser__opts.hide_src_code &&
125 (!current_entry || (browser->use_navkeypressed &&
126 !browser->navkeypressed)));
127 int width = browser->width, printed;
128 int i, pcnt_width = annotate_browser__pcnt_width(ab);
129 double percent_max = 0.0;
130 char bf[256];
131 bool show_title = false;
132
133 for (i = 0; i < ab->nr_events; i++) {
134 if (bdl->samples[i].percent > percent_max)
135 percent_max = bdl->samples[i].percent;
136 }
137
138 if ((row == 0) && (dl->offset == -1 || percent_max == 0.0)) {
139 if (ab->have_cycles) {
140 if (dl->ipc == 0.0 && dl->cycles == 0)
141 show_title = true;
142 } else
143 show_title = true;
144 }
145
146 if (dl->offset != -1 && percent_max != 0.0) {
147 for (i = 0; i < ab->nr_events; i++) {
148 ui_browser__set_percent_color(browser,
149 bdl->samples[i].percent,
150 current_entry);
151 if (annotate_browser__opts.show_total_period) {
152 ui_browser__printf(browser, "%6" PRIu64 " ",
153 bdl->samples[i].nr);
154 } else {
155 ui_browser__printf(browser, "%6.2f ",
156 bdl->samples[i].percent);
157 }
158 }
159 } else {
160 ui_browser__set_percent_color(browser, 0, current_entry);
161
162 if (!show_title)
163 ui_browser__write_nstring(browser, " ", 7 * ab->nr_events);
164 else
165 ui_browser__printf(browser, "%*s", 7, "Percent");
166 }
167 if (ab->have_cycles) {
168 if (dl->ipc)
169 ui_browser__printf(browser, "%*.2f ", IPC_WIDTH - 1, dl->ipc);
170 else if (!show_title)
171 ui_browser__write_nstring(browser, " ", IPC_WIDTH);
172 else
173 ui_browser__printf(browser, "%*s ", IPC_WIDTH - 1, "IPC");
174
175 if (dl->cycles)
176 ui_browser__printf(browser, "%*" PRIu64 " ",
177 CYCLES_WIDTH - 1, dl->cycles);
178 else if (!show_title)
179 ui_browser__write_nstring(browser, " ", CYCLES_WIDTH);
180 else
181 ui_browser__printf(browser, "%*s ", CYCLES_WIDTH - 1, "Cycle");
182 }
183
184 SLsmg_write_char(' ');
185
186
187 if (!browser->navkeypressed)
188 width += 1;
189
190 if (!*dl->line)
191 ui_browser__write_nstring(browser, " ", width - pcnt_width);
192 else if (dl->offset == -1) {
193 if (dl->line_nr && annotate_browser__opts.show_linenr)
194 printed = scnprintf(bf, sizeof(bf), "%-*d ",
195 ab->addr_width + 1, dl->line_nr);
196 else
197 printed = scnprintf(bf, sizeof(bf), "%*s ",
198 ab->addr_width, " ");
199 ui_browser__write_nstring(browser, bf, printed);
200 ui_browser__write_nstring(browser, dl->line, width - printed - pcnt_width + 1);
201 } else {
202 u64 addr = dl->offset;
203 int color = -1;
204
205 if (!annotate_browser__opts.use_offset)
206 addr += ab->start;
207
208 if (!annotate_browser__opts.use_offset) {
209 printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
210 } else {
211 if (bdl->jump_sources) {
212 if (annotate_browser__opts.show_nr_jumps) {
213 int prev;
214 printed = scnprintf(bf, sizeof(bf), "%*d ",
215 ab->jumps_width,
216 bdl->jump_sources);
217 prev = annotate_browser__set_jumps_percent_color(ab, bdl->jump_sources,
218 current_entry);
219 ui_browser__write_nstring(browser, bf, printed);
220 ui_browser__set_color(browser, prev);
221 }
222
223 printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
224 ab->target_width, addr);
225 } else {
226 printed = scnprintf(bf, sizeof(bf), "%*s ",
227 ab->addr_width, " ");
228 }
229 }
230
231 if (change_color)
232 color = ui_browser__set_color(browser, HE_COLORSET_ADDR);
233 ui_browser__write_nstring(browser, bf, printed);
234 if (change_color)
235 ui_browser__set_color(browser, color);
236 if (dl->ins.ops && dl->ins.ops->scnprintf) {
237 if (ins__is_jump(&dl->ins)) {
238 bool fwd = dl->ops.target.offset > dl->offset;
239
240 ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR :
241 SLSMG_UARROW_CHAR);
242 SLsmg_write_char(' ');
243 } else if (ins__is_call(&dl->ins)) {
244 ui_browser__write_graph(browser, SLSMG_RARROW_CHAR);
245 SLsmg_write_char(' ');
246 } else if (ins__is_ret(&dl->ins)) {
247 ui_browser__write_graph(browser, SLSMG_LARROW_CHAR);
248 SLsmg_write_char(' ');
249 } else {
250 ui_browser__write_nstring(browser, " ", 2);
251 }
252 } else {
253 ui_browser__write_nstring(browser, " ", 2);
254 }
255
256 disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset);
257 ui_browser__write_nstring(browser, bf, width - pcnt_width - 3 - printed);
258 }
259
260 if (current_entry)
261 ab->selection = dl;
262}
263
264static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym)
265{
266 if (!dl || !dl->ins.ops || !ins__is_jump(&dl->ins)
267 || !disasm_line__has_offset(dl)
268 || dl->ops.target.offset < 0
269 || dl->ops.target.offset >= (s64)symbol__size(sym))
270 return false;
271
272 return true;
273}
274
275static void annotate_browser__draw_current_jump(struct ui_browser *browser)
276{
277 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
278 struct disasm_line *cursor = ab->selection, *target;
279 struct browser_disasm_line *btarget, *bcursor;
280 unsigned int from, to;
281 struct map_symbol *ms = ab->b.priv;
282 struct symbol *sym = ms->sym;
283 u8 pcnt_width = annotate_browser__pcnt_width(ab);
284
285
286 if (strstr(sym->name, "@plt"))
287 return;
288
289 if (!disasm_line__is_valid_jump(cursor, sym))
290 return;
291
292 target = ab->offsets[cursor->ops.target.offset];
293 if (!target)
294 return;
295
296 bcursor = disasm_line__browser(cursor);
297 btarget = disasm_line__browser(target);
298
299 if (annotate_browser__opts.hide_src_code) {
300 from = bcursor->idx_asm;
301 to = btarget->idx_asm;
302 } else {
303 from = (u64)bcursor->idx;
304 to = (u64)btarget->idx;
305 }
306
307 ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
308 __ui_browser__line_arrow(browser, pcnt_width + 2 + ab->addr_width,
309 from, to);
310}
311
312static unsigned int annotate_browser__refresh(struct ui_browser *browser)
313{
314 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
315 int ret = ui_browser__list_head_refresh(browser);
316 int pcnt_width = annotate_browser__pcnt_width(ab);
317
318 if (annotate_browser__opts.jump_arrows)
319 annotate_browser__draw_current_jump(browser);
320
321 ui_browser__set_color(browser, HE_COLORSET_NORMAL);
322 __ui_browser__vline(browser, pcnt_width, 0, browser->height - 1);
323 return ret;
324}
325
326static int disasm__cmp(struct browser_disasm_line *a,
327 struct browser_disasm_line *b, int nr_pcnt)
328{
329 int i;
330
331 for (i = 0; i < nr_pcnt; i++) {
332 if (a->samples[i].percent == b->samples[i].percent)
333 continue;
334 return a->samples[i].percent < b->samples[i].percent;
335 }
336 return 0;
337}
338
339static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl,
340 int nr_events)
341{
342 struct rb_node **p = &root->rb_node;
343 struct rb_node *parent = NULL;
344 struct browser_disasm_line *l;
345
346 while (*p != NULL) {
347 parent = *p;
348 l = rb_entry(parent, struct browser_disasm_line, rb_node);
349
350 if (disasm__cmp(bdl, l, nr_events))
351 p = &(*p)->rb_left;
352 else
353 p = &(*p)->rb_right;
354 }
355 rb_link_node(&bdl->rb_node, parent, p);
356 rb_insert_color(&bdl->rb_node, root);
357}
358
359static void annotate_browser__set_top(struct annotate_browser *browser,
360 struct disasm_line *pos, u32 idx)
361{
362 unsigned back;
363
364 ui_browser__refresh_dimensions(&browser->b);
365 back = browser->b.height / 2;
366 browser->b.top_idx = browser->b.index = idx;
367
368 while (browser->b.top_idx != 0 && back != 0) {
369 pos = list_entry(pos->node.prev, struct disasm_line, node);
370
371 if (disasm_line__filter(&browser->b, &pos->node))
372 continue;
373
374 --browser->b.top_idx;
375 --back;
376 }
377
378 browser->b.top = pos;
379 browser->b.navkeypressed = true;
380}
381
382static void annotate_browser__set_rb_top(struct annotate_browser *browser,
383 struct rb_node *nd)
384{
385 struct browser_disasm_line *bpos;
386 struct disasm_line *pos;
387 u32 idx;
388
389 bpos = rb_entry(nd, struct browser_disasm_line, rb_node);
390 pos = ((struct disasm_line *)bpos) - 1;
391 idx = bpos->idx;
392 if (annotate_browser__opts.hide_src_code)
393 idx = bpos->idx_asm;
394 annotate_browser__set_top(browser, pos, idx);
395 browser->curr_hot = nd;
396}
397
398static void annotate_browser__calc_percent(struct annotate_browser *browser,
399 struct perf_evsel *evsel)
400{
401 struct map_symbol *ms = browser->b.priv;
402 struct symbol *sym = ms->sym;
403 struct annotation *notes = symbol__annotation(sym);
404 struct disasm_line *pos, *next;
405 s64 len = symbol__size(sym);
406
407 browser->entries = RB_ROOT;
408
409 pthread_mutex_lock(¬es->lock);
410
411 list_for_each_entry(pos, ¬es->src->source, node) {
412 struct browser_disasm_line *bpos = disasm_line__browser(pos);
413 const char *path = NULL;
414 double max_percent = 0.0;
415 int i;
416
417 if (pos->offset == -1) {
418 RB_CLEAR_NODE(&bpos->rb_node);
419 continue;
420 }
421
422 next = disasm__get_next_ip_line(¬es->src->source, pos);
423
424 for (i = 0; i < browser->nr_events; i++) {
425 u64 nr_samples;
426
427 bpos->samples[i].percent = disasm__calc_percent(notes,
428 evsel->idx + i,
429 pos->offset,
430 next ? next->offset : len,
431 &path, &nr_samples);
432 bpos->samples[i].nr = nr_samples;
433
434 if (max_percent < bpos->samples[i].percent)
435 max_percent = bpos->samples[i].percent;
436 }
437
438 if (max_percent < 0.01 && pos->ipc == 0) {
439 RB_CLEAR_NODE(&bpos->rb_node);
440 continue;
441 }
442 disasm_rb_tree__insert(&browser->entries, bpos,
443 browser->nr_events);
444 }
445 pthread_mutex_unlock(¬es->lock);
446
447 browser->curr_hot = rb_last(&browser->entries);
448}
449
450static bool annotate_browser__toggle_source(struct annotate_browser *browser)
451{
452 struct disasm_line *dl;
453 struct browser_disasm_line *bdl;
454 off_t offset = browser->b.index - browser->b.top_idx;
455
456 browser->b.seek(&browser->b, offset, SEEK_CUR);
457 dl = list_entry(browser->b.top, struct disasm_line, node);
458 bdl = disasm_line__browser(dl);
459
460 if (annotate_browser__opts.hide_src_code) {
461 if (bdl->idx_asm < offset)
462 offset = bdl->idx;
463
464 browser->b.nr_entries = browser->nr_entries;
465 annotate_browser__opts.hide_src_code = false;
466 browser->b.seek(&browser->b, -offset, SEEK_CUR);
467 browser->b.top_idx = bdl->idx - offset;
468 browser->b.index = bdl->idx;
469 } else {
470 if (bdl->idx_asm < 0) {
471 ui_helpline__puts("Only available for assembly lines.");
472 browser->b.seek(&browser->b, -offset, SEEK_CUR);
473 return false;
474 }
475
476 if (bdl->idx_asm < offset)
477 offset = bdl->idx_asm;
478
479 browser->b.nr_entries = browser->nr_asm_entries;
480 annotate_browser__opts.hide_src_code = true;
481 browser->b.seek(&browser->b, -offset, SEEK_CUR);
482 browser->b.top_idx = bdl->idx_asm - offset;
483 browser->b.index = bdl->idx_asm;
484 }
485
486 return true;
487}
488
489static void annotate_browser__init_asm_mode(struct annotate_browser *browser)
490{
491 ui_browser__reset_index(&browser->b);
492 browser->b.nr_entries = browser->nr_asm_entries;
493}
494
495#define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
496
497static int sym_title(struct symbol *sym, struct map *map, char *title,
498 size_t sz)
499{
500 return snprintf(title, sz, "%s %s", sym->name, map->dso->long_name);
501}
502
503static bool annotate_browser__callq(struct annotate_browser *browser,
504 struct perf_evsel *evsel,
505 struct hist_browser_timer *hbt)
506{
507 struct map_symbol *ms = browser->b.priv;
508 struct disasm_line *dl = browser->selection;
509 struct annotation *notes;
510 struct addr_map_symbol target = {
511 .map = ms->map,
512 .addr = map__objdump_2mem(ms->map, dl->ops.target.addr),
513 };
514 char title[SYM_TITLE_MAX_SIZE];
515
516 if (!ins__is_call(&dl->ins))
517 return false;
518
519 if (map_groups__find_ams(&target) ||
520 map__rip_2objdump(target.map, target.map->map_ip(target.map,
521 target.addr)) !=
522 dl->ops.target.addr) {
523 ui_helpline__puts("The called function was not found.");
524 return true;
525 }
526
527 notes = symbol__annotation(target.sym);
528 pthread_mutex_lock(¬es->lock);
529
530 if (notes->src == NULL && symbol__alloc_hist(target.sym) < 0) {
531 pthread_mutex_unlock(¬es->lock);
532 ui__warning("Not enough memory for annotating '%s' symbol!\n",
533 target.sym->name);
534 return true;
535 }
536
537 pthread_mutex_unlock(¬es->lock);
538 symbol__tui_annotate(target.sym, target.map, evsel, hbt);
539 sym_title(ms->sym, ms->map, title, sizeof(title));
540 ui_browser__show_title(&browser->b, title);
541 return true;
542}
543
544static
545struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
546 s64 offset, s64 *idx)
547{
548 struct map_symbol *ms = browser->b.priv;
549 struct symbol *sym = ms->sym;
550 struct annotation *notes = symbol__annotation(sym);
551 struct disasm_line *pos;
552
553 *idx = 0;
554 list_for_each_entry(pos, ¬es->src->source, node) {
555 if (pos->offset == offset)
556 return pos;
557 if (!disasm_line__filter(&browser->b, &pos->node))
558 ++*idx;
559 }
560
561 return NULL;
562}
563
564static bool annotate_browser__jump(struct annotate_browser *browser)
565{
566 struct disasm_line *dl = browser->selection;
567 u64 offset;
568 s64 idx;
569
570 if (!ins__is_jump(&dl->ins))
571 return false;
572
573 offset = dl->ops.target.offset;
574 dl = annotate_browser__find_offset(browser, offset, &idx);
575 if (dl == NULL) {
576 ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
577 return true;
578 }
579
580 annotate_browser__set_top(browser, dl, idx);
581
582 return true;
583}
584
585static
586struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
587 char *s, s64 *idx)
588{
589 struct map_symbol *ms = browser->b.priv;
590 struct symbol *sym = ms->sym;
591 struct annotation *notes = symbol__annotation(sym);
592 struct disasm_line *pos = browser->selection;
593
594 *idx = browser->b.index;
595 list_for_each_entry_continue(pos, ¬es->src->source, node) {
596 if (disasm_line__filter(&browser->b, &pos->node))
597 continue;
598
599 ++*idx;
600
601 if (pos->line && strstr(pos->line, s) != NULL)
602 return pos;
603 }
604
605 return NULL;
606}
607
608static bool __annotate_browser__search(struct annotate_browser *browser)
609{
610 struct disasm_line *dl;
611 s64 idx;
612
613 dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
614 if (dl == NULL) {
615 ui_helpline__puts("String not found!");
616 return false;
617 }
618
619 annotate_browser__set_top(browser, dl, idx);
620 browser->searching_backwards = false;
621 return true;
622}
623
624static
625struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
626 char *s, s64 *idx)
627{
628 struct map_symbol *ms = browser->b.priv;
629 struct symbol *sym = ms->sym;
630 struct annotation *notes = symbol__annotation(sym);
631 struct disasm_line *pos = browser->selection;
632
633 *idx = browser->b.index;
634 list_for_each_entry_continue_reverse(pos, ¬es->src->source, node) {
635 if (disasm_line__filter(&browser->b, &pos->node))
636 continue;
637
638 --*idx;
639
640 if (pos->line && strstr(pos->line, s) != NULL)
641 return pos;
642 }
643
644 return NULL;
645}
646
647static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
648{
649 struct disasm_line *dl;
650 s64 idx;
651
652 dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
653 if (dl == NULL) {
654 ui_helpline__puts("String not found!");
655 return false;
656 }
657
658 annotate_browser__set_top(browser, dl, idx);
659 browser->searching_backwards = true;
660 return true;
661}
662
663static bool annotate_browser__search_window(struct annotate_browser *browser,
664 int delay_secs)
665{
666 if (ui_browser__input_window("Search", "String: ", browser->search_bf,
667 "ENTER: OK, ESC: Cancel",
668 delay_secs * 2) != K_ENTER ||
669 !*browser->search_bf)
670 return false;
671
672 return true;
673}
674
675static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
676{
677 if (annotate_browser__search_window(browser, delay_secs))
678 return __annotate_browser__search(browser);
679
680 return false;
681}
682
683static bool annotate_browser__continue_search(struct annotate_browser *browser,
684 int delay_secs)
685{
686 if (!*browser->search_bf)
687 return annotate_browser__search(browser, delay_secs);
688
689 return __annotate_browser__search(browser);
690}
691
692static bool annotate_browser__search_reverse(struct annotate_browser *browser,
693 int delay_secs)
694{
695 if (annotate_browser__search_window(browser, delay_secs))
696 return __annotate_browser__search_reverse(browser);
697
698 return false;
699}
700
701static
702bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
703 int delay_secs)
704{
705 if (!*browser->search_bf)
706 return annotate_browser__search_reverse(browser, delay_secs);
707
708 return __annotate_browser__search_reverse(browser);
709}
710
711static void annotate_browser__update_addr_width(struct annotate_browser *browser)
712{
713 if (annotate_browser__opts.use_offset)
714 browser->target_width = browser->min_addr_width;
715 else
716 browser->target_width = browser->max_addr_width;
717
718 browser->addr_width = browser->target_width;
719
720 if (annotate_browser__opts.show_nr_jumps)
721 browser->addr_width += browser->jumps_width + 1;
722}
723
724static int annotate_browser__run(struct annotate_browser *browser,
725 struct perf_evsel *evsel,
726 struct hist_browser_timer *hbt)
727{
728 struct rb_node *nd = NULL;
729 struct map_symbol *ms = browser->b.priv;
730 struct symbol *sym = ms->sym;
731 const char *help = "Press 'h' for help on key bindings";
732 int delay_secs = hbt ? hbt->refresh : 0;
733 int key;
734 char title[SYM_TITLE_MAX_SIZE];
735
736 sym_title(sym, ms->map, title, sizeof(title));
737 if (ui_browser__show(&browser->b, title, help) < 0)
738 return -1;
739
740 annotate_browser__calc_percent(browser, evsel);
741
742 if (browser->curr_hot) {
743 annotate_browser__set_rb_top(browser, browser->curr_hot);
744 browser->b.navkeypressed = false;
745 }
746
747 nd = browser->curr_hot;
748
749 while (1) {
750 key = ui_browser__run(&browser->b, delay_secs);
751
752 if (delay_secs != 0) {
753 annotate_browser__calc_percent(browser, evsel);
754
755
756
757
758
759 if (nd != NULL && RB_EMPTY_NODE(nd))
760 nd = NULL;
761 }
762
763 switch (key) {
764 case K_TIMER:
765 if (hbt)
766 hbt->timer(hbt->arg);
767
768 if (delay_secs != 0)
769 symbol__annotate_decay_histogram(sym, evsel->idx);
770 continue;
771 case K_TAB:
772 if (nd != NULL) {
773 nd = rb_prev(nd);
774 if (nd == NULL)
775 nd = rb_last(&browser->entries);
776 } else
777 nd = browser->curr_hot;
778 break;
779 case K_UNTAB:
780 if (nd != NULL) {
781 nd = rb_next(nd);
782 if (nd == NULL)
783 nd = rb_first(&browser->entries);
784 } else
785 nd = browser->curr_hot;
786 break;
787 case K_F1:
788 case 'h':
789 ui_browser__help_window(&browser->b,
790 "UP/DOWN/PGUP\n"
791 "PGDN/SPACE Navigate\n"
792 "q/ESC/CTRL+C Exit\n\n"
793 "ENTER Go to target\n"
794 "ESC Exit\n"
795 "H Cycle thru hottest instructions\n"
796 "j Toggle showing jump to target arrows\n"
797 "J Toggle showing number of jump sources on targets\n"
798 "n Search next string\n"
799 "o Toggle disassembler output/simplified view\n"
800 "s Toggle source code view\n"
801 "t Toggle total period view\n"
802 "/ Search string\n"
803 "k Toggle line numbers\n"
804 "r Run available scripts\n"
805 "? Search string backwards\n");
806 continue;
807 case 'r':
808 {
809 script_browse(NULL);
810 continue;
811 }
812 case 'k':
813 annotate_browser__opts.show_linenr =
814 !annotate_browser__opts.show_linenr;
815 break;
816 case 'H':
817 nd = browser->curr_hot;
818 break;
819 case 's':
820 if (annotate_browser__toggle_source(browser))
821 ui_helpline__puts(help);
822 continue;
823 case 'o':
824 annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset;
825 annotate_browser__update_addr_width(browser);
826 continue;
827 case 'j':
828 annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows;
829 continue;
830 case 'J':
831 annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps;
832 annotate_browser__update_addr_width(browser);
833 continue;
834 case '/':
835 if (annotate_browser__search(browser, delay_secs)) {
836show_help:
837 ui_helpline__puts(help);
838 }
839 continue;
840 case 'n':
841 if (browser->searching_backwards ?
842 annotate_browser__continue_search_reverse(browser, delay_secs) :
843 annotate_browser__continue_search(browser, delay_secs))
844 goto show_help;
845 continue;
846 case '?':
847 if (annotate_browser__search_reverse(browser, delay_secs))
848 goto show_help;
849 continue;
850 case 'D': {
851 static int seq;
852 ui_helpline__pop();
853 ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
854 seq++, browser->b.nr_entries,
855 browser->b.height,
856 browser->b.index,
857 browser->b.top_idx,
858 browser->nr_asm_entries);
859 }
860 continue;
861 case K_ENTER:
862 case K_RIGHT:
863 if (browser->selection == NULL)
864 ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
865 else if (browser->selection->offset == -1)
866 ui_helpline__puts("Actions are only available for assembly lines.");
867 else if (!browser->selection->ins.ops)
868 goto show_sup_ins;
869 else if (ins__is_ret(&browser->selection->ins))
870 goto out;
871 else if (!(annotate_browser__jump(browser) ||
872 annotate_browser__callq(browser, evsel, hbt))) {
873show_sup_ins:
874 ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
875 }
876 continue;
877 case 't':
878 annotate_browser__opts.show_total_period =
879 !annotate_browser__opts.show_total_period;
880 annotate_browser__update_addr_width(browser);
881 continue;
882 case K_LEFT:
883 case K_ESC:
884 case 'q':
885 case CTRL('c'):
886 goto out;
887 default:
888 continue;
889 }
890
891 if (nd != NULL)
892 annotate_browser__set_rb_top(browser, nd);
893 }
894out:
895 ui_browser__hide(&browser->b);
896 return key;
897}
898
899int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
900 struct hist_browser_timer *hbt)
901{
902
903 annotate_browser__opts.show_total_period =
904 symbol_conf.show_total_period;
905
906 return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
907}
908
909int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
910 struct hist_browser_timer *hbt)
911{
912
913 SLang_reset_tty();
914 SLang_init_tty(0, 0, 0);
915
916 return map_symbol__tui_annotate(&he->ms, evsel, hbt);
917}
918
919
920static unsigned count_insn(struct annotate_browser *browser, u64 start, u64 end)
921{
922 unsigned n_insn = 0;
923 u64 offset;
924
925 for (offset = start; offset <= end; offset++) {
926 if (browser->offsets[offset])
927 n_insn++;
928 }
929 return n_insn;
930}
931
932static void count_and_fill(struct annotate_browser *browser, u64 start, u64 end,
933 struct cyc_hist *ch)
934{
935 unsigned n_insn;
936 u64 offset;
937
938 n_insn = count_insn(browser, start, end);
939 if (n_insn && ch->num && ch->cycles) {
940 float ipc = n_insn / ((double)ch->cycles / (double)ch->num);
941
942
943 if (ch->reset >= 0x7fff || ch->reset >= ch->num / 2)
944 return;
945
946 for (offset = start; offset <= end; offset++) {
947 struct disasm_line *dl = browser->offsets[offset];
948
949 if (dl)
950 dl->ipc = ipc;
951 }
952 }
953}
954
955
956
957
958
959
960static void annotate__compute_ipc(struct annotate_browser *browser, size_t size,
961 struct symbol *sym)
962{
963 u64 offset;
964 struct annotation *notes = symbol__annotation(sym);
965
966 if (!notes->src || !notes->src->cycles_hist)
967 return;
968
969 pthread_mutex_lock(¬es->lock);
970 for (offset = 0; offset < size; ++offset) {
971 struct cyc_hist *ch;
972
973 ch = ¬es->src->cycles_hist[offset];
974 if (ch && ch->cycles) {
975 struct disasm_line *dl;
976
977 if (ch->have_start)
978 count_and_fill(browser, ch->start, offset, ch);
979 dl = browser->offsets[offset];
980 if (dl && ch->num_aggr)
981 dl->cycles = ch->cycles_aggr / ch->num_aggr;
982 browser->have_cycles = true;
983 }
984 }
985 pthread_mutex_unlock(¬es->lock);
986}
987
988static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
989 size_t size)
990{
991 u64 offset;
992 struct map_symbol *ms = browser->b.priv;
993 struct symbol *sym = ms->sym;
994
995
996 if (strstr(sym->name, "@plt"))
997 return;
998
999 for (offset = 0; offset < size; ++offset) {
1000 struct disasm_line *dl = browser->offsets[offset], *dlt;
1001 struct browser_disasm_line *bdlt;
1002
1003 if (!disasm_line__is_valid_jump(dl, sym))
1004 continue;
1005
1006 dlt = browser->offsets[dl->ops.target.offset];
1007
1008
1009
1010
1011 if (dlt == NULL)
1012 continue;
1013
1014 bdlt = disasm_line__browser(dlt);
1015 if (++bdlt->jump_sources > browser->max_jump_sources)
1016 browser->max_jump_sources = bdlt->jump_sources;
1017
1018 ++browser->nr_jumps;
1019 }
1020}
1021
1022static inline int width_jumps(int n)
1023{
1024 if (n >= 100)
1025 return 5;
1026 if (n / 10)
1027 return 2;
1028 return 1;
1029}
1030
1031int symbol__tui_annotate(struct symbol *sym, struct map *map,
1032 struct perf_evsel *evsel,
1033 struct hist_browser_timer *hbt)
1034{
1035 struct disasm_line *pos, *n;
1036 struct annotation *notes;
1037 size_t size;
1038 struct map_symbol ms = {
1039 .map = map,
1040 .sym = sym,
1041 };
1042 struct annotate_browser browser = {
1043 .b = {
1044 .refresh = annotate_browser__refresh,
1045 .seek = ui_browser__list_head_seek,
1046 .write = annotate_browser__write,
1047 .filter = disasm_line__filter,
1048 .priv = &ms,
1049 .use_navkeypressed = true,
1050 },
1051 };
1052 int ret = -1, err;
1053 int nr_pcnt = 1;
1054 size_t sizeof_bdl = sizeof(struct browser_disasm_line);
1055
1056 if (sym == NULL)
1057 return -1;
1058
1059 size = symbol__size(sym);
1060
1061 if (map->dso->annotate_warned)
1062 return -1;
1063
1064 browser.offsets = zalloc(size * sizeof(struct disasm_line *));
1065 if (browser.offsets == NULL) {
1066 ui__error("Not enough memory!");
1067 return -1;
1068 }
1069
1070 if (perf_evsel__is_group_event(evsel)) {
1071 nr_pcnt = evsel->nr_members;
1072 sizeof_bdl += sizeof(struct disasm_line_samples) *
1073 (nr_pcnt - 1);
1074 }
1075
1076 err = symbol__disassemble(sym, map, perf_evsel__env_arch(evsel),
1077 sizeof_bdl, &browser.arch);
1078 if (err) {
1079 char msg[BUFSIZ];
1080 symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg));
1081 ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
1082 goto out_free_offsets;
1083 }
1084
1085 ui_helpline__push("Press ESC to exit");
1086
1087 notes = symbol__annotation(sym);
1088 browser.start = map__rip_2objdump(map, sym->start);
1089
1090 list_for_each_entry(pos, ¬es->src->source, node) {
1091 struct browser_disasm_line *bpos;
1092 size_t line_len = strlen(pos->line);
1093
1094 if (browser.b.width < line_len)
1095 browser.b.width = line_len;
1096 bpos = disasm_line__browser(pos);
1097 bpos->idx = browser.nr_entries++;
1098 if (pos->offset != -1) {
1099 bpos->idx_asm = browser.nr_asm_entries++;
1100
1101
1102
1103
1104
1105
1106
1107 if (pos->offset < (s64)size)
1108 browser.offsets[pos->offset] = pos;
1109 } else
1110 bpos->idx_asm = -1;
1111 }
1112
1113 annotate_browser__mark_jump_targets(&browser, size);
1114 annotate__compute_ipc(&browser, size, sym);
1115
1116 browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
1117 browser.max_addr_width = hex_width(sym->end);
1118 browser.jumps_width = width_jumps(browser.max_jump_sources);
1119 browser.nr_events = nr_pcnt;
1120 browser.b.nr_entries = browser.nr_entries;
1121 browser.b.entries = ¬es->src->source,
1122 browser.b.width += 18;
1123
1124 if (annotate_browser__opts.hide_src_code)
1125 annotate_browser__init_asm_mode(&browser);
1126
1127 annotate_browser__update_addr_width(&browser);
1128
1129 ret = annotate_browser__run(&browser, evsel, hbt);
1130 list_for_each_entry_safe(pos, n, ¬es->src->source, node) {
1131 list_del(&pos->node);
1132 disasm_line__free(pos);
1133 }
1134
1135out_free_offsets:
1136 free(browser.offsets);
1137 return ret;
1138}
1139
1140#define ANNOTATE_CFG(n) \
1141 { .name = #n, .value = &annotate_browser__opts.n, }
1142
1143
1144
1145
1146static struct annotate_config {
1147 const char *name;
1148 bool *value;
1149} annotate__configs[] = {
1150 ANNOTATE_CFG(hide_src_code),
1151 ANNOTATE_CFG(jump_arrows),
1152 ANNOTATE_CFG(show_linenr),
1153 ANNOTATE_CFG(show_nr_jumps),
1154 ANNOTATE_CFG(show_total_period),
1155 ANNOTATE_CFG(use_offset),
1156};
1157
1158#undef ANNOTATE_CFG
1159
1160static int annotate_config__cmp(const void *name, const void *cfgp)
1161{
1162 const struct annotate_config *cfg = cfgp;
1163
1164 return strcmp(name, cfg->name);
1165}
1166
1167static int annotate__config(const char *var, const char *value,
1168 void *data __maybe_unused)
1169{
1170 struct annotate_config *cfg;
1171 const char *name;
1172
1173 if (prefixcmp(var, "annotate.") != 0)
1174 return 0;
1175
1176 name = var + 9;
1177 cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1178 sizeof(struct annotate_config), annotate_config__cmp);
1179
1180 if (cfg == NULL)
1181 ui__warning("%s variable unknown, ignoring...", var);
1182 else
1183 *cfg->value = perf_config_bool(name, value);
1184 return 0;
1185}
1186
1187void annotate_browser__init(void)
1188{
1189 perf_config(annotate__config, NULL);
1190}
1191