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