1 #include "../../util/util.h"
2 #include "../browser.h"
3 #include "../helpline.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"
14 #include <linux/kernel.h>
16 struct disasm_line_samples
{
22 #define CYCLES_WIDTH 6
24 struct browser_disasm_line
{
25 struct rb_node rb_node
;
30 * actual length of this array is saved on the nr_events field
31 * of the struct annotate_browser
33 struct disasm_line_samples samples
[1];
36 static struct annotate_browser_opt
{
43 } annotate_browser__opts
= {
48 struct annotate_browser
{
50 struct rb_root entries
;
51 struct rb_node
*curr_hot
;
52 struct disasm_line
*selection
;
53 struct disasm_line
**offsets
;
60 bool searching_backwards
;
70 static inline struct browser_disasm_line
*disasm_line__browser(struct disasm_line
*dl
)
72 return (struct browser_disasm_line
*)(dl
+ 1);
75 static bool disasm_line__filter(struct ui_browser
*browser __maybe_unused
,
78 if (annotate_browser__opts
.hide_src_code
) {
79 struct disasm_line
*dl
= list_entry(entry
, struct disasm_line
, node
);
80 return dl
->offset
== -1;
86 static int annotate_browser__jumps_percent_color(struct annotate_browser
*browser
,
89 if (current
&& (!browser
->b
.use_navkeypressed
|| browser
->b
.navkeypressed
))
90 return HE_COLORSET_SELECTED
;
91 if (nr
== browser
->max_jump_sources
)
92 return HE_COLORSET_TOP
;
94 return HE_COLORSET_MEDIUM
;
95 return HE_COLORSET_NORMAL
;
98 static int annotate_browser__set_jumps_percent_color(struct annotate_browser
*browser
,
101 int color
= annotate_browser__jumps_percent_color(browser
, nr
, current
);
102 return ui_browser__set_color(&browser
->b
, color
);
105 static int annotate_browser__pcnt_width(struct annotate_browser
*ab
)
107 int w
= 7 * ab
->nr_events
;
110 w
+= IPC_WIDTH
+ CYCLES_WIDTH
;
114 static void annotate_browser__write(struct ui_browser
*browser
, void *entry
, int row
)
116 struct annotate_browser
*ab
= container_of(browser
, struct annotate_browser
, b
);
117 struct disasm_line
*dl
= list_entry(entry
, struct disasm_line
, node
);
118 struct browser_disasm_line
*bdl
= disasm_line__browser(dl
);
119 bool current_entry
= ui_browser__is_current_entry(browser
, row
);
120 bool change_color
= (!annotate_browser__opts
.hide_src_code
&&
121 (!current_entry
|| (browser
->use_navkeypressed
&&
122 !browser
->navkeypressed
)));
123 int width
= browser
->width
, printed
;
124 int i
, pcnt_width
= annotate_browser__pcnt_width(ab
);
125 double percent_max
= 0.0;
128 for (i
= 0; i
< ab
->nr_events
; i
++) {
129 if (bdl
->samples
[i
].percent
> percent_max
)
130 percent_max
= bdl
->samples
[i
].percent
;
133 if (dl
->offset
!= -1 && percent_max
!= 0.0) {
134 if (percent_max
!= 0.0) {
135 for (i
= 0; i
< ab
->nr_events
; i
++) {
136 ui_browser__set_percent_color(browser
,
137 bdl
->samples
[i
].percent
,
139 if (annotate_browser__opts
.show_total_period
) {
140 ui_browser__printf(browser
, "%6" PRIu64
" ",
143 ui_browser__printf(browser
, "%6.2f ",
144 bdl
->samples
[i
].percent
);
148 ui_browser__write_nstring(browser
, " ", 7 * ab
->nr_events
);
151 ui_browser__set_percent_color(browser
, 0, current_entry
);
152 ui_browser__write_nstring(browser
, " ", 7 * ab
->nr_events
);
154 if (ab
->have_cycles
) {
156 ui_browser__printf(browser
, "%*.2f ", IPC_WIDTH
- 1, dl
->ipc
);
158 ui_browser__write_nstring(browser
, " ", IPC_WIDTH
);
160 ui_browser__printf(browser
, "%*" PRIu64
" ",
161 CYCLES_WIDTH
- 1, dl
->cycles
);
163 ui_browser__write_nstring(browser
, " ", CYCLES_WIDTH
);
166 SLsmg_write_char(' ');
168 /* The scroll bar isn't being used */
169 if (!browser
->navkeypressed
)
173 ui_browser__write_nstring(browser
, " ", width
- pcnt_width
);
174 else if (dl
->offset
== -1) {
175 if (dl
->line_nr
&& annotate_browser__opts
.show_linenr
)
176 printed
= scnprintf(bf
, sizeof(bf
), "%-*d ",
177 ab
->addr_width
+ 1, dl
->line_nr
);
179 printed
= scnprintf(bf
, sizeof(bf
), "%*s ",
180 ab
->addr_width
, " ");
181 ui_browser__write_nstring(browser
, bf
, printed
);
182 ui_browser__write_nstring(browser
, dl
->line
, width
- printed
- pcnt_width
+ 1);
184 u64 addr
= dl
->offset
;
187 if (!annotate_browser__opts
.use_offset
)
190 if (!annotate_browser__opts
.use_offset
) {
191 printed
= scnprintf(bf
, sizeof(bf
), "%" PRIx64
": ", addr
);
193 if (bdl
->jump_sources
) {
194 if (annotate_browser__opts
.show_nr_jumps
) {
196 printed
= scnprintf(bf
, sizeof(bf
), "%*d ",
199 prev
= annotate_browser__set_jumps_percent_color(ab
, bdl
->jump_sources
,
201 ui_browser__write_nstring(browser
, bf
, printed
);
202 ui_browser__set_color(browser
, prev
);
205 printed
= scnprintf(bf
, sizeof(bf
), "%*" PRIx64
": ",
206 ab
->target_width
, addr
);
208 printed
= scnprintf(bf
, sizeof(bf
), "%*s ",
209 ab
->addr_width
, " ");
214 color
= ui_browser__set_color(browser
, HE_COLORSET_ADDR
);
215 ui_browser__write_nstring(browser
, bf
, printed
);
217 ui_browser__set_color(browser
, color
);
218 if (dl
->ins
.ops
&& dl
->ins
.ops
->scnprintf
) {
219 if (ins__is_jump(&dl
->ins
)) {
220 bool fwd
= dl
->ops
.target
.offset
> dl
->offset
;
222 ui_browser__write_graph(browser
, fwd
? SLSMG_DARROW_CHAR
:
224 SLsmg_write_char(' ');
225 } else if (ins__is_call(&dl
->ins
)) {
226 ui_browser__write_graph(browser
, SLSMG_RARROW_CHAR
);
227 SLsmg_write_char(' ');
228 } else if (ins__is_ret(&dl
->ins
)) {
229 ui_browser__write_graph(browser
, SLSMG_LARROW_CHAR
);
230 SLsmg_write_char(' ');
232 ui_browser__write_nstring(browser
, " ", 2);
235 ui_browser__write_nstring(browser
, " ", 2);
238 disasm_line__scnprintf(dl
, bf
, sizeof(bf
), !annotate_browser__opts
.use_offset
);
239 ui_browser__write_nstring(browser
, bf
, width
- pcnt_width
- 3 - printed
);
246 static bool disasm_line__is_valid_jump(struct disasm_line
*dl
, struct symbol
*sym
)
248 if (!dl
|| !dl
->ins
.ops
|| !ins__is_jump(&dl
->ins
)
249 || !disasm_line__has_offset(dl
)
250 || dl
->ops
.target
.offset
< 0
251 || dl
->ops
.target
.offset
>= (s64
)symbol__size(sym
))
257 static void annotate_browser__draw_current_jump(struct ui_browser
*browser
)
259 struct annotate_browser
*ab
= container_of(browser
, struct annotate_browser
, b
);
260 struct disasm_line
*cursor
= ab
->selection
, *target
;
261 struct browser_disasm_line
*btarget
, *bcursor
;
262 unsigned int from
, to
;
263 struct map_symbol
*ms
= ab
->b
.priv
;
264 struct symbol
*sym
= ms
->sym
;
265 u8 pcnt_width
= annotate_browser__pcnt_width(ab
);
267 /* PLT symbols contain external offsets */
268 if (strstr(sym
->name
, "@plt"))
271 if (!disasm_line__is_valid_jump(cursor
, sym
))
274 target
= ab
->offsets
[cursor
->ops
.target
.offset
];
278 bcursor
= disasm_line__browser(cursor
);
279 btarget
= disasm_line__browser(target
);
281 if (annotate_browser__opts
.hide_src_code
) {
282 from
= bcursor
->idx_asm
;
283 to
= btarget
->idx_asm
;
285 from
= (u64
)bcursor
->idx
;
286 to
= (u64
)btarget
->idx
;
289 ui_browser__set_color(browser
, HE_COLORSET_JUMP_ARROWS
);
290 __ui_browser__line_arrow(browser
, pcnt_width
+ 2 + ab
->addr_width
,
294 static unsigned int annotate_browser__refresh(struct ui_browser
*browser
)
296 struct annotate_browser
*ab
= container_of(browser
, struct annotate_browser
, b
);
297 int ret
= ui_browser__list_head_refresh(browser
);
298 int pcnt_width
= annotate_browser__pcnt_width(ab
);
300 if (annotate_browser__opts
.jump_arrows
)
301 annotate_browser__draw_current_jump(browser
);
303 ui_browser__set_color(browser
, HE_COLORSET_NORMAL
);
304 __ui_browser__vline(browser
, pcnt_width
, 0, browser
->height
- 1);
308 static int disasm__cmp(struct browser_disasm_line
*a
,
309 struct browser_disasm_line
*b
, int nr_pcnt
)
313 for (i
= 0; i
< nr_pcnt
; i
++) {
314 if (a
->samples
[i
].percent
== b
->samples
[i
].percent
)
316 return a
->samples
[i
].percent
< b
->samples
[i
].percent
;
321 static void disasm_rb_tree__insert(struct rb_root
*root
, struct browser_disasm_line
*bdl
,
324 struct rb_node
**p
= &root
->rb_node
;
325 struct rb_node
*parent
= NULL
;
326 struct browser_disasm_line
*l
;
330 l
= rb_entry(parent
, struct browser_disasm_line
, rb_node
);
332 if (disasm__cmp(bdl
, l
, nr_events
))
337 rb_link_node(&bdl
->rb_node
, parent
, p
);
338 rb_insert_color(&bdl
->rb_node
, root
);
341 static void annotate_browser__set_top(struct annotate_browser
*browser
,
342 struct disasm_line
*pos
, u32 idx
)
346 ui_browser__refresh_dimensions(&browser
->b
);
347 back
= browser
->b
.height
/ 2;
348 browser
->b
.top_idx
= browser
->b
.index
= idx
;
350 while (browser
->b
.top_idx
!= 0 && back
!= 0) {
351 pos
= list_entry(pos
->node
.prev
, struct disasm_line
, node
);
353 if (disasm_line__filter(&browser
->b
, &pos
->node
))
356 --browser
->b
.top_idx
;
360 browser
->b
.top
= pos
;
361 browser
->b
.navkeypressed
= true;
364 static void annotate_browser__set_rb_top(struct annotate_browser
*browser
,
367 struct browser_disasm_line
*bpos
;
368 struct disasm_line
*pos
;
371 bpos
= rb_entry(nd
, struct browser_disasm_line
, rb_node
);
372 pos
= ((struct disasm_line
*)bpos
) - 1;
374 if (annotate_browser__opts
.hide_src_code
)
376 annotate_browser__set_top(browser
, pos
, idx
);
377 browser
->curr_hot
= nd
;
380 static void annotate_browser__calc_percent(struct annotate_browser
*browser
,
381 struct perf_evsel
*evsel
)
383 struct map_symbol
*ms
= browser
->b
.priv
;
384 struct symbol
*sym
= ms
->sym
;
385 struct annotation
*notes
= symbol__annotation(sym
);
386 struct disasm_line
*pos
, *next
;
387 s64 len
= symbol__size(sym
);
389 browser
->entries
= RB_ROOT
;
391 pthread_mutex_lock(¬es
->lock
);
393 list_for_each_entry(pos
, ¬es
->src
->source
, node
) {
394 struct browser_disasm_line
*bpos
= disasm_line__browser(pos
);
395 const char *path
= NULL
;
396 double max_percent
= 0.0;
399 if (pos
->offset
== -1) {
400 RB_CLEAR_NODE(&bpos
->rb_node
);
404 next
= disasm__get_next_ip_line(¬es
->src
->source
, pos
);
406 for (i
= 0; i
< browser
->nr_events
; i
++) {
409 bpos
->samples
[i
].percent
= disasm__calc_percent(notes
,
412 next
? next
->offset
: len
,
414 bpos
->samples
[i
].nr
= nr_samples
;
416 if (max_percent
< bpos
->samples
[i
].percent
)
417 max_percent
= bpos
->samples
[i
].percent
;
420 if (max_percent
< 0.01 && pos
->ipc
== 0) {
421 RB_CLEAR_NODE(&bpos
->rb_node
);
424 disasm_rb_tree__insert(&browser
->entries
, bpos
,
427 pthread_mutex_unlock(¬es
->lock
);
429 browser
->curr_hot
= rb_last(&browser
->entries
);
432 static bool annotate_browser__toggle_source(struct annotate_browser
*browser
)
434 struct disasm_line
*dl
;
435 struct browser_disasm_line
*bdl
;
436 off_t offset
= browser
->b
.index
- browser
->b
.top_idx
;
438 browser
->b
.seek(&browser
->b
, offset
, SEEK_CUR
);
439 dl
= list_entry(browser
->b
.top
, struct disasm_line
, node
);
440 bdl
= disasm_line__browser(dl
);
442 if (annotate_browser__opts
.hide_src_code
) {
443 if (bdl
->idx_asm
< offset
)
446 browser
->b
.nr_entries
= browser
->nr_entries
;
447 annotate_browser__opts
.hide_src_code
= false;
448 browser
->b
.seek(&browser
->b
, -offset
, SEEK_CUR
);
449 browser
->b
.top_idx
= bdl
->idx
- offset
;
450 browser
->b
.index
= bdl
->idx
;
452 if (bdl
->idx_asm
< 0) {
453 ui_helpline__puts("Only available for assembly lines.");
454 browser
->b
.seek(&browser
->b
, -offset
, SEEK_CUR
);
458 if (bdl
->idx_asm
< offset
)
459 offset
= bdl
->idx_asm
;
461 browser
->b
.nr_entries
= browser
->nr_asm_entries
;
462 annotate_browser__opts
.hide_src_code
= true;
463 browser
->b
.seek(&browser
->b
, -offset
, SEEK_CUR
);
464 browser
->b
.top_idx
= bdl
->idx_asm
- offset
;
465 browser
->b
.index
= bdl
->idx_asm
;
471 static void annotate_browser__init_asm_mode(struct annotate_browser
*browser
)
473 ui_browser__reset_index(&browser
->b
);
474 browser
->b
.nr_entries
= browser
->nr_asm_entries
;
477 #define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
479 static int sym_title(struct symbol
*sym
, struct map
*map
, char *title
,
482 return snprintf(title
, sz
, "%s %s", sym
->name
, map
->dso
->long_name
);
485 static bool annotate_browser__callq(struct annotate_browser
*browser
,
486 struct perf_evsel
*evsel
,
487 struct hist_browser_timer
*hbt
)
489 struct map_symbol
*ms
= browser
->b
.priv
;
490 struct disasm_line
*dl
= browser
->selection
;
491 struct annotation
*notes
;
492 struct addr_map_symbol target
= {
494 .addr
= map__objdump_2mem(ms
->map
, dl
->ops
.target
.addr
),
496 char title
[SYM_TITLE_MAX_SIZE
];
498 if (!ins__is_call(&dl
->ins
))
501 if (map_groups__find_ams(&target
) ||
502 map__rip_2objdump(target
.map
, target
.map
->map_ip(target
.map
,
504 dl
->ops
.target
.addr
) {
505 ui_helpline__puts("The called function was not found.");
509 notes
= symbol__annotation(target
.sym
);
510 pthread_mutex_lock(¬es
->lock
);
512 if (notes
->src
== NULL
&& symbol__alloc_hist(target
.sym
) < 0) {
513 pthread_mutex_unlock(¬es
->lock
);
514 ui__warning("Not enough memory for annotating '%s' symbol!\n",
519 pthread_mutex_unlock(¬es
->lock
);
520 symbol__tui_annotate(target
.sym
, target
.map
, evsel
, hbt
);
521 sym_title(ms
->sym
, ms
->map
, title
, sizeof(title
));
522 ui_browser__show_title(&browser
->b
, title
);
527 struct disasm_line
*annotate_browser__find_offset(struct annotate_browser
*browser
,
528 s64 offset
, s64
*idx
)
530 struct map_symbol
*ms
= browser
->b
.priv
;
531 struct symbol
*sym
= ms
->sym
;
532 struct annotation
*notes
= symbol__annotation(sym
);
533 struct disasm_line
*pos
;
536 list_for_each_entry(pos
, ¬es
->src
->source
, node
) {
537 if (pos
->offset
== offset
)
539 if (!disasm_line__filter(&browser
->b
, &pos
->node
))
546 static bool annotate_browser__jump(struct annotate_browser
*browser
)
548 struct disasm_line
*dl
= browser
->selection
;
552 if (!ins__is_jump(&dl
->ins
))
555 offset
= dl
->ops
.target
.offset
;
556 dl
= annotate_browser__find_offset(browser
, offset
, &idx
);
558 ui_helpline__printf("Invalid jump offset: %" PRIx64
, offset
);
562 annotate_browser__set_top(browser
, dl
, idx
);
568 struct disasm_line
*annotate_browser__find_string(struct annotate_browser
*browser
,
571 struct map_symbol
*ms
= browser
->b
.priv
;
572 struct symbol
*sym
= ms
->sym
;
573 struct annotation
*notes
= symbol__annotation(sym
);
574 struct disasm_line
*pos
= browser
->selection
;
576 *idx
= browser
->b
.index
;
577 list_for_each_entry_continue(pos
, ¬es
->src
->source
, node
) {
578 if (disasm_line__filter(&browser
->b
, &pos
->node
))
583 if (pos
->line
&& strstr(pos
->line
, s
) != NULL
)
590 static bool __annotate_browser__search(struct annotate_browser
*browser
)
592 struct disasm_line
*dl
;
595 dl
= annotate_browser__find_string(browser
, browser
->search_bf
, &idx
);
597 ui_helpline__puts("String not found!");
601 annotate_browser__set_top(browser
, dl
, idx
);
602 browser
->searching_backwards
= false;
607 struct disasm_line
*annotate_browser__find_string_reverse(struct annotate_browser
*browser
,
610 struct map_symbol
*ms
= browser
->b
.priv
;
611 struct symbol
*sym
= ms
->sym
;
612 struct annotation
*notes
= symbol__annotation(sym
);
613 struct disasm_line
*pos
= browser
->selection
;
615 *idx
= browser
->b
.index
;
616 list_for_each_entry_continue_reverse(pos
, ¬es
->src
->source
, node
) {
617 if (disasm_line__filter(&browser
->b
, &pos
->node
))
622 if (pos
->line
&& strstr(pos
->line
, s
) != NULL
)
629 static bool __annotate_browser__search_reverse(struct annotate_browser
*browser
)
631 struct disasm_line
*dl
;
634 dl
= annotate_browser__find_string_reverse(browser
, browser
->search_bf
, &idx
);
636 ui_helpline__puts("String not found!");
640 annotate_browser__set_top(browser
, dl
, idx
);
641 browser
->searching_backwards
= true;
645 static bool annotate_browser__search_window(struct annotate_browser
*browser
,
648 if (ui_browser__input_window("Search", "String: ", browser
->search_bf
,
649 "ENTER: OK, ESC: Cancel",
650 delay_secs
* 2) != K_ENTER
||
651 !*browser
->search_bf
)
657 static bool annotate_browser__search(struct annotate_browser
*browser
, int delay_secs
)
659 if (annotate_browser__search_window(browser
, delay_secs
))
660 return __annotate_browser__search(browser
);
665 static bool annotate_browser__continue_search(struct annotate_browser
*browser
,
668 if (!*browser
->search_bf
)
669 return annotate_browser__search(browser
, delay_secs
);
671 return __annotate_browser__search(browser
);
674 static bool annotate_browser__search_reverse(struct annotate_browser
*browser
,
677 if (annotate_browser__search_window(browser
, delay_secs
))
678 return __annotate_browser__search_reverse(browser
);
684 bool annotate_browser__continue_search_reverse(struct annotate_browser
*browser
,
687 if (!*browser
->search_bf
)
688 return annotate_browser__search_reverse(browser
, delay_secs
);
690 return __annotate_browser__search_reverse(browser
);
693 static void annotate_browser__update_addr_width(struct annotate_browser
*browser
)
695 if (annotate_browser__opts
.use_offset
)
696 browser
->target_width
= browser
->min_addr_width
;
698 browser
->target_width
= browser
->max_addr_width
;
700 browser
->addr_width
= browser
->target_width
;
702 if (annotate_browser__opts
.show_nr_jumps
)
703 browser
->addr_width
+= browser
->jumps_width
+ 1;
706 static int annotate_browser__run(struct annotate_browser
*browser
,
707 struct perf_evsel
*evsel
,
708 struct hist_browser_timer
*hbt
)
710 struct rb_node
*nd
= NULL
;
711 struct map_symbol
*ms
= browser
->b
.priv
;
712 struct symbol
*sym
= ms
->sym
;
713 const char *help
= "Press 'h' for help on key bindings";
714 int delay_secs
= hbt
? hbt
->refresh
: 0;
716 char title
[SYM_TITLE_MAX_SIZE
];
718 sym_title(sym
, ms
->map
, title
, sizeof(title
));
719 if (ui_browser__show(&browser
->b
, title
, help
) < 0)
722 annotate_browser__calc_percent(browser
, evsel
);
724 if (browser
->curr_hot
) {
725 annotate_browser__set_rb_top(browser
, browser
->curr_hot
);
726 browser
->b
.navkeypressed
= false;
729 nd
= browser
->curr_hot
;
732 key
= ui_browser__run(&browser
->b
, delay_secs
);
734 if (delay_secs
!= 0) {
735 annotate_browser__calc_percent(browser
, evsel
);
737 * Current line focus got out of the list of most active
738 * lines, NULL it so that if TAB|UNTAB is pressed, we
739 * move to curr_hot (current hottest line).
741 if (nd
!= NULL
&& RB_EMPTY_NODE(nd
))
748 hbt
->timer(hbt
->arg
);
751 symbol__annotate_decay_histogram(sym
, evsel
->idx
);
757 nd
= rb_last(&browser
->entries
);
759 nd
= browser
->curr_hot
;
765 nd
= rb_first(&browser
->entries
);
767 nd
= browser
->curr_hot
;
771 ui_browser__help_window(&browser
->b
,
773 "PGDN/SPACE Navigate\n"
774 "q/ESC/CTRL+C Exit\n\n"
775 "ENTER Go to target\n"
777 "H Cycle thru hottest instructions\n"
778 "j Toggle showing jump to target arrows\n"
779 "J Toggle showing number of jump sources on targets\n"
780 "n Search next string\n"
781 "o Toggle disassembler output/simplified view\n"
782 "s Toggle source code view\n"
783 "t Toggle total period view\n"
785 "k Toggle line numbers\n"
786 "r Run available scripts\n"
787 "? Search string backwards\n");
795 annotate_browser__opts
.show_linenr
=
796 !annotate_browser__opts
.show_linenr
;
799 nd
= browser
->curr_hot
;
802 if (annotate_browser__toggle_source(browser
))
803 ui_helpline__puts(help
);
806 annotate_browser__opts
.use_offset
= !annotate_browser__opts
.use_offset
;
807 annotate_browser__update_addr_width(browser
);
810 annotate_browser__opts
.jump_arrows
= !annotate_browser__opts
.jump_arrows
;
813 annotate_browser__opts
.show_nr_jumps
= !annotate_browser__opts
.show_nr_jumps
;
814 annotate_browser__update_addr_width(browser
);
817 if (annotate_browser__search(browser
, delay_secs
)) {
819 ui_helpline__puts(help
);
823 if (browser
->searching_backwards
?
824 annotate_browser__continue_search_reverse(browser
, delay_secs
) :
825 annotate_browser__continue_search(browser
, delay_secs
))
829 if (annotate_browser__search_reverse(browser
, delay_secs
))
835 ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
836 seq
++, browser
->b
.nr_entries
,
840 browser
->nr_asm_entries
);
845 if (browser
->selection
== NULL
)
846 ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
847 else if (browser
->selection
->offset
== -1)
848 ui_helpline__puts("Actions are only available for assembly lines.");
849 else if (!browser
->selection
->ins
.ops
)
851 else if (ins__is_ret(&browser
->selection
->ins
))
853 else if (!(annotate_browser__jump(browser
) ||
854 annotate_browser__callq(browser
, evsel
, hbt
))) {
856 ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
860 annotate_browser__opts
.show_total_period
=
861 !annotate_browser__opts
.show_total_period
;
862 annotate_browser__update_addr_width(browser
);
874 annotate_browser__set_rb_top(browser
, nd
);
877 ui_browser__hide(&browser
->b
);
881 int map_symbol__tui_annotate(struct map_symbol
*ms
, struct perf_evsel
*evsel
,
882 struct hist_browser_timer
*hbt
)
884 /* Set default value for show_total_period. */
885 annotate_browser__opts
.show_total_period
=
886 symbol_conf
.show_total_period
;
888 return symbol__tui_annotate(ms
->sym
, ms
->map
, evsel
, hbt
);
891 int hist_entry__tui_annotate(struct hist_entry
*he
, struct perf_evsel
*evsel
,
892 struct hist_browser_timer
*hbt
)
894 /* reset abort key so that it can get Ctrl-C as a key */
896 SLang_init_tty(0, 0, 0);
898 return map_symbol__tui_annotate(&he
->ms
, evsel
, hbt
);
902 static unsigned count_insn(struct annotate_browser
*browser
, u64 start
, u64 end
)
907 for (offset
= start
; offset
<= end
; offset
++) {
908 if (browser
->offsets
[offset
])
914 static void count_and_fill(struct annotate_browser
*browser
, u64 start
, u64 end
,
920 n_insn
= count_insn(browser
, start
, end
);
921 if (n_insn
&& ch
->num
&& ch
->cycles
) {
922 float ipc
= n_insn
/ ((double)ch
->cycles
/ (double)ch
->num
);
924 /* Hide data when there are too many overlaps. */
925 if (ch
->reset
>= 0x7fff || ch
->reset
>= ch
->num
/ 2)
928 for (offset
= start
; offset
<= end
; offset
++) {
929 struct disasm_line
*dl
= browser
->offsets
[offset
];
938 * This should probably be in util/annotate.c to share with the tty
939 * annotate, but right now we need the per byte offsets arrays,
940 * which are only here.
942 static void annotate__compute_ipc(struct annotate_browser
*browser
, size_t size
,
946 struct annotation
*notes
= symbol__annotation(sym
);
948 if (!notes
->src
|| !notes
->src
->cycles_hist
)
951 pthread_mutex_lock(¬es
->lock
);
952 for (offset
= 0; offset
< size
; ++offset
) {
955 ch
= ¬es
->src
->cycles_hist
[offset
];
956 if (ch
&& ch
->cycles
) {
957 struct disasm_line
*dl
;
960 count_and_fill(browser
, ch
->start
, offset
, ch
);
961 dl
= browser
->offsets
[offset
];
962 if (dl
&& ch
->num_aggr
)
963 dl
->cycles
= ch
->cycles_aggr
/ ch
->num_aggr
;
964 browser
->have_cycles
= true;
967 pthread_mutex_unlock(¬es
->lock
);
970 static void annotate_browser__mark_jump_targets(struct annotate_browser
*browser
,
974 struct map_symbol
*ms
= browser
->b
.priv
;
975 struct symbol
*sym
= ms
->sym
;
977 /* PLT symbols contain external offsets */
978 if (strstr(sym
->name
, "@plt"))
981 for (offset
= 0; offset
< size
; ++offset
) {
982 struct disasm_line
*dl
= browser
->offsets
[offset
], *dlt
;
983 struct browser_disasm_line
*bdlt
;
985 if (!disasm_line__is_valid_jump(dl
, sym
))
988 dlt
= browser
->offsets
[dl
->ops
.target
.offset
];
990 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
991 * have to adjust to the previous offset?
996 bdlt
= disasm_line__browser(dlt
);
997 if (++bdlt
->jump_sources
> browser
->max_jump_sources
)
998 browser
->max_jump_sources
= bdlt
->jump_sources
;
1000 ++browser
->nr_jumps
;
1004 static inline int width_jumps(int n
)
1013 int symbol__tui_annotate(struct symbol
*sym
, struct map
*map
,
1014 struct perf_evsel
*evsel
,
1015 struct hist_browser_timer
*hbt
)
1017 struct disasm_line
*pos
, *n
;
1018 struct annotation
*notes
;
1020 struct map_symbol ms
= {
1024 struct annotate_browser browser
= {
1026 .refresh
= annotate_browser__refresh
,
1027 .seek
= ui_browser__list_head_seek
,
1028 .write
= annotate_browser__write
,
1029 .filter
= disasm_line__filter
,
1031 .use_navkeypressed
= true,
1036 size_t sizeof_bdl
= sizeof(struct browser_disasm_line
);
1041 size
= symbol__size(sym
);
1043 if (map
->dso
->annotate_warned
)
1046 browser
.offsets
= zalloc(size
* sizeof(struct disasm_line
*));
1047 if (browser
.offsets
== NULL
) {
1048 ui__error("Not enough memory!");
1052 if (perf_evsel__is_group_event(evsel
)) {
1053 nr_pcnt
= evsel
->nr_members
;
1054 sizeof_bdl
+= sizeof(struct disasm_line_samples
) *
1058 err
= symbol__disassemble(sym
, map
, perf_evsel__env_arch(evsel
), sizeof_bdl
);
1061 symbol__strerror_disassemble(sym
, map
, err
, msg
, sizeof(msg
));
1062 ui__error("Couldn't annotate %s:\n%s", sym
->name
, msg
);
1063 goto out_free_offsets
;
1066 ui_helpline__push("Press ESC to exit");
1068 notes
= symbol__annotation(sym
);
1069 browser
.start
= map__rip_2objdump(map
, sym
->start
);
1071 list_for_each_entry(pos
, ¬es
->src
->source
, node
) {
1072 struct browser_disasm_line
*bpos
;
1073 size_t line_len
= strlen(pos
->line
);
1075 if (browser
.b
.width
< line_len
)
1076 browser
.b
.width
= line_len
;
1077 bpos
= disasm_line__browser(pos
);
1078 bpos
->idx
= browser
.nr_entries
++;
1079 if (pos
->offset
!= -1) {
1080 bpos
->idx_asm
= browser
.nr_asm_entries
++;
1082 * FIXME: short term bandaid to cope with assembly
1083 * routines that comes with labels in the same column
1084 * as the address in objdump, sigh.
1086 * E.g. copy_user_generic_unrolled
1088 if (pos
->offset
< (s64
)size
)
1089 browser
.offsets
[pos
->offset
] = pos
;
1094 annotate_browser__mark_jump_targets(&browser
, size
);
1095 annotate__compute_ipc(&browser
, size
, sym
);
1097 browser
.addr_width
= browser
.target_width
= browser
.min_addr_width
= hex_width(size
);
1098 browser
.max_addr_width
= hex_width(sym
->end
);
1099 browser
.jumps_width
= width_jumps(browser
.max_jump_sources
);
1100 browser
.nr_events
= nr_pcnt
;
1101 browser
.b
.nr_entries
= browser
.nr_entries
;
1102 browser
.b
.entries
= ¬es
->src
->source
,
1103 browser
.b
.width
+= 18; /* Percentage */
1105 if (annotate_browser__opts
.hide_src_code
)
1106 annotate_browser__init_asm_mode(&browser
);
1108 annotate_browser__update_addr_width(&browser
);
1110 ret
= annotate_browser__run(&browser
, evsel
, hbt
);
1111 list_for_each_entry_safe(pos
, n
, ¬es
->src
->source
, node
) {
1112 list_del(&pos
->node
);
1113 disasm_line__free(pos
);
1117 free(browser
.offsets
);
1121 #define ANNOTATE_CFG(n) \
1122 { .name = #n, .value = &annotate_browser__opts.n, }
1125 * Keep the entries sorted, they are bsearch'ed
1127 static struct annotate_config
{
1130 } annotate__configs
[] = {
1131 ANNOTATE_CFG(hide_src_code
),
1132 ANNOTATE_CFG(jump_arrows
),
1133 ANNOTATE_CFG(show_linenr
),
1134 ANNOTATE_CFG(show_nr_jumps
),
1135 ANNOTATE_CFG(show_total_period
),
1136 ANNOTATE_CFG(use_offset
),
1141 static int annotate_config__cmp(const void *name
, const void *cfgp
)
1143 const struct annotate_config
*cfg
= cfgp
;
1145 return strcmp(name
, cfg
->name
);
1148 static int annotate__config(const char *var
, const char *value
,
1149 void *data __maybe_unused
)
1151 struct annotate_config
*cfg
;
1154 if (prefixcmp(var
, "annotate.") != 0)
1158 cfg
= bsearch(name
, annotate__configs
, ARRAY_SIZE(annotate__configs
),
1159 sizeof(struct annotate_config
), annotate_config__cmp
);
1162 ui__warning("%s variable unknown, ignoring...", var
);
1164 *cfg
->value
= perf_config_bool(name
, value
);
1168 void annotate_browser__init(void)
1170 perf_config(annotate__config
, NULL
);