Linux-libre 4.11.5-gnu
[librecmc/linux-libre.git] / tools / perf / ui / browsers / annotate.c
1 #include "../../util/util.h"
2 #include "../browser.h"
3 #include "../helpline.h"
4 #include "../ui.h"
5 #include "../util.h"
6 #include "../../util/annotate.h"
7 #include "../../util/hist.h"
8 #include "../../util/sort.h"
9 #include "../../util/symbol.h"
10 #include "../../util/evsel.h"
11 #include "../../util/config.h"
12 #include <pthread.h>
13
14 struct disasm_line_samples {
15         double          percent;
16         u64             nr;
17 };
18
19 #define IPC_WIDTH 6
20 #define CYCLES_WIDTH 6
21
22 struct browser_disasm_line {
23         struct rb_node                  rb_node;
24         u32                             idx;
25         int                             idx_asm;
26         int                             jump_sources;
27         /*
28          * actual length of this array is saved on the nr_events field
29          * of the struct annotate_browser
30          */
31         struct disasm_line_samples      samples[1];
32 };
33
34 static struct annotate_browser_opt {
35         bool hide_src_code,
36              use_offset,
37              jump_arrows,
38              show_linenr,
39              show_nr_jumps,
40              show_total_period;
41 } annotate_browser__opts = {
42         .use_offset     = true,
43         .jump_arrows    = true,
44 };
45
46 struct annotate_browser {
47         struct ui_browser b;
48         struct rb_root    entries;
49         struct rb_node    *curr_hot;
50         struct disasm_line  *selection;
51         struct disasm_line  **offsets;
52         int                 nr_events;
53         u64                 start;
54         int                 nr_asm_entries;
55         int                 nr_entries;
56         int                 max_jump_sources;
57         int                 nr_jumps;
58         bool                searching_backwards;
59         bool                have_cycles;
60         u8                  addr_width;
61         u8                  jumps_width;
62         u8                  target_width;
63         u8                  min_addr_width;
64         u8                  max_addr_width;
65         char                search_bf[128];
66 };
67
68 static inline struct browser_disasm_line *disasm_line__browser(struct disasm_line *dl)
69 {
70         return (struct browser_disasm_line *)(dl + 1);
71 }
72
73 static bool disasm_line__filter(struct ui_browser *browser __maybe_unused,
74                                 void *entry)
75 {
76         if (annotate_browser__opts.hide_src_code) {
77                 struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
78                 return dl->offset == -1;
79         }
80
81         return false;
82 }
83
84 static int annotate_browser__jumps_percent_color(struct annotate_browser *browser,
85                                                  int nr, bool current)
86 {
87         if (current && (!browser->b.use_navkeypressed || browser->b.navkeypressed))
88                 return HE_COLORSET_SELECTED;
89         if (nr == browser->max_jump_sources)
90                 return HE_COLORSET_TOP;
91         if (nr > 1)
92                 return HE_COLORSET_MEDIUM;
93         return HE_COLORSET_NORMAL;
94 }
95
96 static int annotate_browser__set_jumps_percent_color(struct annotate_browser *browser,
97                                                      int nr, bool current)
98 {
99          int color = annotate_browser__jumps_percent_color(browser, nr, current);
100          return ui_browser__set_color(&browser->b, color);
101 }
102
103 static int annotate_browser__pcnt_width(struct annotate_browser *ab)
104 {
105         int w = 7 * ab->nr_events;
106
107         if (ab->have_cycles)
108                 w += IPC_WIDTH + CYCLES_WIDTH;
109         return w;
110 }
111
112 static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
113 {
114         struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
115         struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
116         struct browser_disasm_line *bdl = disasm_line__browser(dl);
117         bool current_entry = ui_browser__is_current_entry(browser, row);
118         bool change_color = (!annotate_browser__opts.hide_src_code &&
119                              (!current_entry || (browser->use_navkeypressed &&
120                                                  !browser->navkeypressed)));
121         int width = browser->width, printed;
122         int i, pcnt_width = annotate_browser__pcnt_width(ab);
123         double percent_max = 0.0;
124         char bf[256];
125
126         for (i = 0; i < ab->nr_events; i++) {
127                 if (bdl->samples[i].percent > percent_max)
128                         percent_max = bdl->samples[i].percent;
129         }
130
131         if (dl->offset != -1 && percent_max != 0.0) {
132                 if (percent_max != 0.0) {
133                         for (i = 0; i < ab->nr_events; i++) {
134                                 ui_browser__set_percent_color(browser,
135                                                         bdl->samples[i].percent,
136                                                         current_entry);
137                                 if (annotate_browser__opts.show_total_period) {
138                                         ui_browser__printf(browser, "%6" PRIu64 " ",
139                                                            bdl->samples[i].nr);
140                                 } else {
141                                         ui_browser__printf(browser, "%6.2f ",
142                                                            bdl->samples[i].percent);
143                                 }
144                         }
145                 } else {
146                         ui_browser__write_nstring(browser, " ", 7 * ab->nr_events);
147                 }
148         } else {
149                 ui_browser__set_percent_color(browser, 0, current_entry);
150                 ui_browser__write_nstring(browser, " ", 7 * ab->nr_events);
151         }
152         if (ab->have_cycles) {
153                 if (dl->ipc)
154                         ui_browser__printf(browser, "%*.2f ", IPC_WIDTH - 1, dl->ipc);
155                 else
156                         ui_browser__write_nstring(browser, " ", IPC_WIDTH);
157                 if (dl->cycles)
158                         ui_browser__printf(browser, "%*" PRIu64 " ",
159                                            CYCLES_WIDTH - 1, dl->cycles);
160                 else
161                         ui_browser__write_nstring(browser, " ", CYCLES_WIDTH);
162         }
163
164         SLsmg_write_char(' ');
165
166         /* The scroll bar isn't being used */
167         if (!browser->navkeypressed)
168                 width += 1;
169
170         if (!*dl->line)
171                 ui_browser__write_nstring(browser, " ", width - pcnt_width);
172         else if (dl->offset == -1) {
173                 if (dl->line_nr && annotate_browser__opts.show_linenr)
174                         printed = scnprintf(bf, sizeof(bf), "%-*d ",
175                                         ab->addr_width + 1, dl->line_nr);
176                 else
177                         printed = scnprintf(bf, sizeof(bf), "%*s  ",
178                                     ab->addr_width, " ");
179                 ui_browser__write_nstring(browser, bf, printed);
180                 ui_browser__write_nstring(browser, dl->line, width - printed - pcnt_width + 1);
181         } else {
182                 u64 addr = dl->offset;
183                 int color = -1;
184
185                 if (!annotate_browser__opts.use_offset)
186                         addr += ab->start;
187
188                 if (!annotate_browser__opts.use_offset) {
189                         printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
190                 } else {
191                         if (bdl->jump_sources) {
192                                 if (annotate_browser__opts.show_nr_jumps) {
193                                         int prev;
194                                         printed = scnprintf(bf, sizeof(bf), "%*d ",
195                                                             ab->jumps_width,
196                                                             bdl->jump_sources);
197                                         prev = annotate_browser__set_jumps_percent_color(ab, bdl->jump_sources,
198                                                                                          current_entry);
199                                         ui_browser__write_nstring(browser, bf, printed);
200                                         ui_browser__set_color(browser, prev);
201                                 }
202
203                                 printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
204                                                     ab->target_width, addr);
205                         } else {
206                                 printed = scnprintf(bf, sizeof(bf), "%*s  ",
207                                                     ab->addr_width, " ");
208                         }
209                 }
210
211                 if (change_color)
212                         color = ui_browser__set_color(browser, HE_COLORSET_ADDR);
213                 ui_browser__write_nstring(browser, bf, printed);
214                 if (change_color)
215                         ui_browser__set_color(browser, color);
216                 if (dl->ins.ops && dl->ins.ops->scnprintf) {
217                         if (ins__is_jump(&dl->ins)) {
218                                 bool fwd = dl->ops.target.offset > dl->offset;
219
220                                 ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR :
221                                                                     SLSMG_UARROW_CHAR);
222                                 SLsmg_write_char(' ');
223                         } else if (ins__is_call(&dl->ins)) {
224                                 ui_browser__write_graph(browser, SLSMG_RARROW_CHAR);
225                                 SLsmg_write_char(' ');
226                         } else if (ins__is_ret(&dl->ins)) {
227                                 ui_browser__write_graph(browser, SLSMG_LARROW_CHAR);
228                                 SLsmg_write_char(' ');
229                         } else {
230                                 ui_browser__write_nstring(browser, " ", 2);
231                         }
232                 } else {
233                         ui_browser__write_nstring(browser, " ", 2);
234                 }
235
236                 disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset);
237                 ui_browser__write_nstring(browser, bf, width - pcnt_width - 3 - printed);
238         }
239
240         if (current_entry)
241                 ab->selection = dl;
242 }
243
244 static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym)
245 {
246         if (!dl || !dl->ins.ops || !ins__is_jump(&dl->ins)
247             || !disasm_line__has_offset(dl)
248             || dl->ops.target.offset < 0
249             || dl->ops.target.offset >= (s64)symbol__size(sym))
250                 return false;
251
252         return true;
253 }
254
255 static void annotate_browser__draw_current_jump(struct ui_browser *browser)
256 {
257         struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
258         struct disasm_line *cursor = ab->selection, *target;
259         struct browser_disasm_line *btarget, *bcursor;
260         unsigned int from, to;
261         struct map_symbol *ms = ab->b.priv;
262         struct symbol *sym = ms->sym;
263         u8 pcnt_width = annotate_browser__pcnt_width(ab);
264
265         /* PLT symbols contain external offsets */
266         if (strstr(sym->name, "@plt"))
267                 return;
268
269         if (!disasm_line__is_valid_jump(cursor, sym))
270                 return;
271
272         target = ab->offsets[cursor->ops.target.offset];
273         if (!target)
274                 return;
275
276         bcursor = disasm_line__browser(cursor);
277         btarget = disasm_line__browser(target);
278
279         if (annotate_browser__opts.hide_src_code) {
280                 from = bcursor->idx_asm;
281                 to = btarget->idx_asm;
282         } else {
283                 from = (u64)bcursor->idx;
284                 to = (u64)btarget->idx;
285         }
286
287         ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
288         __ui_browser__line_arrow(browser, pcnt_width + 2 + ab->addr_width,
289                                  from, to);
290 }
291
292 static unsigned int annotate_browser__refresh(struct ui_browser *browser)
293 {
294         struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
295         int ret = ui_browser__list_head_refresh(browser);
296         int pcnt_width = annotate_browser__pcnt_width(ab);
297
298         if (annotate_browser__opts.jump_arrows)
299                 annotate_browser__draw_current_jump(browser);
300
301         ui_browser__set_color(browser, HE_COLORSET_NORMAL);
302         __ui_browser__vline(browser, pcnt_width, 0, browser->height - 1);
303         return ret;
304 }
305
306 static int disasm__cmp(struct browser_disasm_line *a,
307                        struct browser_disasm_line *b, int nr_pcnt)
308 {
309         int i;
310
311         for (i = 0; i < nr_pcnt; i++) {
312                 if (a->samples[i].percent == b->samples[i].percent)
313                         continue;
314                 return a->samples[i].percent < b->samples[i].percent;
315         }
316         return 0;
317 }
318
319 static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl,
320                                    int nr_events)
321 {
322         struct rb_node **p = &root->rb_node;
323         struct rb_node *parent = NULL;
324         struct browser_disasm_line *l;
325
326         while (*p != NULL) {
327                 parent = *p;
328                 l = rb_entry(parent, struct browser_disasm_line, rb_node);
329
330                 if (disasm__cmp(bdl, l, nr_events))
331                         p = &(*p)->rb_left;
332                 else
333                         p = &(*p)->rb_right;
334         }
335         rb_link_node(&bdl->rb_node, parent, p);
336         rb_insert_color(&bdl->rb_node, root);
337 }
338
339 static void annotate_browser__set_top(struct annotate_browser *browser,
340                                       struct disasm_line *pos, u32 idx)
341 {
342         unsigned back;
343
344         ui_browser__refresh_dimensions(&browser->b);
345         back = browser->b.height / 2;
346         browser->b.top_idx = browser->b.index = idx;
347
348         while (browser->b.top_idx != 0 && back != 0) {
349                 pos = list_entry(pos->node.prev, struct disasm_line, node);
350
351                 if (disasm_line__filter(&browser->b, &pos->node))
352                         continue;
353
354                 --browser->b.top_idx;
355                 --back;
356         }
357
358         browser->b.top = pos;
359         browser->b.navkeypressed = true;
360 }
361
362 static void annotate_browser__set_rb_top(struct annotate_browser *browser,
363                                          struct rb_node *nd)
364 {
365         struct browser_disasm_line *bpos;
366         struct disasm_line *pos;
367         u32 idx;
368
369         bpos = rb_entry(nd, struct browser_disasm_line, rb_node);
370         pos = ((struct disasm_line *)bpos) - 1;
371         idx = bpos->idx;
372         if (annotate_browser__opts.hide_src_code)
373                 idx = bpos->idx_asm;
374         annotate_browser__set_top(browser, pos, idx);
375         browser->curr_hot = nd;
376 }
377
378 static void annotate_browser__calc_percent(struct annotate_browser *browser,
379                                            struct perf_evsel *evsel)
380 {
381         struct map_symbol *ms = browser->b.priv;
382         struct symbol *sym = ms->sym;
383         struct annotation *notes = symbol__annotation(sym);
384         struct disasm_line *pos, *next;
385         s64 len = symbol__size(sym);
386
387         browser->entries = RB_ROOT;
388
389         pthread_mutex_lock(&notes->lock);
390
391         list_for_each_entry(pos, &notes->src->source, node) {
392                 struct browser_disasm_line *bpos = disasm_line__browser(pos);
393                 const char *path = NULL;
394                 double max_percent = 0.0;
395                 int i;
396
397                 if (pos->offset == -1) {
398                         RB_CLEAR_NODE(&bpos->rb_node);
399                         continue;
400                 }
401
402                 next = disasm__get_next_ip_line(&notes->src->source, pos);
403
404                 for (i = 0; i < browser->nr_events; i++) {
405                         u64 nr_samples;
406
407                         bpos->samples[i].percent = disasm__calc_percent(notes,
408                                                 evsel->idx + i,
409                                                 pos->offset,
410                                                 next ? next->offset : len,
411                                                 &path, &nr_samples);
412                         bpos->samples[i].nr = nr_samples;
413
414                         if (max_percent < bpos->samples[i].percent)
415                                 max_percent = bpos->samples[i].percent;
416                 }
417
418                 if (max_percent < 0.01 && pos->ipc == 0) {
419                         RB_CLEAR_NODE(&bpos->rb_node);
420                         continue;
421                 }
422                 disasm_rb_tree__insert(&browser->entries, bpos,
423                                        browser->nr_events);
424         }
425         pthread_mutex_unlock(&notes->lock);
426
427         browser->curr_hot = rb_last(&browser->entries);
428 }
429
430 static bool annotate_browser__toggle_source(struct annotate_browser *browser)
431 {
432         struct disasm_line *dl;
433         struct browser_disasm_line *bdl;
434         off_t offset = browser->b.index - browser->b.top_idx;
435
436         browser->b.seek(&browser->b, offset, SEEK_CUR);
437         dl = list_entry(browser->b.top, struct disasm_line, node);
438         bdl = disasm_line__browser(dl);
439
440         if (annotate_browser__opts.hide_src_code) {
441                 if (bdl->idx_asm < offset)
442                         offset = bdl->idx;
443
444                 browser->b.nr_entries = browser->nr_entries;
445                 annotate_browser__opts.hide_src_code = false;
446                 browser->b.seek(&browser->b, -offset, SEEK_CUR);
447                 browser->b.top_idx = bdl->idx - offset;
448                 browser->b.index = bdl->idx;
449         } else {
450                 if (bdl->idx_asm < 0) {
451                         ui_helpline__puts("Only available for assembly lines.");
452                         browser->b.seek(&browser->b, -offset, SEEK_CUR);
453                         return false;
454                 }
455
456                 if (bdl->idx_asm < offset)
457                         offset = bdl->idx_asm;
458
459                 browser->b.nr_entries = browser->nr_asm_entries;
460                 annotate_browser__opts.hide_src_code = true;
461                 browser->b.seek(&browser->b, -offset, SEEK_CUR);
462                 browser->b.top_idx = bdl->idx_asm - offset;
463                 browser->b.index = bdl->idx_asm;
464         }
465
466         return true;
467 }
468
469 static void annotate_browser__init_asm_mode(struct annotate_browser *browser)
470 {
471         ui_browser__reset_index(&browser->b);
472         browser->b.nr_entries = browser->nr_asm_entries;
473 }
474
475 #define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
476
477 static int sym_title(struct symbol *sym, struct map *map, char *title,
478                      size_t sz)
479 {
480         return snprintf(title, sz, "%s  %s", sym->name, map->dso->long_name);
481 }
482
483 static bool annotate_browser__callq(struct annotate_browser *browser,
484                                     struct perf_evsel *evsel,
485                                     struct hist_browser_timer *hbt)
486 {
487         struct map_symbol *ms = browser->b.priv;
488         struct disasm_line *dl = browser->selection;
489         struct annotation *notes;
490         struct addr_map_symbol target = {
491                 .map = ms->map,
492                 .addr = map__objdump_2mem(ms->map, dl->ops.target.addr),
493         };
494         char title[SYM_TITLE_MAX_SIZE];
495
496         if (!ins__is_call(&dl->ins))
497                 return false;
498
499         if (map_groups__find_ams(&target) ||
500             map__rip_2objdump(target.map, target.map->map_ip(target.map,
501                                                              target.addr)) !=
502             dl->ops.target.addr) {
503                 ui_helpline__puts("The called function was not found.");
504                 return true;
505         }
506
507         notes = symbol__annotation(target.sym);
508         pthread_mutex_lock(&notes->lock);
509
510         if (notes->src == NULL && symbol__alloc_hist(target.sym) < 0) {
511                 pthread_mutex_unlock(&notes->lock);
512                 ui__warning("Not enough memory for annotating '%s' symbol!\n",
513                             target.sym->name);
514                 return true;
515         }
516
517         pthread_mutex_unlock(&notes->lock);
518         symbol__tui_annotate(target.sym, target.map, evsel, hbt);
519         sym_title(ms->sym, ms->map, title, sizeof(title));
520         ui_browser__show_title(&browser->b, title);
521         return true;
522 }
523
524 static
525 struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
526                                           s64 offset, s64 *idx)
527 {
528         struct map_symbol *ms = browser->b.priv;
529         struct symbol *sym = ms->sym;
530         struct annotation *notes = symbol__annotation(sym);
531         struct disasm_line *pos;
532
533         *idx = 0;
534         list_for_each_entry(pos, &notes->src->source, node) {
535                 if (pos->offset == offset)
536                         return pos;
537                 if (!disasm_line__filter(&browser->b, &pos->node))
538                         ++*idx;
539         }
540
541         return NULL;
542 }
543
544 static bool annotate_browser__jump(struct annotate_browser *browser)
545 {
546         struct disasm_line *dl = browser->selection;
547         u64 offset;
548         s64 idx;
549
550         if (!ins__is_jump(&dl->ins))
551                 return false;
552
553         offset = dl->ops.target.offset;
554         dl = annotate_browser__find_offset(browser, offset, &idx);
555         if (dl == NULL) {
556                 ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
557                 return true;
558         }
559
560         annotate_browser__set_top(browser, dl, idx);
561
562         return true;
563 }
564
565 static
566 struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
567                                           char *s, s64 *idx)
568 {
569         struct map_symbol *ms = browser->b.priv;
570         struct symbol *sym = ms->sym;
571         struct annotation *notes = symbol__annotation(sym);
572         struct disasm_line *pos = browser->selection;
573
574         *idx = browser->b.index;
575         list_for_each_entry_continue(pos, &notes->src->source, node) {
576                 if (disasm_line__filter(&browser->b, &pos->node))
577                         continue;
578
579                 ++*idx;
580
581                 if (pos->line && strstr(pos->line, s) != NULL)
582                         return pos;
583         }
584
585         return NULL;
586 }
587
588 static bool __annotate_browser__search(struct annotate_browser *browser)
589 {
590         struct disasm_line *dl;
591         s64 idx;
592
593         dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
594         if (dl == NULL) {
595                 ui_helpline__puts("String not found!");
596                 return false;
597         }
598
599         annotate_browser__set_top(browser, dl, idx);
600         browser->searching_backwards = false;
601         return true;
602 }
603
604 static
605 struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
606                                                   char *s, s64 *idx)
607 {
608         struct map_symbol *ms = browser->b.priv;
609         struct symbol *sym = ms->sym;
610         struct annotation *notes = symbol__annotation(sym);
611         struct disasm_line *pos = browser->selection;
612
613         *idx = browser->b.index;
614         list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
615                 if (disasm_line__filter(&browser->b, &pos->node))
616                         continue;
617
618                 --*idx;
619
620                 if (pos->line && strstr(pos->line, s) != NULL)
621                         return pos;
622         }
623
624         return NULL;
625 }
626
627 static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
628 {
629         struct disasm_line *dl;
630         s64 idx;
631
632         dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
633         if (dl == NULL) {
634                 ui_helpline__puts("String not found!");
635                 return false;
636         }
637
638         annotate_browser__set_top(browser, dl, idx);
639         browser->searching_backwards = true;
640         return true;
641 }
642
643 static bool annotate_browser__search_window(struct annotate_browser *browser,
644                                             int delay_secs)
645 {
646         if (ui_browser__input_window("Search", "String: ", browser->search_bf,
647                                      "ENTER: OK, ESC: Cancel",
648                                      delay_secs * 2) != K_ENTER ||
649             !*browser->search_bf)
650                 return false;
651
652         return true;
653 }
654
655 static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
656 {
657         if (annotate_browser__search_window(browser, delay_secs))
658                 return __annotate_browser__search(browser);
659
660         return false;
661 }
662
663 static bool annotate_browser__continue_search(struct annotate_browser *browser,
664                                               int delay_secs)
665 {
666         if (!*browser->search_bf)
667                 return annotate_browser__search(browser, delay_secs);
668
669         return __annotate_browser__search(browser);
670 }
671
672 static bool annotate_browser__search_reverse(struct annotate_browser *browser,
673                                            int delay_secs)
674 {
675         if (annotate_browser__search_window(browser, delay_secs))
676                 return __annotate_browser__search_reverse(browser);
677
678         return false;
679 }
680
681 static
682 bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
683                                                int delay_secs)
684 {
685         if (!*browser->search_bf)
686                 return annotate_browser__search_reverse(browser, delay_secs);
687
688         return __annotate_browser__search_reverse(browser);
689 }
690
691 static void annotate_browser__update_addr_width(struct annotate_browser *browser)
692 {
693         if (annotate_browser__opts.use_offset)
694                 browser->target_width = browser->min_addr_width;
695         else
696                 browser->target_width = browser->max_addr_width;
697
698         browser->addr_width = browser->target_width;
699
700         if (annotate_browser__opts.show_nr_jumps)
701                 browser->addr_width += browser->jumps_width + 1;
702 }
703
704 static int annotate_browser__run(struct annotate_browser *browser,
705                                  struct perf_evsel *evsel,
706                                  struct hist_browser_timer *hbt)
707 {
708         struct rb_node *nd = NULL;
709         struct map_symbol *ms = browser->b.priv;
710         struct symbol *sym = ms->sym;
711         const char *help = "Press 'h' for help on key bindings";
712         int delay_secs = hbt ? hbt->refresh : 0;
713         int key;
714         char title[SYM_TITLE_MAX_SIZE];
715
716         sym_title(sym, ms->map, title, sizeof(title));
717         if (ui_browser__show(&browser->b, title, help) < 0)
718                 return -1;
719
720         annotate_browser__calc_percent(browser, evsel);
721
722         if (browser->curr_hot) {
723                 annotate_browser__set_rb_top(browser, browser->curr_hot);
724                 browser->b.navkeypressed = false;
725         }
726
727         nd = browser->curr_hot;
728
729         while (1) {
730                 key = ui_browser__run(&browser->b, delay_secs);
731
732                 if (delay_secs != 0) {
733                         annotate_browser__calc_percent(browser, evsel);
734                         /*
735                          * Current line focus got out of the list of most active
736                          * lines, NULL it so that if TAB|UNTAB is pressed, we
737                          * move to curr_hot (current hottest line).
738                          */
739                         if (nd != NULL && RB_EMPTY_NODE(nd))
740                                 nd = NULL;
741                 }
742
743                 switch (key) {
744                 case K_TIMER:
745                         if (hbt)
746                                 hbt->timer(hbt->arg);
747
748                         if (delay_secs != 0)
749                                 symbol__annotate_decay_histogram(sym, evsel->idx);
750                         continue;
751                 case K_TAB:
752                         if (nd != NULL) {
753                                 nd = rb_prev(nd);
754                                 if (nd == NULL)
755                                         nd = rb_last(&browser->entries);
756                         } else
757                                 nd = browser->curr_hot;
758                         break;
759                 case K_UNTAB:
760                         if (nd != NULL) {
761                                 nd = rb_next(nd);
762                                 if (nd == NULL)
763                                         nd = rb_first(&browser->entries);
764                         } else
765                                 nd = browser->curr_hot;
766                         break;
767                 case K_F1:
768                 case 'h':
769                         ui_browser__help_window(&browser->b,
770                 "UP/DOWN/PGUP\n"
771                 "PGDN/SPACE    Navigate\n"
772                 "q/ESC/CTRL+C  Exit\n\n"
773                 "ENTER         Go to target\n"
774                 "ESC           Exit\n"
775                 "H             Cycle thru hottest instructions\n"
776                 "j             Toggle showing jump to target arrows\n"
777                 "J             Toggle showing number of jump sources on targets\n"
778                 "n             Search next string\n"
779                 "o             Toggle disassembler output/simplified view\n"
780                 "s             Toggle source code view\n"
781                 "t             Toggle total period view\n"
782                 "/             Search string\n"
783                 "k             Toggle line numbers\n"
784                 "r             Run available scripts\n"
785                 "?             Search string backwards\n");
786                         continue;
787                 case 'r':
788                         {
789                                 script_browse(NULL);
790                                 continue;
791                         }
792                 case 'k':
793                         annotate_browser__opts.show_linenr =
794                                 !annotate_browser__opts.show_linenr;
795                         break;
796                 case 'H':
797                         nd = browser->curr_hot;
798                         break;
799                 case 's':
800                         if (annotate_browser__toggle_source(browser))
801                                 ui_helpline__puts(help);
802                         continue;
803                 case 'o':
804                         annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset;
805                         annotate_browser__update_addr_width(browser);
806                         continue;
807                 case 'j':
808                         annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows;
809                         continue;
810                 case 'J':
811                         annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps;
812                         annotate_browser__update_addr_width(browser);
813                         continue;
814                 case '/':
815                         if (annotate_browser__search(browser, delay_secs)) {
816 show_help:
817                                 ui_helpline__puts(help);
818                         }
819                         continue;
820                 case 'n':
821                         if (browser->searching_backwards ?
822                             annotate_browser__continue_search_reverse(browser, delay_secs) :
823                             annotate_browser__continue_search(browser, delay_secs))
824                                 goto show_help;
825                         continue;
826                 case '?':
827                         if (annotate_browser__search_reverse(browser, delay_secs))
828                                 goto show_help;
829                         continue;
830                 case 'D': {
831                         static int seq;
832                         ui_helpline__pop();
833                         ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
834                                            seq++, browser->b.nr_entries,
835                                            browser->b.height,
836                                            browser->b.index,
837                                            browser->b.top_idx,
838                                            browser->nr_asm_entries);
839                 }
840                         continue;
841                 case K_ENTER:
842                 case K_RIGHT:
843                         if (browser->selection == NULL)
844                                 ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
845                         else if (browser->selection->offset == -1)
846                                 ui_helpline__puts("Actions are only available for assembly lines.");
847                         else if (!browser->selection->ins.ops)
848                                 goto show_sup_ins;
849                         else if (ins__is_ret(&browser->selection->ins))
850                                 goto out;
851                         else if (!(annotate_browser__jump(browser) ||
852                                      annotate_browser__callq(browser, evsel, hbt))) {
853 show_sup_ins:
854                                 ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
855                         }
856                         continue;
857                 case 't':
858                         annotate_browser__opts.show_total_period =
859                           !annotate_browser__opts.show_total_period;
860                         annotate_browser__update_addr_width(browser);
861                         continue;
862                 case K_LEFT:
863                 case K_ESC:
864                 case 'q':
865                 case CTRL('c'):
866                         goto out;
867                 default:
868                         continue;
869                 }
870
871                 if (nd != NULL)
872                         annotate_browser__set_rb_top(browser, nd);
873         }
874 out:
875         ui_browser__hide(&browser->b);
876         return key;
877 }
878
879 int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
880                              struct hist_browser_timer *hbt)
881 {
882         /* Set default value for show_total_period.  */
883         annotate_browser__opts.show_total_period =
884           symbol_conf.show_total_period;
885
886         return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
887 }
888
889 int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
890                              struct hist_browser_timer *hbt)
891 {
892         /* reset abort key so that it can get Ctrl-C as a key */
893         SLang_reset_tty();
894         SLang_init_tty(0, 0, 0);
895
896         return map_symbol__tui_annotate(&he->ms, evsel, hbt);
897 }
898
899
900 static unsigned count_insn(struct annotate_browser *browser, u64 start, u64 end)
901 {
902         unsigned n_insn = 0;
903         u64 offset;
904
905         for (offset = start; offset <= end; offset++) {
906                 if (browser->offsets[offset])
907                         n_insn++;
908         }
909         return n_insn;
910 }
911
912 static void count_and_fill(struct annotate_browser *browser, u64 start, u64 end,
913                            struct cyc_hist *ch)
914 {
915         unsigned n_insn;
916         u64 offset;
917
918         n_insn = count_insn(browser, start, end);
919         if (n_insn && ch->num && ch->cycles) {
920                 float ipc = n_insn / ((double)ch->cycles / (double)ch->num);
921
922                 /* Hide data when there are too many overlaps. */
923                 if (ch->reset >= 0x7fff || ch->reset >= ch->num / 2)
924                         return;
925
926                 for (offset = start; offset <= end; offset++) {
927                         struct disasm_line *dl = browser->offsets[offset];
928
929                         if (dl)
930                                 dl->ipc = ipc;
931                 }
932         }
933 }
934
935 /*
936  * This should probably be in util/annotate.c to share with the tty
937  * annotate, but right now we need the per byte offsets arrays,
938  * which are only here.
939  */
940 static void annotate__compute_ipc(struct annotate_browser *browser, size_t size,
941                            struct symbol *sym)
942 {
943         u64 offset;
944         struct annotation *notes = symbol__annotation(sym);
945
946         if (!notes->src || !notes->src->cycles_hist)
947                 return;
948
949         pthread_mutex_lock(&notes->lock);
950         for (offset = 0; offset < size; ++offset) {
951                 struct cyc_hist *ch;
952
953                 ch = &notes->src->cycles_hist[offset];
954                 if (ch && ch->cycles) {
955                         struct disasm_line *dl;
956
957                         if (ch->have_start)
958                                 count_and_fill(browser, ch->start, offset, ch);
959                         dl = browser->offsets[offset];
960                         if (dl && ch->num_aggr)
961                                 dl->cycles = ch->cycles_aggr / ch->num_aggr;
962                         browser->have_cycles = true;
963                 }
964         }
965         pthread_mutex_unlock(&notes->lock);
966 }
967
968 static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
969                                                 size_t size)
970 {
971         u64 offset;
972         struct map_symbol *ms = browser->b.priv;
973         struct symbol *sym = ms->sym;
974
975         /* PLT symbols contain external offsets */
976         if (strstr(sym->name, "@plt"))
977                 return;
978
979         for (offset = 0; offset < size; ++offset) {
980                 struct disasm_line *dl = browser->offsets[offset], *dlt;
981                 struct browser_disasm_line *bdlt;
982
983                 if (!disasm_line__is_valid_jump(dl, sym))
984                         continue;
985
986                 dlt = browser->offsets[dl->ops.target.offset];
987                 /*
988                  * FIXME: Oops, no jump target? Buggy disassembler? Or do we
989                  * have to adjust to the previous offset?
990                  */
991                 if (dlt == NULL)
992                         continue;
993
994                 bdlt = disasm_line__browser(dlt);
995                 if (++bdlt->jump_sources > browser->max_jump_sources)
996                         browser->max_jump_sources = bdlt->jump_sources;
997
998                 ++browser->nr_jumps;
999         }
1000 }
1001
1002 static inline int width_jumps(int n)
1003 {
1004         if (n >= 100)
1005                 return 5;
1006         if (n / 10)
1007                 return 2;
1008         return 1;
1009 }
1010
1011 int symbol__tui_annotate(struct symbol *sym, struct map *map,
1012                          struct perf_evsel *evsel,
1013                          struct hist_browser_timer *hbt)
1014 {
1015         struct disasm_line *pos, *n;
1016         struct annotation *notes;
1017         size_t size;
1018         struct map_symbol ms = {
1019                 .map = map,
1020                 .sym = sym,
1021         };
1022         struct annotate_browser browser = {
1023                 .b = {
1024                         .refresh = annotate_browser__refresh,
1025                         .seek    = ui_browser__list_head_seek,
1026                         .write   = annotate_browser__write,
1027                         .filter  = disasm_line__filter,
1028                         .priv    = &ms,
1029                         .use_navkeypressed = true,
1030                 },
1031         };
1032         int ret = -1, err;
1033         int nr_pcnt = 1;
1034         size_t sizeof_bdl = sizeof(struct browser_disasm_line);
1035
1036         if (sym == NULL)
1037                 return -1;
1038
1039         size = symbol__size(sym);
1040
1041         if (map->dso->annotate_warned)
1042                 return -1;
1043
1044         browser.offsets = zalloc(size * sizeof(struct disasm_line *));
1045         if (browser.offsets == NULL) {
1046                 ui__error("Not enough memory!");
1047                 return -1;
1048         }
1049
1050         if (perf_evsel__is_group_event(evsel)) {
1051                 nr_pcnt = evsel->nr_members;
1052                 sizeof_bdl += sizeof(struct disasm_line_samples) *
1053                   (nr_pcnt - 1);
1054         }
1055
1056         err = symbol__disassemble(sym, map, perf_evsel__env_arch(evsel), sizeof_bdl);
1057         if (err) {
1058                 char msg[BUFSIZ];
1059                 symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg));
1060                 ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
1061                 goto out_free_offsets;
1062         }
1063
1064         ui_helpline__push("Press ESC to exit");
1065
1066         notes = symbol__annotation(sym);
1067         browser.start = map__rip_2objdump(map, sym->start);
1068
1069         list_for_each_entry(pos, &notes->src->source, node) {
1070                 struct browser_disasm_line *bpos;
1071                 size_t line_len = strlen(pos->line);
1072
1073                 if (browser.b.width < line_len)
1074                         browser.b.width = line_len;
1075                 bpos = disasm_line__browser(pos);
1076                 bpos->idx = browser.nr_entries++;
1077                 if (pos->offset != -1) {
1078                         bpos->idx_asm = browser.nr_asm_entries++;
1079                         /*
1080                          * FIXME: short term bandaid to cope with assembly
1081                          * routines that comes with labels in the same column
1082                          * as the address in objdump, sigh.
1083                          *
1084                          * E.g. copy_user_generic_unrolled
1085                          */
1086                         if (pos->offset < (s64)size)
1087                                 browser.offsets[pos->offset] = pos;
1088                 } else
1089                         bpos->idx_asm = -1;
1090         }
1091
1092         annotate_browser__mark_jump_targets(&browser, size);
1093         annotate__compute_ipc(&browser, size, sym);
1094
1095         browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
1096         browser.max_addr_width = hex_width(sym->end);
1097         browser.jumps_width = width_jumps(browser.max_jump_sources);
1098         browser.nr_events = nr_pcnt;
1099         browser.b.nr_entries = browser.nr_entries;
1100         browser.b.entries = &notes->src->source,
1101         browser.b.width += 18; /* Percentage */
1102
1103         if (annotate_browser__opts.hide_src_code)
1104                 annotate_browser__init_asm_mode(&browser);
1105
1106         annotate_browser__update_addr_width(&browser);
1107
1108         ret = annotate_browser__run(&browser, evsel, hbt);
1109         list_for_each_entry_safe(pos, n, &notes->src->source, node) {
1110                 list_del(&pos->node);
1111                 disasm_line__free(pos);
1112         }
1113
1114 out_free_offsets:
1115         free(browser.offsets);
1116         return ret;
1117 }
1118
1119 #define ANNOTATE_CFG(n) \
1120         { .name = #n, .value = &annotate_browser__opts.n, }
1121
1122 /*
1123  * Keep the entries sorted, they are bsearch'ed
1124  */
1125 static struct annotate_config {
1126         const char *name;
1127         bool *value;
1128 } annotate__configs[] = {
1129         ANNOTATE_CFG(hide_src_code),
1130         ANNOTATE_CFG(jump_arrows),
1131         ANNOTATE_CFG(show_linenr),
1132         ANNOTATE_CFG(show_nr_jumps),
1133         ANNOTATE_CFG(show_total_period),
1134         ANNOTATE_CFG(use_offset),
1135 };
1136
1137 #undef ANNOTATE_CFG
1138
1139 static int annotate_config__cmp(const void *name, const void *cfgp)
1140 {
1141         const struct annotate_config *cfg = cfgp;
1142
1143         return strcmp(name, cfg->name);
1144 }
1145
1146 static int annotate__config(const char *var, const char *value,
1147                             void *data __maybe_unused)
1148 {
1149         struct annotate_config *cfg;
1150         const char *name;
1151
1152         if (prefixcmp(var, "annotate.") != 0)
1153                 return 0;
1154
1155         name = var + 9;
1156         cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1157                       sizeof(struct annotate_config), annotate_config__cmp);
1158
1159         if (cfg == NULL)
1160                 ui__warning("%s variable unknown, ignoring...", var);
1161         else
1162                 *cfg->value = perf_config_bool(name, value);
1163         return 0;
1164 }
1165
1166 void annotate_browser__init(void)
1167 {
1168         perf_config(annotate__config, NULL);
1169 }