]> git.proxmox.com Git - mirror_ubuntu-focal-kernel.git/blob - tools/perf/ui/browsers/annotate.c
perf tools: Including missing inttypes.h header
[mirror_ubuntu-focal-kernel.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 }