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