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