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