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