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