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