62369f0b6608b4993916d7c5ab1aeec22aa5f516
[linux-3.10.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         u32             idx;
18         int             idx_asm;
19         int             jump_sources;
20         double          percent[1];
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.0) {
101                 ui_browser__set_percent_color(browser, bdl->percent[0], current_entry);
102                 slsmg_printf("%6.2f ", bdl->percent[0]);
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[0] < l->percent[0])
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[0] = disasm_line__calc_percent(pos, sym, evsel->idx);
349                 if (bpos->percent[0] < 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 }