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