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