]> git.karo-electronics.de Git - karo-tx-linux.git/blob - tools/perf/ui/browsers/annotate.c
Merge remote-tracking branch 'usb-chipidea-next/ci-for-usb-next'
[karo-tx-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 <pthread.h>
12
13 struct disasm_line_samples {
14         double          percent;
15         u64             nr;
16 };
17
18 #define IPC_WIDTH 6
19 #define CYCLES_WIDTH 6
20
21 struct browser_disasm_line {
22         struct rb_node                  rb_node;
23         u32                             idx;
24         int                             idx_asm;
25         int                             jump_sources;
26         /*
27          * actual length of this array is saved on the nr_events field
28          * of the struct annotate_browser
29          */
30         struct disasm_line_samples      samples[1];
31 };
32
33 static struct annotate_browser_opt {
34         bool hide_src_code,
35              use_offset,
36              jump_arrows,
37              show_linenr,
38              show_nr_jumps,
39              show_total_period;
40 } annotate_browser__opts = {
41         .use_offset     = true,
42         .jump_arrows    = true,
43 };
44
45 struct annotate_browser {
46         struct ui_browser b;
47         struct rb_root    entries;
48         struct rb_node    *curr_hot;
49         struct disasm_line  *selection;
50         struct disasm_line  **offsets;
51         int                 nr_events;
52         u64                 start;
53         int                 nr_asm_entries;
54         int                 nr_entries;
55         int                 max_jump_sources;
56         int                 nr_jumps;
57         bool                searching_backwards;
58         bool                have_cycles;
59         u8                  addr_width;
60         u8                  jumps_width;
61         u8                  target_width;
62         u8                  min_addr_width;
63         u8                  max_addr_width;
64         char                search_bf[128];
65 };
66
67 static inline struct browser_disasm_line *disasm_line__browser(struct disasm_line *dl)
68 {
69         return (struct browser_disasm_line *)(dl + 1);
70 }
71
72 static bool disasm_line__filter(struct ui_browser *browser __maybe_unused,
73                                 void *entry)
74 {
75         if (annotate_browser__opts.hide_src_code) {
76                 struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
77                 return dl->offset == -1;
78         }
79
80         return false;
81 }
82
83 static int annotate_browser__jumps_percent_color(struct annotate_browser *browser,
84                                                  int nr, bool current)
85 {
86         if (current && (!browser->b.use_navkeypressed || browser->b.navkeypressed))
87                 return HE_COLORSET_SELECTED;
88         if (nr == browser->max_jump_sources)
89                 return HE_COLORSET_TOP;
90         if (nr > 1)
91                 return HE_COLORSET_MEDIUM;
92         return HE_COLORSET_NORMAL;
93 }
94
95 static int annotate_browser__set_jumps_percent_color(struct annotate_browser *browser,
96                                                      int nr, bool current)
97 {
98          int color = annotate_browser__jumps_percent_color(browser, nr, current);
99          return ui_browser__set_color(&browser->b, color);
100 }
101
102 static int annotate_browser__pcnt_width(struct annotate_browser *ab)
103 {
104         int w = 7 * ab->nr_events;
105
106         if (ab->have_cycles)
107                 w += IPC_WIDTH + CYCLES_WIDTH;
108         return w;
109 }
110
111 static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
112 {
113         struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
114         struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
115         struct browser_disasm_line *bdl = disasm_line__browser(dl);
116         bool current_entry = ui_browser__is_current_entry(browser, row);
117         bool change_color = (!annotate_browser__opts.hide_src_code &&
118                              (!current_entry || (browser->use_navkeypressed &&
119                                                  !browser->navkeypressed)));
120         int width = browser->width, printed;
121         int i, pcnt_width = annotate_browser__pcnt_width(ab);
122         double percent_max = 0.0;
123         char bf[256];
124
125         for (i = 0; i < ab->nr_events; i++) {
126                 if (bdl->samples[i].percent > percent_max)
127                         percent_max = bdl->samples[i].percent;
128         }
129
130         if (dl->offset != -1 && percent_max != 0.0) {
131                 if (percent_max != 0.0) {
132                         for (i = 0; i < ab->nr_events; i++) {
133                                 ui_browser__set_percent_color(browser,
134                                                         bdl->samples[i].percent,
135                                                         current_entry);
136                                 if (annotate_browser__opts.show_total_period) {
137                                         ui_browser__printf(browser, "%6" PRIu64 " ",
138                                                            bdl->samples[i].nr);
139                                 } else {
140                                         ui_browser__printf(browser, "%6.2f ",
141                                                            bdl->samples[i].percent);
142                                 }
143                         }
144                 } else {
145                         ui_browser__write_nstring(browser, " ", 7 * ab->nr_events);
146                 }
147         } else {
148                 ui_browser__set_percent_color(browser, 0, current_entry);
149                 ui_browser__write_nstring(browser, " ", 7 * ab->nr_events);
150         }
151         if (ab->have_cycles) {
152                 if (dl->ipc)
153                         ui_browser__printf(browser, "%*.2f ", IPC_WIDTH - 1, dl->ipc);
154                 else
155                         ui_browser__write_nstring(browser, " ", IPC_WIDTH);
156                 if (dl->cycles)
157                         ui_browser__printf(browser, "%*" PRIu64 " ",
158                                            CYCLES_WIDTH - 1, dl->cycles);
159                 else
160                         ui_browser__write_nstring(browser, " ", CYCLES_WIDTH);
161         }
162
163         SLsmg_write_char(' ');
164
165         /* The scroll bar isn't being used */
166         if (!browser->navkeypressed)
167                 width += 1;
168
169         if (!*dl->line)
170                 ui_browser__write_nstring(browser, " ", width - pcnt_width);
171         else if (dl->offset == -1) {
172                 if (dl->line_nr && annotate_browser__opts.show_linenr)
173                         printed = scnprintf(bf, sizeof(bf), "%-*d ",
174                                         ab->addr_width + 1, dl->line_nr);
175                 else
176                         printed = scnprintf(bf, sizeof(bf), "%*s  ",
177                                     ab->addr_width, " ");
178                 ui_browser__write_nstring(browser, bf, printed);
179                 ui_browser__write_nstring(browser, dl->line, width - printed - pcnt_width + 1);
180         } else {
181                 u64 addr = dl->offset;
182                 int color = -1;
183
184                 if (!annotate_browser__opts.use_offset)
185                         addr += ab->start;
186
187                 if (!annotate_browser__opts.use_offset) {
188                         printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
189                 } else {
190                         if (bdl->jump_sources) {
191                                 if (annotate_browser__opts.show_nr_jumps) {
192                                         int prev;
193                                         printed = scnprintf(bf, sizeof(bf), "%*d ",
194                                                             ab->jumps_width,
195                                                             bdl->jump_sources);
196                                         prev = annotate_browser__set_jumps_percent_color(ab, bdl->jump_sources,
197                                                                                          current_entry);
198                                         ui_browser__write_nstring(browser, bf, printed);
199                                         ui_browser__set_color(browser, prev);
200                                 }
201
202                                 printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
203                                                     ab->target_width, addr);
204                         } else {
205                                 printed = scnprintf(bf, sizeof(bf), "%*s  ",
206                                                     ab->addr_width, " ");
207                         }
208                 }
209
210                 if (change_color)
211                         color = ui_browser__set_color(browser, HE_COLORSET_ADDR);
212                 ui_browser__write_nstring(browser, bf, printed);
213                 if (change_color)
214                         ui_browser__set_color(browser, color);
215                 if (dl->ins && dl->ins->ops->scnprintf) {
216                         if (ins__is_jump(dl->ins)) {
217                                 bool fwd = dl->ops.target.offset > (u64)dl->offset;
218
219                                 ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR :
220                                                                     SLSMG_UARROW_CHAR);
221                                 SLsmg_write_char(' ');
222                         } else if (ins__is_call(dl->ins)) {
223                                 ui_browser__write_graph(browser, SLSMG_RARROW_CHAR);
224                                 SLsmg_write_char(' ');
225                         } else {
226                                 ui_browser__write_nstring(browser, " ", 2);
227                         }
228                 } else {
229                         if (strcmp(dl->name, "retq")) {
230                                 ui_browser__write_nstring(browser, " ", 2);
231                         } else {
232                                 ui_browser__write_graph(browser, SLSMG_LARROW_CHAR);
233                                 SLsmg_write_char(' ');
234                         }
235                 }
236
237                 disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset);
238                 ui_browser__write_nstring(browser, bf, width - pcnt_width - 3 - printed);
239         }
240
241         if (current_entry)
242                 ab->selection = dl;
243 }
244
245 static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym)
246 {
247         if (!dl || !dl->ins || !ins__is_jump(dl->ins)
248             || !disasm_line__has_offset(dl)
249             || dl->ops.target.offset >= 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, NULL) ||
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         s64 idx;
548
549         if (!ins__is_jump(dl->ins))
550                 return false;
551
552         dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx);
553         if (dl == NULL) {
554                 ui_helpline__puts("Invalid jump offset");
555                 return true;
556         }
557
558         annotate_browser__set_top(browser, dl, idx);
559
560         return true;
561 }
562
563 static
564 struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
565                                           char *s, s64 *idx)
566 {
567         struct map_symbol *ms = browser->b.priv;
568         struct symbol *sym = ms->sym;
569         struct annotation *notes = symbol__annotation(sym);
570         struct disasm_line *pos = browser->selection;
571
572         *idx = browser->b.index;
573         list_for_each_entry_continue(pos, &notes->src->source, node) {
574                 if (disasm_line__filter(&browser->b, &pos->node))
575                         continue;
576
577                 ++*idx;
578
579                 if (pos->line && strstr(pos->line, s) != NULL)
580                         return pos;
581         }
582
583         return NULL;
584 }
585
586 static bool __annotate_browser__search(struct annotate_browser *browser)
587 {
588         struct disasm_line *dl;
589         s64 idx;
590
591         dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
592         if (dl == NULL) {
593                 ui_helpline__puts("String not found!");
594                 return false;
595         }
596
597         annotate_browser__set_top(browser, dl, idx);
598         browser->searching_backwards = false;
599         return true;
600 }
601
602 static
603 struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
604                                                   char *s, s64 *idx)
605 {
606         struct map_symbol *ms = browser->b.priv;
607         struct symbol *sym = ms->sym;
608         struct annotation *notes = symbol__annotation(sym);
609         struct disasm_line *pos = browser->selection;
610
611         *idx = browser->b.index;
612         list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
613                 if (disasm_line__filter(&browser->b, &pos->node))
614                         continue;
615
616                 --*idx;
617
618                 if (pos->line && strstr(pos->line, s) != NULL)
619                         return pos;
620         }
621
622         return NULL;
623 }
624
625 static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
626 {
627         struct disasm_line *dl;
628         s64 idx;
629
630         dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
631         if (dl == NULL) {
632                 ui_helpline__puts("String not found!");
633                 return false;
634         }
635
636         annotate_browser__set_top(browser, dl, idx);
637         browser->searching_backwards = true;
638         return true;
639 }
640
641 static bool annotate_browser__search_window(struct annotate_browser *browser,
642                                             int delay_secs)
643 {
644         if (ui_browser__input_window("Search", "String: ", browser->search_bf,
645                                      "ENTER: OK, ESC: Cancel",
646                                      delay_secs * 2) != K_ENTER ||
647             !*browser->search_bf)
648                 return false;
649
650         return true;
651 }
652
653 static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
654 {
655         if (annotate_browser__search_window(browser, delay_secs))
656                 return __annotate_browser__search(browser);
657
658         return false;
659 }
660
661 static bool annotate_browser__continue_search(struct annotate_browser *browser,
662                                               int delay_secs)
663 {
664         if (!*browser->search_bf)
665                 return annotate_browser__search(browser, delay_secs);
666
667         return __annotate_browser__search(browser);
668 }
669
670 static bool annotate_browser__search_reverse(struct annotate_browser *browser,
671                                            int delay_secs)
672 {
673         if (annotate_browser__search_window(browser, delay_secs))
674                 return __annotate_browser__search_reverse(browser);
675
676         return false;
677 }
678
679 static
680 bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
681                                                int delay_secs)
682 {
683         if (!*browser->search_bf)
684                 return annotate_browser__search_reverse(browser, delay_secs);
685
686         return __annotate_browser__search_reverse(browser);
687 }
688
689 static void annotate_browser__update_addr_width(struct annotate_browser *browser)
690 {
691         if (annotate_browser__opts.use_offset)
692                 browser->target_width = browser->min_addr_width;
693         else
694                 browser->target_width = browser->max_addr_width;
695
696         browser->addr_width = browser->target_width;
697
698         if (annotate_browser__opts.show_nr_jumps)
699                 browser->addr_width += browser->jumps_width + 1;
700 }
701
702 static int annotate_browser__run(struct annotate_browser *browser,
703                                  struct perf_evsel *evsel,
704                                  struct hist_browser_timer *hbt)
705 {
706         struct rb_node *nd = NULL;
707         struct map_symbol *ms = browser->b.priv;
708         struct symbol *sym = ms->sym;
709         const char *help = "Press 'h' for help on key bindings";
710         int delay_secs = hbt ? hbt->refresh : 0;
711         int key;
712         char title[SYM_TITLE_MAX_SIZE];
713
714         sym_title(sym, ms->map, title, sizeof(title));
715         if (ui_browser__show(&browser->b, title, help) < 0)
716                 return -1;
717
718         annotate_browser__calc_percent(browser, evsel);
719
720         if (browser->curr_hot) {
721                 annotate_browser__set_rb_top(browser, browser->curr_hot);
722                 browser->b.navkeypressed = false;
723         }
724
725         nd = browser->curr_hot;
726
727         while (1) {
728                 key = ui_browser__run(&browser->b, delay_secs);
729
730                 if (delay_secs != 0) {
731                         annotate_browser__calc_percent(browser, evsel);
732                         /*
733                          * Current line focus got out of the list of most active
734                          * lines, NULL it so that if TAB|UNTAB is pressed, we
735                          * move to curr_hot (current hottest line).
736                          */
737                         if (nd != NULL && RB_EMPTY_NODE(nd))
738                                 nd = NULL;
739                 }
740
741                 switch (key) {
742                 case K_TIMER:
743                         if (hbt)
744                                 hbt->timer(hbt->arg);
745
746                         if (delay_secs != 0)
747                                 symbol__annotate_decay_histogram(sym, evsel->idx);
748                         continue;
749                 case K_TAB:
750                         if (nd != NULL) {
751                                 nd = rb_prev(nd);
752                                 if (nd == NULL)
753                                         nd = rb_last(&browser->entries);
754                         } else
755                                 nd = browser->curr_hot;
756                         break;
757                 case K_UNTAB:
758                         if (nd != NULL) {
759                                 nd = rb_next(nd);
760                                 if (nd == NULL)
761                                         nd = rb_first(&browser->entries);
762                         } else
763                                 nd = browser->curr_hot;
764                         break;
765                 case K_F1:
766                 case 'h':
767                         ui_browser__help_window(&browser->b,
768                 "UP/DOWN/PGUP\n"
769                 "PGDN/SPACE    Navigate\n"
770                 "q/ESC/CTRL+C  Exit\n\n"
771                 "ENTER         Go to target\n"
772                 "ESC           Exit\n"
773                 "H             Cycle thru hottest instructions\n"
774                 "j             Toggle showing jump to target arrows\n"
775                 "J             Toggle showing number of jump sources on targets\n"
776                 "n             Search next string\n"
777                 "o             Toggle disassembler output/simplified view\n"
778                 "s             Toggle source code view\n"
779                 "t             Toggle total period view\n"
780                 "/             Search string\n"
781                 "k             Toggle line numbers\n"
782                 "r             Run available scripts\n"
783                 "?             Search string backwards\n");
784                         continue;
785                 case 'r':
786                         {
787                                 script_browse(NULL);
788                                 continue;
789                         }
790                 case 'k':
791                         annotate_browser__opts.show_linenr =
792                                 !annotate_browser__opts.show_linenr;
793                         break;
794                 case 'H':
795                         nd = browser->curr_hot;
796                         break;
797                 case 's':
798                         if (annotate_browser__toggle_source(browser))
799                                 ui_helpline__puts(help);
800                         continue;
801                 case 'o':
802                         annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset;
803                         annotate_browser__update_addr_width(browser);
804                         continue;
805                 case 'j':
806                         annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows;
807                         continue;
808                 case 'J':
809                         annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps;
810                         annotate_browser__update_addr_width(browser);
811                         continue;
812                 case '/':
813                         if (annotate_browser__search(browser, delay_secs)) {
814 show_help:
815                                 ui_helpline__puts(help);
816                         }
817                         continue;
818                 case 'n':
819                         if (browser->searching_backwards ?
820                             annotate_browser__continue_search_reverse(browser, delay_secs) :
821                             annotate_browser__continue_search(browser, delay_secs))
822                                 goto show_help;
823                         continue;
824                 case '?':
825                         if (annotate_browser__search_reverse(browser, delay_secs))
826                                 goto show_help;
827                         continue;
828                 case 'D': {
829                         static int seq;
830                         ui_helpline__pop();
831                         ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
832                                            seq++, browser->b.nr_entries,
833                                            browser->b.height,
834                                            browser->b.index,
835                                            browser->b.top_idx,
836                                            browser->nr_asm_entries);
837                 }
838                         continue;
839                 case K_ENTER:
840                 case K_RIGHT:
841                         if (browser->selection == NULL)
842                                 ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
843                         else if (browser->selection->offset == -1)
844                                 ui_helpline__puts("Actions are only available for assembly lines.");
845                         else if (!browser->selection->ins) {
846                                 if (strcmp(browser->selection->name, "retq"))
847                                         goto show_sup_ins;
848                                 goto out;
849                         } else if (!(annotate_browser__jump(browser) ||
850                                      annotate_browser__callq(browser, evsel, hbt))) {
851 show_sup_ins:
852                                 ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions.");
853                         }
854                         continue;
855                 case 't':
856                         annotate_browser__opts.show_total_period =
857                           !annotate_browser__opts.show_total_period;
858                         annotate_browser__update_addr_width(browser);
859                         continue;
860                 case K_LEFT:
861                 case K_ESC:
862                 case 'q':
863                 case CTRL('c'):
864                         goto out;
865                 default:
866                         continue;
867                 }
868
869                 if (nd != NULL)
870                         annotate_browser__set_rb_top(browser, nd);
871         }
872 out:
873         ui_browser__hide(&browser->b);
874         return key;
875 }
876
877 int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
878                              struct hist_browser_timer *hbt)
879 {
880         /* Set default value for show_total_period.  */
881         annotate_browser__opts.show_total_period =
882           symbol_conf.show_total_period;
883
884         return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
885 }
886
887 int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
888                              struct hist_browser_timer *hbt)
889 {
890         /* reset abort key so that it can get Ctrl-C as a key */
891         SLang_reset_tty();
892         SLang_init_tty(0, 0, 0);
893
894         return map_symbol__tui_annotate(&he->ms, evsel, hbt);
895 }
896
897
898 static unsigned count_insn(struct annotate_browser *browser, u64 start, u64 end)
899 {
900         unsigned n_insn = 0;
901         u64 offset;
902
903         for (offset = start; offset <= end; offset++) {
904                 if (browser->offsets[offset])
905                         n_insn++;
906         }
907         return n_insn;
908 }
909
910 static void count_and_fill(struct annotate_browser *browser, u64 start, u64 end,
911                            struct cyc_hist *ch)
912 {
913         unsigned n_insn;
914         u64 offset;
915
916         n_insn = count_insn(browser, start, end);
917         if (n_insn && ch->num && ch->cycles) {
918                 float ipc = n_insn / ((double)ch->cycles / (double)ch->num);
919
920                 /* Hide data when there are too many overlaps. */
921                 if (ch->reset >= 0x7fff || ch->reset >= ch->num / 2)
922                         return;
923
924                 for (offset = start; offset <= end; offset++) {
925                         struct disasm_line *dl = browser->offsets[offset];
926
927                         if (dl)
928                                 dl->ipc = ipc;
929                 }
930         }
931 }
932
933 /*
934  * This should probably be in util/annotate.c to share with the tty
935  * annotate, but right now we need the per byte offsets arrays,
936  * which are only here.
937  */
938 static void annotate__compute_ipc(struct annotate_browser *browser, size_t size,
939                            struct symbol *sym)
940 {
941         u64 offset;
942         struct annotation *notes = symbol__annotation(sym);
943
944         if (!notes->src || !notes->src->cycles_hist)
945                 return;
946
947         pthread_mutex_lock(&notes->lock);
948         for (offset = 0; offset < size; ++offset) {
949                 struct cyc_hist *ch;
950
951                 ch = &notes->src->cycles_hist[offset];
952                 if (ch && ch->cycles) {
953                         struct disasm_line *dl;
954
955                         if (ch->have_start)
956                                 count_and_fill(browser, ch->start, offset, ch);
957                         dl = browser->offsets[offset];
958                         if (dl && ch->num_aggr)
959                                 dl->cycles = ch->cycles_aggr / ch->num_aggr;
960                         browser->have_cycles = true;
961                 }
962         }
963         pthread_mutex_unlock(&notes->lock);
964 }
965
966 static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
967                                                 size_t size)
968 {
969         u64 offset;
970         struct map_symbol *ms = browser->b.priv;
971         struct symbol *sym = ms->sym;
972
973         /* PLT symbols contain external offsets */
974         if (strstr(sym->name, "@plt"))
975                 return;
976
977         for (offset = 0; offset < size; ++offset) {
978                 struct disasm_line *dl = browser->offsets[offset], *dlt;
979                 struct browser_disasm_line *bdlt;
980
981                 if (!disasm_line__is_valid_jump(dl, sym))
982                         continue;
983
984                 dlt = browser->offsets[dl->ops.target.offset];
985                 /*
986                  * FIXME: Oops, no jump target? Buggy disassembler? Or do we
987                  * have to adjust to the previous offset?
988                  */
989                 if (dlt == NULL)
990                         continue;
991
992                 bdlt = disasm_line__browser(dlt);
993                 if (++bdlt->jump_sources > browser->max_jump_sources)
994                         browser->max_jump_sources = bdlt->jump_sources;
995
996                 ++browser->nr_jumps;
997         }
998 }
999
1000 static inline int width_jumps(int n)
1001 {
1002         if (n >= 100)
1003                 return 5;
1004         if (n / 10)
1005                 return 2;
1006         return 1;
1007 }
1008
1009 int symbol__tui_annotate(struct symbol *sym, struct map *map,
1010                          struct perf_evsel *evsel,
1011                          struct hist_browser_timer *hbt)
1012 {
1013         struct disasm_line *pos, *n;
1014         struct annotation *notes;
1015         size_t size;
1016         struct map_symbol ms = {
1017                 .map = map,
1018                 .sym = sym,
1019         };
1020         struct annotate_browser browser = {
1021                 .b = {
1022                         .refresh = annotate_browser__refresh,
1023                         .seek    = ui_browser__list_head_seek,
1024                         .write   = annotate_browser__write,
1025                         .filter  = disasm_line__filter,
1026                         .priv    = &ms,
1027                         .use_navkeypressed = true,
1028                 },
1029         };
1030         int ret = -1;
1031         int nr_pcnt = 1;
1032         size_t sizeof_bdl = sizeof(struct browser_disasm_line);
1033
1034         if (sym == NULL)
1035                 return -1;
1036
1037         size = symbol__size(sym);
1038
1039         if (map->dso->annotate_warned)
1040                 return -1;
1041
1042         browser.offsets = zalloc(size * sizeof(struct disasm_line *));
1043         if (browser.offsets == NULL) {
1044                 ui__error("Not enough memory!");
1045                 return -1;
1046         }
1047
1048         if (perf_evsel__is_group_event(evsel)) {
1049                 nr_pcnt = evsel->nr_members;
1050                 sizeof_bdl += sizeof(struct disasm_line_samples) *
1051                   (nr_pcnt - 1);
1052         }
1053
1054         if (symbol__annotate(sym, map, sizeof_bdl) < 0) {
1055                 ui__error("%s", ui_helpline__last_msg);
1056                 goto out_free_offsets;
1057         }
1058
1059         ui_helpline__push("Press ESC to exit");
1060
1061         notes = symbol__annotation(sym);
1062         browser.start = map__rip_2objdump(map, sym->start);
1063
1064         list_for_each_entry(pos, &notes->src->source, node) {
1065                 struct browser_disasm_line *bpos;
1066                 size_t line_len = strlen(pos->line);
1067
1068                 if (browser.b.width < line_len)
1069                         browser.b.width = line_len;
1070                 bpos = disasm_line__browser(pos);
1071                 bpos->idx = browser.nr_entries++;
1072                 if (pos->offset != -1) {
1073                         bpos->idx_asm = browser.nr_asm_entries++;
1074                         /*
1075                          * FIXME: short term bandaid to cope with assembly
1076                          * routines that comes with labels in the same column
1077                          * as the address in objdump, sigh.
1078                          *
1079                          * E.g. copy_user_generic_unrolled
1080                          */
1081                         if (pos->offset < (s64)size)
1082                                 browser.offsets[pos->offset] = pos;
1083                 } else
1084                         bpos->idx_asm = -1;
1085         }
1086
1087         annotate_browser__mark_jump_targets(&browser, size);
1088         annotate__compute_ipc(&browser, size, sym);
1089
1090         browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
1091         browser.max_addr_width = hex_width(sym->end);
1092         browser.jumps_width = width_jumps(browser.max_jump_sources);
1093         browser.nr_events = nr_pcnt;
1094         browser.b.nr_entries = browser.nr_entries;
1095         browser.b.entries = &notes->src->source,
1096         browser.b.width += 18; /* Percentage */
1097
1098         if (annotate_browser__opts.hide_src_code)
1099                 annotate_browser__init_asm_mode(&browser);
1100
1101         annotate_browser__update_addr_width(&browser);
1102
1103         ret = annotate_browser__run(&browser, evsel, hbt);
1104         list_for_each_entry_safe(pos, n, &notes->src->source, node) {
1105                 list_del(&pos->node);
1106                 disasm_line__free(pos);
1107         }
1108
1109 out_free_offsets:
1110         free(browser.offsets);
1111         return ret;
1112 }
1113
1114 #define ANNOTATE_CFG(n) \
1115         { .name = #n, .value = &annotate_browser__opts.n, }
1116
1117 /*
1118  * Keep the entries sorted, they are bsearch'ed
1119  */
1120 static struct annotate_config {
1121         const char *name;
1122         bool *value;
1123 } annotate__configs[] = {
1124         ANNOTATE_CFG(hide_src_code),
1125         ANNOTATE_CFG(jump_arrows),
1126         ANNOTATE_CFG(show_linenr),
1127         ANNOTATE_CFG(show_nr_jumps),
1128         ANNOTATE_CFG(show_total_period),
1129         ANNOTATE_CFG(use_offset),
1130 };
1131
1132 #undef ANNOTATE_CFG
1133
1134 static int annotate_config__cmp(const void *name, const void *cfgp)
1135 {
1136         const struct annotate_config *cfg = cfgp;
1137
1138         return strcmp(name, cfg->name);
1139 }
1140
1141 static int annotate__config(const char *var, const char *value,
1142                             void *data __maybe_unused)
1143 {
1144         struct annotate_config *cfg;
1145         const char *name;
1146
1147         if (prefixcmp(var, "annotate.") != 0)
1148                 return 0;
1149
1150         name = var + 9;
1151         cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1152                       sizeof(struct annotate_config), annotate_config__cmp);
1153
1154         if (cfg == NULL)
1155                 ui__warning("%s variable unknown, ignoring...", var);
1156         else
1157                 *cfg->value = perf_config_bool(name, value);
1158         return 0;
1159 }
1160
1161 void annotate_browser__init(void)
1162 {
1163         perf_config(annotate__config, NULL);
1164 }