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