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