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