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