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