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