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