1 #include "../../util/util.h"
2 #include "../browser.h"
3 #include "../helpline.h"
4 #include "../libslang.h"
7 #include "../../util/annotate.h"
8 #include "../../util/hist.h"
9 #include "../../util/sort.h"
10 #include "../../util/symbol.h"
14 struct annotate_browser
{
16 struct rb_root entries
;
17 struct rb_node
*curr_hot
;
18 struct objdump_line
*selection
;
24 bool searching_backwards
;
28 struct objdump_line_rb_node
{
29 struct rb_node rb_node
;
36 struct objdump_line_rb_node
*objdump_line__rb(struct objdump_line
*self
)
38 return (struct objdump_line_rb_node
*)(self
+ 1);
41 static bool objdump_line__filter(struct ui_browser
*browser
, void *entry
)
43 struct annotate_browser
*ab
= container_of(browser
, struct annotate_browser
, b
);
45 if (ab
->hide_src_code
) {
46 struct objdump_line
*ol
= list_entry(entry
, struct objdump_line
, node
);
47 return ol
->offset
== -1;
53 static void annotate_browser__write(struct ui_browser
*self
, void *entry
, int row
)
55 struct annotate_browser
*ab
= container_of(self
, struct annotate_browser
, b
);
56 struct objdump_line
*ol
= list_entry(entry
, struct objdump_line
, node
);
57 bool current_entry
= ui_browser__is_current_entry(self
, row
);
58 bool change_color
= (!ab
->hide_src_code
&&
59 (!current_entry
|| (self
->use_navkeypressed
&&
60 !self
->navkeypressed
)));
61 int width
= self
->width
;
63 if (ol
->offset
!= -1) {
64 struct objdump_line_rb_node
*olrb
= objdump_line__rb(ol
);
65 ui_browser__set_percent_color(self
, olrb
->percent
, current_entry
);
66 slsmg_printf(" %7.2f ", olrb
->percent
);
68 ui_browser__set_percent_color(self
, 0, current_entry
);
69 slsmg_write_nstring(" ", 9);
72 SLsmg_write_char(':');
73 slsmg_write_nstring(" ", 8);
75 /* The scroll bar isn't being used */
76 if (!self
->navkeypressed
)
79 if (ol
->offset
!= -1 && change_color
)
80 ui_browser__set_color(self
, HE_COLORSET_CODE
);
83 slsmg_write_nstring(" ", width
- 18);
84 else if (ol
->offset
== -1)
85 slsmg_write_nstring(ol
->line
, width
- 18);
88 u64 addr
= ol
->offset
;
89 int printed
, color
= -1;
94 printed
= scnprintf(bf
, sizeof(bf
), " %" PRIx64
":", addr
);
96 color
= ui_browser__set_color(self
, HE_COLORSET_ADDR
);
97 slsmg_write_nstring(bf
, printed
);
99 ui_browser__set_color(self
, color
);
100 slsmg_write_nstring(ol
->line
, width
- 18 - printed
);
107 static double objdump_line__calc_percent(struct objdump_line
*self
,
108 struct symbol
*sym
, int evidx
)
110 double percent
= 0.0;
112 if (self
->offset
!= -1) {
113 int len
= sym
->end
- sym
->start
;
114 unsigned int hits
= 0;
115 struct annotation
*notes
= symbol__annotation(sym
);
116 struct source_line
*src_line
= notes
->src
->lines
;
117 struct sym_hist
*h
= annotation__histogram(notes
, evidx
);
118 s64 offset
= self
->offset
;
119 struct objdump_line
*next
;
121 next
= objdump__get_next_ip_line(¬es
->src
->source
, self
);
122 while (offset
< (s64
)len
&&
123 (next
== NULL
|| offset
< next
->offset
)) {
125 percent
+= src_line
[offset
].percent
;
127 hits
+= h
->addr
[offset
];
132 * If the percentage wasn't already calculated in
133 * symbol__get_source_line, do it now:
135 if (src_line
== NULL
&& h
->sum
)
136 percent
= 100.0 * hits
/ h
->sum
;
142 static void objdump__insert_line(struct rb_root
*self
,
143 struct objdump_line_rb_node
*line
)
145 struct rb_node
**p
= &self
->rb_node
;
146 struct rb_node
*parent
= NULL
;
147 struct objdump_line_rb_node
*l
;
151 l
= rb_entry(parent
, struct objdump_line_rb_node
, rb_node
);
152 if (line
->percent
< l
->percent
)
157 rb_link_node(&line
->rb_node
, parent
, p
);
158 rb_insert_color(&line
->rb_node
, self
);
161 static void annotate_browser__set_top(struct annotate_browser
*self
,
162 struct objdump_line
*pos
, u32 idx
)
166 ui_browser__refresh_dimensions(&self
->b
);
167 back
= self
->b
.height
/ 2;
168 self
->b
.top_idx
= self
->b
.index
= idx
;
170 while (self
->b
.top_idx
!= 0 && back
!= 0) {
171 pos
= list_entry(pos
->node
.prev
, struct objdump_line
, node
);
173 if (objdump_line__filter(&self
->b
, &pos
->node
))
181 self
->b
.navkeypressed
= true;
184 static void annotate_browser__set_rb_top(struct annotate_browser
*browser
,
187 struct objdump_line_rb_node
*rbpos
;
188 struct objdump_line
*pos
;
190 rbpos
= rb_entry(nd
, struct objdump_line_rb_node
, rb_node
);
191 pos
= ((struct objdump_line
*)rbpos
) - 1;
192 annotate_browser__set_top(browser
, pos
, rbpos
->idx
);
193 browser
->curr_hot
= nd
;
196 static void annotate_browser__calc_percent(struct annotate_browser
*browser
,
199 struct map_symbol
*ms
= browser
->b
.priv
;
200 struct symbol
*sym
= ms
->sym
;
201 struct annotation
*notes
= symbol__annotation(sym
);
202 struct objdump_line
*pos
;
204 browser
->entries
= RB_ROOT
;
206 pthread_mutex_lock(¬es
->lock
);
208 list_for_each_entry(pos
, ¬es
->src
->source
, node
) {
209 struct objdump_line_rb_node
*rbpos
= objdump_line__rb(pos
);
210 rbpos
->percent
= objdump_line__calc_percent(pos
, sym
, evidx
);
211 if (rbpos
->percent
< 0.01) {
212 RB_CLEAR_NODE(&rbpos
->rb_node
);
215 objdump__insert_line(&browser
->entries
, rbpos
);
217 pthread_mutex_unlock(¬es
->lock
);
219 browser
->curr_hot
= rb_last(&browser
->entries
);
222 static bool annotate_browser__toggle_source(struct annotate_browser
*browser
)
224 struct objdump_line
*ol
;
225 struct objdump_line_rb_node
*olrb
;
226 off_t offset
= browser
->b
.index
- browser
->b
.top_idx
;
228 browser
->b
.seek(&browser
->b
, offset
, SEEK_CUR
);
229 ol
= list_entry(browser
->b
.top
, struct objdump_line
, node
);
230 olrb
= objdump_line__rb(ol
);
232 if (browser
->hide_src_code
) {
233 if (olrb
->idx_asm
< offset
)
236 browser
->b
.nr_entries
= browser
->nr_entries
;
237 browser
->hide_src_code
= false;
238 browser
->b
.seek(&browser
->b
, -offset
, SEEK_CUR
);
239 browser
->b
.top_idx
= olrb
->idx
- offset
;
240 browser
->b
.index
= olrb
->idx
;
242 if (olrb
->idx_asm
< 0) {
243 ui_helpline__puts("Only available for assembly lines.");
244 browser
->b
.seek(&browser
->b
, -offset
, SEEK_CUR
);
248 if (olrb
->idx_asm
< offset
)
249 offset
= olrb
->idx_asm
;
251 browser
->b
.nr_entries
= browser
->nr_asm_entries
;
252 browser
->hide_src_code
= true;
253 browser
->b
.seek(&browser
->b
, -offset
, SEEK_CUR
);
254 browser
->b
.top_idx
= olrb
->idx_asm
- offset
;
255 browser
->b
.index
= olrb
->idx_asm
;
261 static bool annotate_browser__callq(struct annotate_browser
*browser
,
262 int evidx
, void (*timer
)(void *arg
),
263 void *arg
, int delay_secs
)
265 struct map_symbol
*ms
= browser
->b
.priv
;
266 struct symbol
*sym
= ms
->sym
;
267 struct annotation
*notes
;
268 struct symbol
*target
;
269 char *s
= strstr(browser
->selection
->line
, "callq ");
277 ui_helpline__puts("Invallid callq instruction.");
281 ip
= strtoull(s
, NULL
, 16);
282 ip
= ms
->map
->map_ip(ms
->map
, ip
);
283 target
= map__find_symbol(ms
->map
, ip
, NULL
);
284 if (target
== NULL
) {
285 ui_helpline__puts("The called function was not found.");
289 notes
= symbol__annotation(target
);
290 pthread_mutex_lock(¬es
->lock
);
292 if (notes
->src
== NULL
&& symbol__alloc_hist(target
) < 0) {
293 pthread_mutex_unlock(¬es
->lock
);
294 ui__warning("Not enough memory for annotating '%s' symbol!\n",
299 pthread_mutex_unlock(¬es
->lock
);
300 symbol__tui_annotate(target
, ms
->map
, evidx
, timer
, arg
, delay_secs
);
301 ui_browser__show_title(&browser
->b
, sym
->name
);
305 static struct objdump_line
*
306 annotate_browser__find_offset(struct annotate_browser
*browser
,
307 s64 offset
, s64
*idx
)
309 struct map_symbol
*ms
= browser
->b
.priv
;
310 struct symbol
*sym
= ms
->sym
;
311 struct annotation
*notes
= symbol__annotation(sym
);
312 struct objdump_line
*pos
;
315 list_for_each_entry(pos
, ¬es
->src
->source
, node
) {
316 if (pos
->offset
== offset
)
318 if (!objdump_line__filter(&browser
->b
, &pos
->node
))
325 static bool annotate_browser__jump(struct annotate_browser
*browser
)
327 const char *jumps
[] = { "je ", "jne ", "ja ", "jmpq ", "js ", "jmp ", NULL
};
328 struct objdump_line
*line
;
334 s
= strstr(browser
->selection
->line
, jumps
[i
++]);
344 ui_helpline__puts("Invallid jump instruction.");
348 offset
= strtoll(s
, NULL
, 16);
349 line
= annotate_browser__find_offset(browser
, offset
, &idx
);
351 ui_helpline__puts("Invallid jump offset");
355 annotate_browser__set_top(browser
, line
, idx
);
360 static struct objdump_line
*
361 annotate_browser__find_string(struct annotate_browser
*browser
,
364 struct map_symbol
*ms
= browser
->b
.priv
;
365 struct symbol
*sym
= ms
->sym
;
366 struct annotation
*notes
= symbol__annotation(sym
);
367 struct objdump_line
*pos
= browser
->selection
;
369 *idx
= browser
->b
.index
;
370 list_for_each_entry_continue(pos
, ¬es
->src
->source
, node
) {
371 if (objdump_line__filter(&browser
->b
, &pos
->node
))
376 if (pos
->line
&& strstr(pos
->line
, s
) != NULL
)
383 static bool __annotate_browser__search(struct annotate_browser
*browser
)
385 struct objdump_line
*line
;
388 line
= annotate_browser__find_string(browser
, browser
->search_bf
, &idx
);
390 ui_helpline__puts("String not found!");
394 annotate_browser__set_top(browser
, line
, idx
);
395 browser
->searching_backwards
= false;
399 static struct objdump_line
*
400 annotate_browser__find_string_reverse(struct annotate_browser
*browser
,
403 struct map_symbol
*ms
= browser
->b
.priv
;
404 struct symbol
*sym
= ms
->sym
;
405 struct annotation
*notes
= symbol__annotation(sym
);
406 struct objdump_line
*pos
= browser
->selection
;
408 *idx
= browser
->b
.index
;
409 list_for_each_entry_continue_reverse(pos
, ¬es
->src
->source
, node
) {
410 if (objdump_line__filter(&browser
->b
, &pos
->node
))
415 if (pos
->line
&& strstr(pos
->line
, s
) != NULL
)
422 static bool __annotate_browser__search_reverse(struct annotate_browser
*browser
)
424 struct objdump_line
*line
;
427 line
= annotate_browser__find_string_reverse(browser
, browser
->search_bf
, &idx
);
429 ui_helpline__puts("String not found!");
433 annotate_browser__set_top(browser
, line
, idx
);
434 browser
->searching_backwards
= true;
438 static bool annotate_browser__search_window(struct annotate_browser
*browser
,
441 if (ui_browser__input_window("Search", "String: ", browser
->search_bf
,
442 "ENTER: OK, ESC: Cancel",
443 delay_secs
* 2) != K_ENTER
||
444 !*browser
->search_bf
)
450 static bool annotate_browser__search(struct annotate_browser
*browser
, int delay_secs
)
452 if (annotate_browser__search_window(browser
, delay_secs
))
453 return __annotate_browser__search(browser
);
458 static bool annotate_browser__continue_search(struct annotate_browser
*browser
,
461 if (!*browser
->search_bf
)
462 return annotate_browser__search(browser
, delay_secs
);
464 return __annotate_browser__search(browser
);
467 static bool annotate_browser__search_reverse(struct annotate_browser
*browser
,
470 if (annotate_browser__search_window(browser
, delay_secs
))
471 return __annotate_browser__search_reverse(browser
);
477 bool annotate_browser__continue_search_reverse(struct annotate_browser
*browser
,
480 if (!*browser
->search_bf
)
481 return annotate_browser__search_reverse(browser
, delay_secs
);
483 return __annotate_browser__search_reverse(browser
);
486 static int annotate_browser__run(struct annotate_browser
*self
, int evidx
,
487 void(*timer
)(void *arg
),
488 void *arg
, int delay_secs
)
490 struct rb_node
*nd
= NULL
;
491 struct map_symbol
*ms
= self
->b
.priv
;
492 struct symbol
*sym
= ms
->sym
;
493 const char *help
= "<-/ESC: Exit, TAB/shift+TAB: Cycle hot lines, "
494 "H: Go to hottest line, ->/ENTER: Line action, "
495 "O: Toggle offset view, "
496 "S: Toggle source code view";
499 if (ui_browser__show(&self
->b
, sym
->name
, help
) < 0)
502 annotate_browser__calc_percent(self
, evidx
);
504 if (self
->curr_hot
) {
505 annotate_browser__set_rb_top(self
, self
->curr_hot
);
506 self
->b
.navkeypressed
= false;
512 key
= ui_browser__run(&self
->b
, delay_secs
);
514 if (delay_secs
!= 0) {
515 annotate_browser__calc_percent(self
, evidx
);
517 * Current line focus got out of the list of most active
518 * lines, NULL it so that if TAB|UNTAB is pressed, we
519 * move to curr_hot (current hottest line).
521 if (nd
!= NULL
&& RB_EMPTY_NODE(nd
))
531 symbol__annotate_decay_histogram(sym
, evidx
);
537 nd
= rb_last(&self
->entries
);
545 nd
= rb_first(&self
->entries
);
555 if (annotate_browser__toggle_source(self
))
556 ui_helpline__puts(help
);
560 self
->use_offset
= !self
->use_offset
;
563 if (annotate_browser__search(self
, delay_secs
)) {
565 ui_helpline__puts(help
);
569 if (self
->searching_backwards
?
570 annotate_browser__continue_search_reverse(self
, delay_secs
) :
571 annotate_browser__continue_search(self
, delay_secs
))
575 if (annotate_browser__search_reverse(self
, delay_secs
))
580 if (self
->selection
== NULL
)
581 ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
582 else if (self
->selection
->offset
== -1)
583 ui_helpline__puts("Actions are only available for assembly lines.");
584 else if (!(annotate_browser__jump(self
) ||
585 annotate_browser__callq(self
, evidx
, timer
, arg
, delay_secs
)))
586 ui_helpline__puts("Actions are only available for the 'callq' and jump instructions.");
598 annotate_browser__set_rb_top(self
, nd
);
601 ui_browser__hide(&self
->b
);
605 int hist_entry__tui_annotate(struct hist_entry
*he
, int evidx
,
606 void(*timer
)(void *arg
), void *arg
, int delay_secs
)
608 return symbol__tui_annotate(he
->ms
.sym
, he
->ms
.map
, evidx
,
609 timer
, arg
, delay_secs
);
612 int symbol__tui_annotate(struct symbol
*sym
, struct map
*map
, int evidx
,
613 void(*timer
)(void *arg
), void *arg
,
616 struct objdump_line
*pos
, *n
;
617 struct annotation
*notes
;
618 struct map_symbol ms
= {
622 struct annotate_browser browser
= {
624 .refresh
= ui_browser__list_head_refresh
,
625 .seek
= ui_browser__list_head_seek
,
626 .write
= annotate_browser__write
,
627 .filter
= objdump_line__filter
,
629 .use_navkeypressed
= true,
637 if (map
->dso
->annotate_warned
)
640 if (symbol__annotate(sym
, map
, sizeof(struct objdump_line_rb_node
)) < 0) {
641 ui__error("%s", ui_helpline__last_msg
);
645 ui_helpline__push("Press <- or ESC to exit");
647 notes
= symbol__annotation(sym
);
648 browser
.start
= map__rip_2objdump(map
, sym
->start
);
650 list_for_each_entry(pos
, ¬es
->src
->source
, node
) {
651 struct objdump_line_rb_node
*rbpos
;
652 size_t line_len
= strlen(pos
->line
);
654 if (browser
.b
.width
< line_len
)
655 browser
.b
.width
= line_len
;
656 rbpos
= objdump_line__rb(pos
);
657 rbpos
->idx
= browser
.nr_entries
++;
658 if (pos
->offset
!= -1)
659 rbpos
->idx_asm
= browser
.nr_asm_entries
++;
664 browser
.b
.nr_entries
= browser
.nr_entries
;
665 browser
.b
.entries
= ¬es
->src
->source
,
666 browser
.b
.width
+= 18; /* Percentage */
667 ret
= annotate_browser__run(&browser
, evidx
, timer
, arg
, delay_secs
);
668 list_for_each_entry_safe(pos
, n
, ¬es
->src
->source
, node
) {
669 list_del(&pos
->node
);
670 objdump_line__free(pos
);