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