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