1 // SPDX-License-Identifier: GPL-2.0
2 #include "../../util/util.h"
3 #include "../browser.h"
4 #include "../helpline.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"
16 #include <linux/kernel.h>
17 #include <linux/string.h>
18 #include <sys/ttydefaults.h>
20 struct disasm_line_samples
{
22 struct sym_hist_entry he
;
26 #define CYCLES_WIDTH 6
34 static struct annotate_browser_opt
{
42 } annotate_browser__opts
= {
49 struct annotate_browser
{
51 struct rb_root entries
;
52 struct rb_node
*curr_hot
;
53 struct annotation_line
*selection
;
54 struct annotation_line
**offsets
;
62 bool searching_backwards
;
72 static inline struct browser_line
*browser_line(struct annotation_line
*al
)
76 ptr
= container_of(al
, struct disasm_line
, al
);
77 return ptr
- sizeof(struct browser_line
);
80 static bool disasm_line__filter(struct ui_browser
*browser __maybe_unused
,
83 if (annotate_browser__opts
.hide_src_code
) {
84 struct annotation_line
*al
= list_entry(entry
, struct annotation_line
, node
);
86 return al
->offset
== -1;
92 static int annotate_browser__jumps_percent_color(struct annotate_browser
*browser
,
95 if (current
&& (!browser
->b
.use_navkeypressed
|| browser
->b
.navkeypressed
))
96 return HE_COLORSET_SELECTED
;
97 if (nr
== browser
->max_jump_sources
)
98 return HE_COLORSET_TOP
;
100 return HE_COLORSET_MEDIUM
;
101 return HE_COLORSET_NORMAL
;
104 static int annotate_browser__set_jumps_percent_color(struct annotate_browser
*browser
,
105 int nr
, bool current
)
107 int color
= annotate_browser__jumps_percent_color(browser
, nr
, current
);
108 return ui_browser__set_color(&browser
->b
, color
);
111 static int annotate_browser__pcnt_width(struct annotate_browser
*ab
)
113 return (annotate_browser__opts
.show_total_period
? 12 : 7) * ab
->nr_events
;
116 static int annotate_browser__cycles_width(struct annotate_browser
*ab
)
118 return ab
->have_cycles
? IPC_WIDTH
+ CYCLES_WIDTH
: 0;
121 static void disasm_line__write(struct disasm_line
*dl
, struct ui_browser
*browser
,
122 char *bf
, size_t size
)
124 if (dl
->ins
.ops
&& dl
->ins
.ops
->scnprintf
) {
125 if (ins__is_jump(&dl
->ins
)) {
126 bool fwd
= dl
->ops
.target
.offset
> dl
->al
.offset
;
128 ui_browser__write_graph(browser
, fwd
? SLSMG_DARROW_CHAR
:
130 SLsmg_write_char(' ');
131 } else if (ins__is_call(&dl
->ins
)) {
132 ui_browser__write_graph(browser
, SLSMG_RARROW_CHAR
);
133 SLsmg_write_char(' ');
134 } else if (ins__is_ret(&dl
->ins
)) {
135 ui_browser__write_graph(browser
, SLSMG_LARROW_CHAR
);
136 SLsmg_write_char(' ');
138 ui_browser__write_nstring(browser
, " ", 2);
141 ui_browser__write_nstring(browser
, " ", 2);
144 disasm_line__scnprintf(dl
, bf
, size
, !annotate_browser__opts
.use_offset
);
147 static void annotate_browser__write(struct ui_browser
*browser
, void *entry
, int row
)
149 struct annotate_browser
*ab
= container_of(browser
, struct annotate_browser
, b
);
150 struct annotation_line
*al
= list_entry(entry
, struct annotation_line
, node
);
151 struct browser_line
*bl
= browser_line(al
);
152 bool current_entry
= ui_browser__is_current_entry(browser
, row
);
153 bool change_color
= (!annotate_browser__opts
.hide_src_code
&&
154 (!current_entry
|| (browser
->use_navkeypressed
&&
155 !browser
->navkeypressed
)));
156 int width
= browser
->width
, printed
;
157 int i
, pcnt_width
= annotate_browser__pcnt_width(ab
),
158 cycles_width
= annotate_browser__cycles_width(ab
);
159 double percent_max
= 0.0;
161 bool show_title
= false;
163 for (i
= 0; i
< ab
->nr_events
; i
++) {
164 if (al
->samples
[i
].percent
> percent_max
)
165 percent_max
= al
->samples
[i
].percent
;
168 if ((row
== 0) && (al
->offset
== -1 || percent_max
== 0.0)) {
169 if (ab
->have_cycles
) {
170 if (al
->ipc
== 0.0 && al
->cycles
== 0)
176 if (al
->offset
!= -1 && percent_max
!= 0.0) {
177 for (i
= 0; i
< ab
->nr_events
; i
++) {
178 ui_browser__set_percent_color(browser
,
179 al
->samples
[i
].percent
,
181 if (annotate_browser__opts
.show_total_period
) {
182 ui_browser__printf(browser
, "%11" PRIu64
" ",
183 al
->samples
[i
].he
.period
);
184 } else if (annotate_browser__opts
.show_nr_samples
) {
185 ui_browser__printf(browser
, "%6" PRIu64
" ",
186 al
->samples
[i
].he
.nr_samples
);
188 ui_browser__printf(browser
, "%6.2f ",
189 al
->samples
[i
].percent
);
193 ui_browser__set_percent_color(browser
, 0, current_entry
);
196 ui_browser__write_nstring(browser
, " ", pcnt_width
);
198 ui_browser__printf(browser
, "%*s", pcnt_width
,
199 annotate_browser__opts
.show_total_period
? "Period" :
200 annotate_browser__opts
.show_nr_samples
? "Samples" : "Percent");
203 if (ab
->have_cycles
) {
205 ui_browser__printf(browser
, "%*.2f ", IPC_WIDTH
- 1, al
->ipc
);
206 else if (!show_title
)
207 ui_browser__write_nstring(browser
, " ", IPC_WIDTH
);
209 ui_browser__printf(browser
, "%*s ", IPC_WIDTH
- 1, "IPC");
212 ui_browser__printf(browser
, "%*" PRIu64
" ",
213 CYCLES_WIDTH
- 1, al
->cycles
);
214 else if (!show_title
)
215 ui_browser__write_nstring(browser
, " ", CYCLES_WIDTH
);
217 ui_browser__printf(browser
, "%*s ", CYCLES_WIDTH
- 1, "Cycle");
220 SLsmg_write_char(' ');
222 /* The scroll bar isn't being used */
223 if (!browser
->navkeypressed
)
227 ui_browser__write_nstring(browser
, " ", width
- pcnt_width
- cycles_width
);
228 else if (al
->offset
== -1) {
229 if (al
->line_nr
&& annotate_browser__opts
.show_linenr
)
230 printed
= scnprintf(bf
, sizeof(bf
), "%-*d ",
231 ab
->addr_width
+ 1, al
->line_nr
);
233 printed
= scnprintf(bf
, sizeof(bf
), "%*s ",
234 ab
->addr_width
, " ");
235 ui_browser__write_nstring(browser
, bf
, printed
);
236 ui_browser__write_nstring(browser
, al
->line
, width
- printed
- pcnt_width
- cycles_width
+ 1);
238 u64 addr
= al
->offset
;
241 if (!annotate_browser__opts
.use_offset
)
244 if (!annotate_browser__opts
.use_offset
) {
245 printed
= scnprintf(bf
, sizeof(bf
), "%" PRIx64
": ", addr
);
247 if (bl
->jump_sources
) {
248 if (annotate_browser__opts
.show_nr_jumps
) {
250 printed
= scnprintf(bf
, sizeof(bf
), "%*d ",
253 prev
= annotate_browser__set_jumps_percent_color(ab
, bl
->jump_sources
,
255 ui_browser__write_nstring(browser
, bf
, printed
);
256 ui_browser__set_color(browser
, prev
);
259 printed
= scnprintf(bf
, sizeof(bf
), "%*" PRIx64
": ",
260 ab
->target_width
, addr
);
262 printed
= scnprintf(bf
, sizeof(bf
), "%*s ",
263 ab
->addr_width
, " ");
268 color
= ui_browser__set_color(browser
, HE_COLORSET_ADDR
);
269 ui_browser__write_nstring(browser
, bf
, printed
);
271 ui_browser__set_color(browser
, color
);
273 disasm_line__write(disasm_line(al
), browser
, bf
, sizeof(bf
));
275 ui_browser__write_nstring(browser
, bf
, width
- pcnt_width
- cycles_width
- 3 - printed
);
282 static bool disasm_line__is_valid_jump(struct disasm_line
*dl
, struct symbol
*sym
)
284 if (!dl
|| !dl
->ins
.ops
|| !ins__is_jump(&dl
->ins
)
285 || !disasm_line__has_offset(dl
)
286 || dl
->ops
.target
.offset
< 0
287 || dl
->ops
.target
.offset
>= (s64
)symbol__size(sym
))
293 static bool is_fused(struct annotate_browser
*ab
, struct disasm_line
*cursor
)
295 struct disasm_line
*pos
= list_prev_entry(cursor
, al
.node
);
301 if (ins__is_lock(&pos
->ins
))
302 name
= pos
->ops
.locked
.ins
.name
;
304 name
= pos
->ins
.name
;
306 if (!name
|| !cursor
->ins
.name
)
309 return ins__is_fused(ab
->arch
, name
, cursor
->ins
.name
);
312 static void annotate_browser__draw_current_jump(struct ui_browser
*browser
)
314 struct annotate_browser
*ab
= container_of(browser
, struct annotate_browser
, b
);
315 struct disasm_line
*cursor
= disasm_line(ab
->selection
);
316 struct annotation_line
*target
;
317 struct browser_line
*btarget
, *bcursor
;
318 unsigned int from
, to
;
319 struct map_symbol
*ms
= ab
->b
.priv
;
320 struct symbol
*sym
= ms
->sym
;
321 u8 pcnt_width
= annotate_browser__pcnt_width(ab
);
323 /* PLT symbols contain external offsets */
324 if (strstr(sym
->name
, "@plt"))
327 if (!disasm_line__is_valid_jump(cursor
, sym
))
330 target
= ab
->offsets
[cursor
->ops
.target
.offset
];
332 bcursor
= browser_line(&cursor
->al
);
333 btarget
= browser_line(target
);
335 if (annotate_browser__opts
.hide_src_code
) {
336 from
= bcursor
->idx_asm
;
337 to
= btarget
->idx_asm
;
339 from
= (u64
)bcursor
->idx
;
340 to
= (u64
)btarget
->idx
;
343 ui_browser__set_color(browser
, HE_COLORSET_JUMP_ARROWS
);
344 __ui_browser__line_arrow(browser
, pcnt_width
+ 2 + ab
->addr_width
,
347 if (is_fused(ab
, cursor
)) {
348 ui_browser__mark_fused(browser
,
349 pcnt_width
+ 3 + ab
->addr_width
,
351 to
> from
? true : false);
355 static unsigned int annotate_browser__refresh(struct ui_browser
*browser
)
357 struct annotate_browser
*ab
= container_of(browser
, struct annotate_browser
, b
);
358 int ret
= ui_browser__list_head_refresh(browser
);
359 int pcnt_width
= annotate_browser__pcnt_width(ab
);
361 if (annotate_browser__opts
.jump_arrows
)
362 annotate_browser__draw_current_jump(browser
);
364 ui_browser__set_color(browser
, HE_COLORSET_NORMAL
);
365 __ui_browser__vline(browser
, pcnt_width
, 0, browser
->height
- 1);
369 static int disasm__cmp(struct annotation_line
*a
, struct annotation_line
*b
)
373 for (i
= 0; i
< a
->samples_nr
; i
++) {
374 if (a
->samples
[i
].percent
== b
->samples
[i
].percent
)
376 return a
->samples
[i
].percent
< b
->samples
[i
].percent
;
381 static void disasm_rb_tree__insert(struct rb_root
*root
, struct annotation_line
*al
)
383 struct rb_node
**p
= &root
->rb_node
;
384 struct rb_node
*parent
= NULL
;
385 struct annotation_line
*l
;
389 l
= rb_entry(parent
, struct annotation_line
, rb_node
);
391 if (disasm__cmp(al
, l
))
396 rb_link_node(&al
->rb_node
, parent
, p
);
397 rb_insert_color(&al
->rb_node
, root
);
400 static void annotate_browser__set_top(struct annotate_browser
*browser
,
401 struct annotation_line
*pos
, u32 idx
)
405 ui_browser__refresh_dimensions(&browser
->b
);
406 back
= browser
->b
.height
/ 2;
407 browser
->b
.top_idx
= browser
->b
.index
= idx
;
409 while (browser
->b
.top_idx
!= 0 && back
!= 0) {
410 pos
= list_entry(pos
->node
.prev
, struct annotation_line
, node
);
412 if (disasm_line__filter(&browser
->b
, &pos
->node
))
415 --browser
->b
.top_idx
;
419 browser
->b
.top
= pos
;
420 browser
->b
.navkeypressed
= true;
423 static void annotate_browser__set_rb_top(struct annotate_browser
*browser
,
426 struct browser_line
*bpos
;
427 struct annotation_line
*pos
;
430 pos
= rb_entry(nd
, struct annotation_line
, rb_node
);
431 bpos
= browser_line(pos
);
434 if (annotate_browser__opts
.hide_src_code
)
436 annotate_browser__set_top(browser
, pos
, idx
);
437 browser
->curr_hot
= nd
;
440 static void annotate_browser__calc_percent(struct annotate_browser
*browser
,
441 struct perf_evsel
*evsel
)
443 struct map_symbol
*ms
= browser
->b
.priv
;
444 struct symbol
*sym
= ms
->sym
;
445 struct annotation
*notes
= symbol__annotation(sym
);
446 struct disasm_line
*pos
;
448 browser
->entries
= RB_ROOT
;
450 pthread_mutex_lock(¬es
->lock
);
452 symbol__calc_percent(sym
, evsel
);
454 list_for_each_entry(pos
, ¬es
->src
->source
, al
.node
) {
455 double max_percent
= 0.0;
458 if (pos
->al
.offset
== -1) {
459 RB_CLEAR_NODE(&pos
->al
.rb_node
);
463 for (i
= 0; i
< pos
->al
.samples_nr
; i
++) {
464 struct annotation_data
*sample
= &pos
->al
.samples
[i
];
466 if (max_percent
< sample
->percent
)
467 max_percent
= sample
->percent
;
470 if (max_percent
< 0.01 && pos
->al
.ipc
== 0) {
471 RB_CLEAR_NODE(&pos
->al
.rb_node
);
474 disasm_rb_tree__insert(&browser
->entries
, &pos
->al
);
476 pthread_mutex_unlock(¬es
->lock
);
478 browser
->curr_hot
= rb_last(&browser
->entries
);
481 static bool annotate_browser__toggle_source(struct annotate_browser
*browser
)
483 struct annotation_line
*al
;
484 struct browser_line
*bl
;
485 off_t offset
= browser
->b
.index
- browser
->b
.top_idx
;
487 browser
->b
.seek(&browser
->b
, offset
, SEEK_CUR
);
488 al
= list_entry(browser
->b
.top
, struct annotation_line
, node
);
489 bl
= browser_line(al
);
491 if (annotate_browser__opts
.hide_src_code
) {
492 if (bl
->idx_asm
< offset
)
495 browser
->b
.nr_entries
= browser
->nr_entries
;
496 annotate_browser__opts
.hide_src_code
= false;
497 browser
->b
.seek(&browser
->b
, -offset
, SEEK_CUR
);
498 browser
->b
.top_idx
= bl
->idx
- offset
;
499 browser
->b
.index
= bl
->idx
;
501 if (bl
->idx_asm
< 0) {
502 ui_helpline__puts("Only available for assembly lines.");
503 browser
->b
.seek(&browser
->b
, -offset
, SEEK_CUR
);
507 if (bl
->idx_asm
< offset
)
508 offset
= bl
->idx_asm
;
510 browser
->b
.nr_entries
= browser
->nr_asm_entries
;
511 annotate_browser__opts
.hide_src_code
= true;
512 browser
->b
.seek(&browser
->b
, -offset
, SEEK_CUR
);
513 browser
->b
.top_idx
= bl
->idx_asm
- offset
;
514 browser
->b
.index
= bl
->idx_asm
;
520 static void annotate_browser__init_asm_mode(struct annotate_browser
*browser
)
522 ui_browser__reset_index(&browser
->b
);
523 browser
->b
.nr_entries
= browser
->nr_asm_entries
;
526 #define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
528 static int sym_title(struct symbol
*sym
, struct map
*map
, char *title
,
531 return snprintf(title
, sz
, "%s %s", sym
->name
, map
->dso
->long_name
);
534 static bool annotate_browser__callq(struct annotate_browser
*browser
,
535 struct perf_evsel
*evsel
,
536 struct hist_browser_timer
*hbt
)
538 struct map_symbol
*ms
= browser
->b
.priv
;
539 struct disasm_line
*dl
= disasm_line(browser
->selection
);
540 struct annotation
*notes
;
541 struct addr_map_symbol target
= {
543 .addr
= map__objdump_2mem(ms
->map
, dl
->ops
.target
.addr
),
545 char title
[SYM_TITLE_MAX_SIZE
];
547 if (!ins__is_call(&dl
->ins
))
550 if (map_groups__find_ams(&target
) ||
551 map__rip_2objdump(target
.map
, target
.map
->map_ip(target
.map
,
553 dl
->ops
.target
.addr
) {
554 ui_helpline__puts("The called function was not found.");
558 notes
= symbol__annotation(target
.sym
);
559 pthread_mutex_lock(¬es
->lock
);
561 if (notes
->src
== NULL
&& symbol__alloc_hist(target
.sym
) < 0) {
562 pthread_mutex_unlock(¬es
->lock
);
563 ui__warning("Not enough memory for annotating '%s' symbol!\n",
568 pthread_mutex_unlock(¬es
->lock
);
569 symbol__tui_annotate(target
.sym
, target
.map
, evsel
, hbt
);
570 sym_title(ms
->sym
, ms
->map
, title
, sizeof(title
));
571 ui_browser__show_title(&browser
->b
, title
);
576 struct disasm_line
*annotate_browser__find_offset(struct annotate_browser
*browser
,
577 s64 offset
, s64
*idx
)
579 struct map_symbol
*ms
= browser
->b
.priv
;
580 struct symbol
*sym
= ms
->sym
;
581 struct annotation
*notes
= symbol__annotation(sym
);
582 struct disasm_line
*pos
;
585 list_for_each_entry(pos
, ¬es
->src
->source
, al
.node
) {
586 if (pos
->al
.offset
== offset
)
588 if (!disasm_line__filter(&browser
->b
, &pos
->al
.node
))
595 static bool annotate_browser__jump(struct annotate_browser
*browser
)
597 struct disasm_line
*dl
= disasm_line(browser
->selection
);
601 if (!ins__is_jump(&dl
->ins
))
604 offset
= dl
->ops
.target
.offset
;
605 dl
= annotate_browser__find_offset(browser
, offset
, &idx
);
607 ui_helpline__printf("Invalid jump offset: %" PRIx64
, offset
);
611 annotate_browser__set_top(browser
, &dl
->al
, idx
);
617 struct annotation_line
*annotate_browser__find_string(struct annotate_browser
*browser
,
620 struct map_symbol
*ms
= browser
->b
.priv
;
621 struct symbol
*sym
= ms
->sym
;
622 struct annotation
*notes
= symbol__annotation(sym
);
623 struct annotation_line
*al
= browser
->selection
;
625 *idx
= browser
->b
.index
;
626 list_for_each_entry_continue(al
, ¬es
->src
->source
, node
) {
627 if (disasm_line__filter(&browser
->b
, &al
->node
))
632 if (al
->line
&& strstr(al
->line
, s
) != NULL
)
639 static bool __annotate_browser__search(struct annotate_browser
*browser
)
641 struct annotation_line
*al
;
644 al
= annotate_browser__find_string(browser
, browser
->search_bf
, &idx
);
646 ui_helpline__puts("String not found!");
650 annotate_browser__set_top(browser
, al
, idx
);
651 browser
->searching_backwards
= false;
656 struct annotation_line
*annotate_browser__find_string_reverse(struct annotate_browser
*browser
,
659 struct map_symbol
*ms
= browser
->b
.priv
;
660 struct symbol
*sym
= ms
->sym
;
661 struct annotation
*notes
= symbol__annotation(sym
);
662 struct annotation_line
*al
= browser
->selection
;
664 *idx
= browser
->b
.index
;
665 list_for_each_entry_continue_reverse(al
, ¬es
->src
->source
, node
) {
666 if (disasm_line__filter(&browser
->b
, &al
->node
))
671 if (al
->line
&& strstr(al
->line
, s
) != NULL
)
678 static bool __annotate_browser__search_reverse(struct annotate_browser
*browser
)
680 struct annotation_line
*al
;
683 al
= annotate_browser__find_string_reverse(browser
, browser
->search_bf
, &idx
);
685 ui_helpline__puts("String not found!");
689 annotate_browser__set_top(browser
, al
, idx
);
690 browser
->searching_backwards
= true;
694 static bool annotate_browser__search_window(struct annotate_browser
*browser
,
697 if (ui_browser__input_window("Search", "String: ", browser
->search_bf
,
698 "ENTER: OK, ESC: Cancel",
699 delay_secs
* 2) != K_ENTER
||
700 !*browser
->search_bf
)
706 static bool annotate_browser__search(struct annotate_browser
*browser
, int delay_secs
)
708 if (annotate_browser__search_window(browser
, delay_secs
))
709 return __annotate_browser__search(browser
);
714 static bool annotate_browser__continue_search(struct annotate_browser
*browser
,
717 if (!*browser
->search_bf
)
718 return annotate_browser__search(browser
, delay_secs
);
720 return __annotate_browser__search(browser
);
723 static bool annotate_browser__search_reverse(struct annotate_browser
*browser
,
726 if (annotate_browser__search_window(browser
, delay_secs
))
727 return __annotate_browser__search_reverse(browser
);
733 bool annotate_browser__continue_search_reverse(struct annotate_browser
*browser
,
736 if (!*browser
->search_bf
)
737 return annotate_browser__search_reverse(browser
, delay_secs
);
739 return __annotate_browser__search_reverse(browser
);
742 static void annotate_browser__update_addr_width(struct annotate_browser
*browser
)
744 if (annotate_browser__opts
.use_offset
)
745 browser
->target_width
= browser
->min_addr_width
;
747 browser
->target_width
= browser
->max_addr_width
;
749 browser
->addr_width
= browser
->target_width
;
751 if (annotate_browser__opts
.show_nr_jumps
)
752 browser
->addr_width
+= browser
->jumps_width
+ 1;
755 static int annotate_browser__run(struct annotate_browser
*browser
,
756 struct perf_evsel
*evsel
,
757 struct hist_browser_timer
*hbt
)
759 struct rb_node
*nd
= NULL
;
760 struct map_symbol
*ms
= browser
->b
.priv
;
761 struct symbol
*sym
= ms
->sym
;
762 const char *help
= "Press 'h' for help on key bindings";
763 int delay_secs
= hbt
? hbt
->refresh
: 0;
765 char title
[SYM_TITLE_MAX_SIZE
];
767 sym_title(sym
, ms
->map
, title
, sizeof(title
));
768 if (ui_browser__show(&browser
->b
, title
, help
) < 0)
771 annotate_browser__calc_percent(browser
, evsel
);
773 if (browser
->curr_hot
) {
774 annotate_browser__set_rb_top(browser
, browser
->curr_hot
);
775 browser
->b
.navkeypressed
= false;
778 nd
= browser
->curr_hot
;
781 key
= ui_browser__run(&browser
->b
, delay_secs
);
783 if (delay_secs
!= 0) {
784 annotate_browser__calc_percent(browser
, evsel
);
786 * Current line focus got out of the list of most active
787 * lines, NULL it so that if TAB|UNTAB is pressed, we
788 * move to curr_hot (current hottest line).
790 if (nd
!= NULL
&& RB_EMPTY_NODE(nd
))
797 hbt
->timer(hbt
->arg
);
800 symbol__annotate_decay_histogram(sym
, evsel
->idx
);
806 nd
= rb_last(&browser
->entries
);
808 nd
= browser
->curr_hot
;
814 nd
= rb_first(&browser
->entries
);
816 nd
= browser
->curr_hot
;
820 ui_browser__help_window(&browser
->b
,
822 "PGDN/SPACE Navigate\n"
823 "q/ESC/CTRL+C Exit\n\n"
824 "ENTER Go to target\n"
826 "H Go to hottest instruction\n"
827 "TAB/shift+TAB Cycle thru hottest instructions\n"
828 "j Toggle showing jump to target arrows\n"
829 "J Toggle showing number of jump sources on targets\n"
830 "n Search next string\n"
831 "o Toggle disassembler output/simplified view\n"
832 "s Toggle source code view\n"
833 "t Circulate percent, total period, samples view\n"
835 "k Toggle line numbers\n"
836 "r Run available scripts\n"
837 "? Search string backwards\n");
845 annotate_browser__opts
.show_linenr
=
846 !annotate_browser__opts
.show_linenr
;
849 nd
= browser
->curr_hot
;
852 if (annotate_browser__toggle_source(browser
))
853 ui_helpline__puts(help
);
856 annotate_browser__opts
.use_offset
= !annotate_browser__opts
.use_offset
;
857 annotate_browser__update_addr_width(browser
);
860 annotate_browser__opts
.jump_arrows
= !annotate_browser__opts
.jump_arrows
;
863 annotate_browser__opts
.show_nr_jumps
= !annotate_browser__opts
.show_nr_jumps
;
864 annotate_browser__update_addr_width(browser
);
867 if (annotate_browser__search(browser
, delay_secs
)) {
869 ui_helpline__puts(help
);
873 if (browser
->searching_backwards
?
874 annotate_browser__continue_search_reverse(browser
, delay_secs
) :
875 annotate_browser__continue_search(browser
, delay_secs
))
879 if (annotate_browser__search_reverse(browser
, delay_secs
))
885 ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
886 seq
++, browser
->b
.nr_entries
,
890 browser
->nr_asm_entries
);
896 struct disasm_line
*dl
= disasm_line(browser
->selection
);
898 if (browser
->selection
== NULL
)
899 ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
900 else if (browser
->selection
->offset
== -1)
901 ui_helpline__puts("Actions are only available for assembly lines.");
902 else if (!dl
->ins
.ops
)
904 else if (ins__is_ret(&dl
->ins
))
906 else if (!(annotate_browser__jump(browser
) ||
907 annotate_browser__callq(browser
, evsel
, hbt
))) {
909 ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
914 if (annotate_browser__opts
.show_total_period
) {
915 annotate_browser__opts
.show_total_period
= false;
916 annotate_browser__opts
.show_nr_samples
= true;
917 } else if (annotate_browser__opts
.show_nr_samples
)
918 annotate_browser__opts
.show_nr_samples
= false;
920 annotate_browser__opts
.show_total_period
= true;
921 annotate_browser__update_addr_width(browser
);
933 annotate_browser__set_rb_top(browser
, nd
);
936 ui_browser__hide(&browser
->b
);
940 int map_symbol__tui_annotate(struct map_symbol
*ms
, struct perf_evsel
*evsel
,
941 struct hist_browser_timer
*hbt
)
943 /* Set default value for show_total_period and show_nr_samples */
944 annotate_browser__opts
.show_total_period
=
945 symbol_conf
.show_total_period
;
946 annotate_browser__opts
.show_nr_samples
=
947 symbol_conf
.show_nr_samples
;
949 return symbol__tui_annotate(ms
->sym
, ms
->map
, evsel
, hbt
);
952 int hist_entry__tui_annotate(struct hist_entry
*he
, struct perf_evsel
*evsel
,
953 struct hist_browser_timer
*hbt
)
955 /* reset abort key so that it can get Ctrl-C as a key */
957 SLang_init_tty(0, 0, 0);
959 return map_symbol__tui_annotate(&he
->ms
, evsel
, hbt
);
963 static unsigned count_insn(struct annotate_browser
*browser
, u64 start
, u64 end
)
968 for (offset
= start
; offset
<= end
; offset
++) {
969 if (browser
->offsets
[offset
])
975 static void count_and_fill(struct annotate_browser
*browser
, u64 start
, u64 end
,
981 n_insn
= count_insn(browser
, start
, end
);
982 if (n_insn
&& ch
->num
&& ch
->cycles
) {
983 float ipc
= n_insn
/ ((double)ch
->cycles
/ (double)ch
->num
);
985 /* Hide data when there are too many overlaps. */
986 if (ch
->reset
>= 0x7fff || ch
->reset
>= ch
->num
/ 2)
989 for (offset
= start
; offset
<= end
; offset
++) {
990 struct annotation_line
*al
= browser
->offsets
[offset
];
999 * This should probably be in util/annotate.c to share with the tty
1000 * annotate, but right now we need the per byte offsets arrays,
1001 * which are only here.
1003 static void annotate__compute_ipc(struct annotate_browser
*browser
, size_t size
,
1007 struct annotation
*notes
= symbol__annotation(sym
);
1009 if (!notes
->src
|| !notes
->src
->cycles_hist
)
1012 pthread_mutex_lock(¬es
->lock
);
1013 for (offset
= 0; offset
< size
; ++offset
) {
1014 struct cyc_hist
*ch
;
1016 ch
= ¬es
->src
->cycles_hist
[offset
];
1017 if (ch
&& ch
->cycles
) {
1018 struct annotation_line
*al
;
1021 count_and_fill(browser
, ch
->start
, offset
, ch
);
1022 al
= browser
->offsets
[offset
];
1023 if (al
&& ch
->num_aggr
)
1024 al
->cycles
= ch
->cycles_aggr
/ ch
->num_aggr
;
1025 browser
->have_cycles
= true;
1028 pthread_mutex_unlock(¬es
->lock
);
1031 static void annotate_browser__mark_jump_targets(struct annotate_browser
*browser
,
1035 struct map_symbol
*ms
= browser
->b
.priv
;
1036 struct symbol
*sym
= ms
->sym
;
1038 /* PLT symbols contain external offsets */
1039 if (strstr(sym
->name
, "@plt"))
1042 for (offset
= 0; offset
< size
; ++offset
) {
1043 struct annotation_line
*al
= browser
->offsets
[offset
];
1044 struct disasm_line
*dl
;
1045 struct browser_line
*blt
;
1047 dl
= disasm_line(al
);
1049 if (!disasm_line__is_valid_jump(dl
, sym
))
1052 al
= browser
->offsets
[dl
->ops
.target
.offset
];
1055 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
1056 * have to adjust to the previous offset?
1061 blt
= browser_line(al
);
1062 if (++blt
->jump_sources
> browser
->max_jump_sources
)
1063 browser
->max_jump_sources
= blt
->jump_sources
;
1065 ++browser
->nr_jumps
;
1069 static inline int width_jumps(int n
)
1078 int symbol__tui_annotate(struct symbol
*sym
, struct map
*map
,
1079 struct perf_evsel
*evsel
,
1080 struct hist_browser_timer
*hbt
)
1082 struct annotation_line
*al
;
1083 struct annotation
*notes
;
1085 struct map_symbol ms
= {
1089 struct annotate_browser browser
= {
1091 .refresh
= annotate_browser__refresh
,
1092 .seek
= ui_browser__list_head_seek
,
1093 .write
= annotate_browser__write
,
1094 .filter
= disasm_line__filter
,
1096 .use_navkeypressed
= true,
1105 size
= symbol__size(sym
);
1107 if (map
->dso
->annotate_warned
)
1110 browser
.offsets
= zalloc(size
* sizeof(struct annotation_line
*));
1111 if (browser
.offsets
== NULL
) {
1112 ui__error("Not enough memory!");
1116 if (perf_evsel__is_group_event(evsel
))
1117 nr_pcnt
= evsel
->nr_members
;
1119 err
= symbol__annotate(sym
, map
, evsel
, sizeof(struct browser_line
), &browser
.arch
);
1122 symbol__strerror_disassemble(sym
, map
, err
, msg
, sizeof(msg
));
1123 ui__error("Couldn't annotate %s:\n%s", sym
->name
, msg
);
1124 goto out_free_offsets
;
1127 symbol__calc_percent(sym
, evsel
);
1129 ui_helpline__push("Press ESC to exit");
1131 notes
= symbol__annotation(sym
);
1132 browser
.start
= map__rip_2objdump(map
, sym
->start
);
1134 list_for_each_entry(al
, ¬es
->src
->source
, node
) {
1135 struct browser_line
*bpos
;
1136 size_t line_len
= strlen(al
->line
);
1138 if (browser
.b
.width
< line_len
)
1139 browser
.b
.width
= line_len
;
1140 bpos
= browser_line(al
);
1141 bpos
->idx
= browser
.nr_entries
++;
1142 if (al
->offset
!= -1) {
1143 bpos
->idx_asm
= browser
.nr_asm_entries
++;
1145 * FIXME: short term bandaid to cope with assembly
1146 * routines that comes with labels in the same column
1147 * as the address in objdump, sigh.
1149 * E.g. copy_user_generic_unrolled
1151 if (al
->offset
< (s64
)size
)
1152 browser
.offsets
[al
->offset
] = al
;
1157 annotate_browser__mark_jump_targets(&browser
, size
);
1158 annotate__compute_ipc(&browser
, size
, sym
);
1160 browser
.addr_width
= browser
.target_width
= browser
.min_addr_width
= hex_width(size
);
1161 browser
.max_addr_width
= hex_width(sym
->end
);
1162 browser
.jumps_width
= width_jumps(browser
.max_jump_sources
);
1163 browser
.nr_events
= nr_pcnt
;
1164 browser
.b
.nr_entries
= browser
.nr_entries
;
1165 browser
.b
.entries
= ¬es
->src
->source
,
1166 browser
.b
.width
+= 18; /* Percentage */
1168 if (annotate_browser__opts
.hide_src_code
)
1169 annotate_browser__init_asm_mode(&browser
);
1171 annotate_browser__update_addr_width(&browser
);
1173 ret
= annotate_browser__run(&browser
, evsel
, hbt
);
1175 annotated_source__purge(notes
->src
);
1178 free(browser
.offsets
);
1182 #define ANNOTATE_CFG(n) \
1183 { .name = #n, .value = &annotate_browser__opts.n, }
1186 * Keep the entries sorted, they are bsearch'ed
1188 static struct annotate_config
{
1191 } annotate__configs
[] = {
1192 ANNOTATE_CFG(hide_src_code
),
1193 ANNOTATE_CFG(jump_arrows
),
1194 ANNOTATE_CFG(show_linenr
),
1195 ANNOTATE_CFG(show_nr_jumps
),
1196 ANNOTATE_CFG(show_nr_samples
),
1197 ANNOTATE_CFG(show_total_period
),
1198 ANNOTATE_CFG(use_offset
),
1203 static int annotate_config__cmp(const void *name
, const void *cfgp
)
1205 const struct annotate_config
*cfg
= cfgp
;
1207 return strcmp(name
, cfg
->name
);
1210 static int annotate__config(const char *var
, const char *value
,
1211 void *data __maybe_unused
)
1213 struct annotate_config
*cfg
;
1216 if (!strstarts(var
, "annotate."))
1220 cfg
= bsearch(name
, annotate__configs
, ARRAY_SIZE(annotate__configs
),
1221 sizeof(struct annotate_config
), annotate_config__cmp
);
1224 ui__warning("%s variable unknown, ignoring...", var
);
1226 *cfg
->value
= perf_config_bool(name
, value
);
1230 void annotate_browser__init(void)
1232 perf_config(annotate__config
, NULL
);