]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blame - tools/perf/ui/browsers/annotate.c
perf config: Allow '_' in config file variable names
[mirror_ubuntu-zesty-kernel.git] / tools / perf / ui / browsers / annotate.c
CommitLineData
aca7a94d 1#include "../../util/util.h"
211ef127
ACM
2#include "../browser.h"
3#include "../helpline.h"
4#include "../libslang.h"
ae55795e
ACM
5#include "../ui.h"
6#include "../util.h"
aca7a94d
NK
7#include "../../util/annotate.h"
8#include "../../util/hist.h"
9#include "../../util/sort.h"
10#include "../../util/symbol.h"
c97cf422 11#include <pthread.h>
cf958003 12#include <newt.h>
211ef127 13
b793a401
ACM
14struct browser_disasm_line {
15 struct rb_node rb_node;
16 double percent;
17 u32 idx;
18 int idx_asm;
7d5b12f5 19 int jump_sources;
b793a401
ACM
20};
21
e9823b21
ACM
22static struct annotate_browser_opt {
23 bool hide_src_code,
24 use_offset,
25 jump_arrows,
26 show_nr_jumps;
27} annotate_browser__opts = {
28 .use_offset = true,
29 .jump_arrows = true,
30};
31
92221162
ACM
32struct annotate_browser {
33 struct ui_browser b;
34 struct rb_root entries;
f1e9214c 35 struct rb_node *curr_hot;
29ed6e76 36 struct disasm_line *selection;
b793a401 37 struct disasm_line **offsets;
058b4cc9 38 u64 start;
0361fc25
ACM
39 int nr_asm_entries;
40 int nr_entries;
2402e4a9
ACM
41 int max_jump_sources;
42 int nr_jumps;
d3d1f61a 43 bool searching_backwards;
83b1f2aa 44 u8 addr_width;
2402e4a9
ACM
45 u8 jumps_width;
46 u8 target_width;
83b1f2aa
ACM
47 u8 min_addr_width;
48 u8 max_addr_width;
d3d1f61a 49 char search_bf[128];
92221162
ACM
50};
51
887c0066 52static inline struct browser_disasm_line *disasm_line__browser(struct disasm_line *dl)
92221162 53{
887c0066 54 return (struct browser_disasm_line *)(dl + 1);
92221162
ACM
55}
56
e9823b21 57static bool disasm_line__filter(struct ui_browser *browser __used, void *entry)
0361fc25 58{
e9823b21 59 if (annotate_browser__opts.hide_src_code) {
29ed6e76
ACM
60 struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
61 return dl->offset == -1;
0361fc25
ACM
62 }
63
64 return false;
65}
66
2402e4a9
ACM
67static int annotate_browser__jumps_percent_color(struct annotate_browser *browser,
68 int nr, bool current)
69{
70 if (current && (!browser->b.use_navkeypressed || browser->b.navkeypressed))
71 return HE_COLORSET_SELECTED;
72 if (nr == browser->max_jump_sources)
73 return HE_COLORSET_TOP;
74 if (nr > 1)
75 return HE_COLORSET_MEDIUM;
76 return HE_COLORSET_NORMAL;
77}
78
79static int annotate_browser__set_jumps_percent_color(struct annotate_browser *browser,
80 int nr, bool current)
81{
82 int color = annotate_browser__jumps_percent_color(browser, nr, current);
83 return ui_browser__set_color(&browser->b, color);
84}
85
211ef127
ACM
86static void annotate_browser__write(struct ui_browser *self, void *entry, int row)
87{
34958544 88 struct annotate_browser *ab = container_of(self, struct annotate_browser, b);
29ed6e76 89 struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
b793a401 90 struct browser_disasm_line *bdl = disasm_line__browser(dl);
211ef127 91 bool current_entry = ui_browser__is_current_entry(self, row);
e9823b21 92 bool change_color = (!annotate_browser__opts.hide_src_code &&
058b4cc9
ACM
93 (!current_entry || (self->use_navkeypressed &&
94 !self->navkeypressed)));
83b1f2aa
ACM
95 int width = self->width, printed;
96 char bf[256];
211ef127 97
0822cc80 98 if (dl->offset != -1 && bdl->percent != 0.0) {
887c0066 99 ui_browser__set_percent_color(self, bdl->percent, current_entry);
0822cc80 100 slsmg_printf("%6.2f ", bdl->percent);
92221162 101 } else {
8f9bbc40 102 ui_browser__set_percent_color(self, 0, current_entry);
0822cc80 103 slsmg_write_nstring(" ", 7);
92221162
ACM
104 }
105
cf2dacc5 106 SLsmg_write_char(' ');
c172f742
ACM
107
108 /* The scroll bar isn't being used */
109 if (!self->navkeypressed)
110 width += 1;
111
29ed6e76 112 if (!*dl->line)
0822cc80 113 slsmg_write_nstring(" ", width - 7);
83b1f2aa
ACM
114 else if (dl->offset == -1) {
115 printed = scnprintf(bf, sizeof(bf), "%*s ",
116 ab->addr_width, " ");
117 slsmg_write_nstring(bf, printed);
118 slsmg_write_nstring(dl->line, width - printed - 6);
119 } else {
29ed6e76 120 u64 addr = dl->offset;
83b1f2aa 121 int color = -1;
058b4cc9 122
e9823b21 123 if (!annotate_browser__opts.use_offset)
e235f3f3
ACM
124 addr += ab->start;
125
e9823b21 126 if (!annotate_browser__opts.use_offset) {
83b1f2aa 127 printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
61e04b33 128 } else {
7d5b12f5 129 if (bdl->jump_sources) {
e9823b21 130 if (annotate_browser__opts.show_nr_jumps) {
2402e4a9
ACM
131 int prev;
132 printed = scnprintf(bf, sizeof(bf), "%*d ",
133 ab->jumps_width,
134 bdl->jump_sources);
135 prev = annotate_browser__set_jumps_percent_color(ab, bdl->jump_sources,
136 current_entry);
137 slsmg_write_nstring(bf, printed);
138 ui_browser__set_color(self, prev);
139 }
140
83b1f2aa 141 printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
2402e4a9 142 ab->target_width, addr);
61e04b33 143 } else {
83b1f2aa
ACM
144 printed = scnprintf(bf, sizeof(bf), "%*s ",
145 ab->addr_width, " ");
61e04b33
ACM
146 }
147 }
b793a401 148
058b4cc9
ACM
149 if (change_color)
150 color = ui_browser__set_color(self, HE_COLORSET_ADDR);
151 slsmg_write_nstring(bf, printed);
152 if (change_color)
153 ui_browser__set_color(self, color);
28548d78 154 if (dl->ins && dl->ins->ops->scnprintf) {
51a0d455 155 if (ins__is_jump(dl->ins)) {
44d1a3ed 156 bool fwd = dl->ops.target.offset > (u64)dl->offset;
51a0d455 157
59d038d5
ACM
158 ui_browser__write_graph(self, fwd ? SLSMG_DARROW_CHAR :
159 SLSMG_UARROW_CHAR);
51a0d455 160 SLsmg_write_char(' ');
88298f5a
ACM
161 } else if (ins__is_call(dl->ins)) {
162 ui_browser__write_graph(self, SLSMG_RARROW_CHAR);
163 SLsmg_write_char(' ');
51a0d455
ACM
164 } else {
165 slsmg_write_nstring(" ", 2);
166 }
4ea08b52
ACM
167 } else {
168 if (strcmp(dl->name, "retq")) {
169 slsmg_write_nstring(" ", 2);
170 } else {
59d038d5 171 ui_browser__write_graph(self, SLSMG_LARROW_CHAR);
4ea08b52
ACM
172 SLsmg_write_char(' ');
173 }
4ea08b52 174 }
28548d78 175
e9823b21 176 disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset);
83b1f2aa 177 slsmg_write_nstring(bf, width - 10 - printed);
058b4cc9 178 }
b99976e2 179
58e817d9 180 if (current_entry)
29ed6e76 181 ab->selection = dl;
92221162
ACM
182}
183
9d1ef56d 184static void annotate_browser__draw_current_jump(struct ui_browser *browser)
a3f895be
ACM
185{
186 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
9d1ef56d
ACM
187 struct disasm_line *cursor = ab->selection, *target;
188 struct browser_disasm_line *btarget, *bcursor;
83b1f2aa 189 unsigned int from, to;
a3f895be 190
e9823b21 191 if (!cursor || !cursor->ins || !ins__is_jump(cursor->ins) ||
9d1ef56d
ACM
192 !disasm_line__has_offset(cursor))
193 return;
a3f895be 194
9d1ef56d
ACM
195 target = ab->offsets[cursor->ops.target.offset];
196 if (!target)
197 return;
a3f895be 198
9d1ef56d
ACM
199 bcursor = disasm_line__browser(cursor);
200 btarget = disasm_line__browser(target);
a3f895be 201
e9823b21 202 if (annotate_browser__opts.hide_src_code) {
9d1ef56d 203 from = bcursor->idx_asm;
a3f895be
ACM
204 to = btarget->idx_asm;
205 } else {
9d1ef56d 206 from = (u64)bcursor->idx;
a3f895be
ACM
207 to = (u64)btarget->idx;
208 }
209
210 ui_browser__set_color(browser, HE_COLORSET_CODE);
83b1f2aa 211 __ui_browser__line_arrow(browser, 9 + ab->addr_width, from, to);
a3f895be
ACM
212}
213
214static unsigned int annotate_browser__refresh(struct ui_browser *browser)
215{
216 int ret = ui_browser__list_head_refresh(browser);
217
e9823b21 218 if (annotate_browser__opts.jump_arrows)
9d1ef56d 219 annotate_browser__draw_current_jump(browser);
a3f895be 220
83b1f2aa
ACM
221 ui_browser__set_color(browser, HE_COLORSET_NORMAL);
222 __ui_browser__vline(browser, 7, 0, browser->height - 1);
a3f895be
ACM
223 return ret;
224}
225
29ed6e76 226static double disasm_line__calc_percent(struct disasm_line *dl, struct symbol *sym, int evidx)
92221162
ACM
227{
228 double percent = 0.0;
229
29ed6e76 230 if (dl->offset != -1) {
92221162 231 int len = sym->end - sym->start;
211ef127 232 unsigned int hits = 0;
78f7defe 233 struct annotation *notes = symbol__annotation(sym);
ce6f4fab 234 struct source_line *src_line = notes->src->lines;
2f525d01 235 struct sym_hist *h = annotation__histogram(notes, evidx);
29ed6e76
ACM
236 s64 offset = dl->offset;
237 struct disasm_line *next;
92221162 238
29ed6e76 239 next = disasm__get_next_ip_line(&notes->src->source, dl);
211ef127
ACM
240 while (offset < (s64)len &&
241 (next == NULL || offset < next->offset)) {
78f7defe
ACM
242 if (src_line) {
243 percent += src_line[offset].percent;
211ef127 244 } else
78f7defe 245 hits += h->addr[offset];
211ef127
ACM
246
247 ++offset;
248 }
78f7defe
ACM
249 /*
250 * If the percentage wasn't already calculated in
251 * symbol__get_source_line, do it now:
252 */
253 if (src_line == NULL && h->sum)
211ef127 254 percent = 100.0 * hits / h->sum;
211ef127
ACM
255 }
256
92221162
ACM
257 return percent;
258}
259
887c0066 260static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl)
92221162 261{
29ed6e76 262 struct rb_node **p = &root->rb_node;
92221162 263 struct rb_node *parent = NULL;
887c0066 264 struct browser_disasm_line *l;
92221162
ACM
265
266 while (*p != NULL) {
267 parent = *p;
887c0066
ACM
268 l = rb_entry(parent, struct browser_disasm_line, rb_node);
269 if (bdl->percent < l->percent)
92221162
ACM
270 p = &(*p)->rb_left;
271 else
272 p = &(*p)->rb_right;
273 }
887c0066
ACM
274 rb_link_node(&bdl->rb_node, parent, p);
275 rb_insert_color(&bdl->rb_node, root);
211ef127
ACM
276}
277
f1e9214c 278static void annotate_browser__set_top(struct annotate_browser *self,
29ed6e76 279 struct disasm_line *pos, u32 idx)
f1e9214c 280{
f1e9214c
ACM
281 unsigned back;
282
283 ui_browser__refresh_dimensions(&self->b);
284 back = self->b.height / 2;
b0ffb2c4 285 self->b.top_idx = self->b.index = idx;
f1e9214c
ACM
286
287 while (self->b.top_idx != 0 && back != 0) {
29ed6e76 288 pos = list_entry(pos->node.prev, struct disasm_line, node);
f1e9214c 289
29ed6e76 290 if (disasm_line__filter(&self->b, &pos->node))
08be4eed
ACM
291 continue;
292
f1e9214c
ACM
293 --self->b.top_idx;
294 --back;
295 }
296
297 self->b.top = pos;
d3d1f61a 298 self->b.navkeypressed = true;
b0ffb2c4
ACM
299}
300
301static void annotate_browser__set_rb_top(struct annotate_browser *browser,
302 struct rb_node *nd)
303{
887c0066 304 struct browser_disasm_line *bpos;
29ed6e76 305 struct disasm_line *pos;
a44b45f2 306 u32 idx;
b0ffb2c4 307
887c0066
ACM
308 bpos = rb_entry(nd, struct browser_disasm_line, rb_node);
309 pos = ((struct disasm_line *)bpos) - 1;
a44b45f2 310 idx = bpos->idx;
e9823b21 311 if (annotate_browser__opts.hide_src_code)
a44b45f2
ACM
312 idx = bpos->idx_asm;
313 annotate_browser__set_top(browser, pos, idx);
b0ffb2c4 314 browser->curr_hot = nd;
f1e9214c
ACM
315}
316
c97cf422
ACM
317static void annotate_browser__calc_percent(struct annotate_browser *browser,
318 int evidx)
f1e9214c 319{
34958544
ACM
320 struct map_symbol *ms = browser->b.priv;
321 struct symbol *sym = ms->sym;
c97cf422 322 struct annotation *notes = symbol__annotation(sym);
29ed6e76 323 struct disasm_line *pos;
c97cf422
ACM
324
325 browser->entries = RB_ROOT;
326
327 pthread_mutex_lock(&notes->lock);
328
329 list_for_each_entry(pos, &notes->src->source, node) {
887c0066
ACM
330 struct browser_disasm_line *bpos = disasm_line__browser(pos);
331 bpos->percent = disasm_line__calc_percent(pos, sym, evidx);
332 if (bpos->percent < 0.01) {
333 RB_CLEAR_NODE(&bpos->rb_node);
c97cf422
ACM
334 continue;
335 }
887c0066 336 disasm_rb_tree__insert(&browser->entries, bpos);
c97cf422
ACM
337 }
338 pthread_mutex_unlock(&notes->lock);
339
340 browser->curr_hot = rb_last(&browser->entries);
341}
342
0361fc25
ACM
343static bool annotate_browser__toggle_source(struct annotate_browser *browser)
344{
29ed6e76 345 struct disasm_line *dl;
887c0066 346 struct browser_disasm_line *bdl;
0361fc25
ACM
347 off_t offset = browser->b.index - browser->b.top_idx;
348
349 browser->b.seek(&browser->b, offset, SEEK_CUR);
29ed6e76 350 dl = list_entry(browser->b.top, struct disasm_line, node);
887c0066 351 bdl = disasm_line__browser(dl);
0361fc25 352
e9823b21 353 if (annotate_browser__opts.hide_src_code) {
887c0066
ACM
354 if (bdl->idx_asm < offset)
355 offset = bdl->idx;
0361fc25
ACM
356
357 browser->b.nr_entries = browser->nr_entries;
e9823b21 358 annotate_browser__opts.hide_src_code = false;
0361fc25 359 browser->b.seek(&browser->b, -offset, SEEK_CUR);
887c0066
ACM
360 browser->b.top_idx = bdl->idx - offset;
361 browser->b.index = bdl->idx;
0361fc25 362 } else {
887c0066 363 if (bdl->idx_asm < 0) {
0361fc25
ACM
364 ui_helpline__puts("Only available for assembly lines.");
365 browser->b.seek(&browser->b, -offset, SEEK_CUR);
366 return false;
367 }
368
887c0066
ACM
369 if (bdl->idx_asm < offset)
370 offset = bdl->idx_asm;
0361fc25
ACM
371
372 browser->b.nr_entries = browser->nr_asm_entries;
e9823b21 373 annotate_browser__opts.hide_src_code = true;
0361fc25 374 browser->b.seek(&browser->b, -offset, SEEK_CUR);
887c0066
ACM
375 browser->b.top_idx = bdl->idx_asm - offset;
376 browser->b.index = bdl->idx_asm;
0361fc25
ACM
377 }
378
379 return true;
380}
381
e9823b21
ACM
382static void annotate_browser__init_asm_mode(struct annotate_browser *browser)
383{
384 ui_browser__reset_index(&browser->b);
385 browser->b.nr_entries = browser->nr_asm_entries;
386}
387
60521702
ACM
388static bool annotate_browser__callq(struct annotate_browser *browser,
389 int evidx, void (*timer)(void *arg),
390 void *arg, int delay_secs)
391{
392 struct map_symbol *ms = browser->b.priv;
657bcaf5 393 struct disasm_line *dl = browser->selection;
60521702
ACM
394 struct symbol *sym = ms->sym;
395 struct annotation *notes;
396 struct symbol *target;
60521702
ACM
397 u64 ip;
398
d86b0597 399 if (!ins__is_call(dl->ins))
60521702
ACM
400 return false;
401
44d1a3ed 402 ip = ms->map->map_ip(ms->map, dl->ops.target.addr);
60521702
ACM
403 target = map__find_symbol(ms->map, ip, NULL);
404 if (target == NULL) {
405 ui_helpline__puts("The called function was not found.");
406 return true;
407 }
408
409 notes = symbol__annotation(target);
410 pthread_mutex_lock(&notes->lock);
411
412 if (notes->src == NULL && symbol__alloc_hist(target) < 0) {
413 pthread_mutex_unlock(&notes->lock);
414 ui__warning("Not enough memory for annotating '%s' symbol!\n",
415 target->name);
416 return true;
417 }
418
419 pthread_mutex_unlock(&notes->lock);
420 symbol__tui_annotate(target, ms->map, evidx, timer, arg, delay_secs);
421 ui_browser__show_title(&browser->b, sym->name);
422 return true;
423}
424
29ed6e76
ACM
425static
426struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
427 s64 offset, s64 *idx)
08be4eed
ACM
428{
429 struct map_symbol *ms = browser->b.priv;
430 struct symbol *sym = ms->sym;
431 struct annotation *notes = symbol__annotation(sym);
29ed6e76 432 struct disasm_line *pos;
08be4eed
ACM
433
434 *idx = 0;
435 list_for_each_entry(pos, &notes->src->source, node) {
436 if (pos->offset == offset)
437 return pos;
29ed6e76 438 if (!disasm_line__filter(&browser->b, &pos->node))
08be4eed
ACM
439 ++*idx;
440 }
441
442 return NULL;
443}
444
445static bool annotate_browser__jump(struct annotate_browser *browser)
446{
657bcaf5 447 struct disasm_line *dl = browser->selection;
4f9d0325 448 s64 idx;
08be4eed 449
d86b0597 450 if (!ins__is_jump(dl->ins))
08be4eed
ACM
451 return false;
452
44d1a3ed 453 dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx);
29ed6e76 454 if (dl == NULL) {
08be4eed
ACM
455 ui_helpline__puts("Invallid jump offset");
456 return true;
457 }
458
29ed6e76 459 annotate_browser__set_top(browser, dl, idx);
08be4eed
ACM
460
461 return true;
462}
463
29ed6e76
ACM
464static
465struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
466 char *s, s64 *idx)
d3d1f61a
ACM
467{
468 struct map_symbol *ms = browser->b.priv;
469 struct symbol *sym = ms->sym;
470 struct annotation *notes = symbol__annotation(sym);
29ed6e76 471 struct disasm_line *pos = browser->selection;
d3d1f61a
ACM
472
473 *idx = browser->b.index;
474 list_for_each_entry_continue(pos, &notes->src->source, node) {
29ed6e76 475 if (disasm_line__filter(&browser->b, &pos->node))
d3d1f61a
ACM
476 continue;
477
478 ++*idx;
479
480 if (pos->line && strstr(pos->line, s) != NULL)
481 return pos;
482 }
483
484 return NULL;
485}
486
487static bool __annotate_browser__search(struct annotate_browser *browser)
488{
29ed6e76 489 struct disasm_line *dl;
d3d1f61a
ACM
490 s64 idx;
491
29ed6e76
ACM
492 dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
493 if (dl == NULL) {
d3d1f61a
ACM
494 ui_helpline__puts("String not found!");
495 return false;
496 }
497
29ed6e76 498 annotate_browser__set_top(browser, dl, idx);
d3d1f61a
ACM
499 browser->searching_backwards = false;
500 return true;
501}
502
29ed6e76
ACM
503static
504struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
505 char *s, s64 *idx)
d3d1f61a
ACM
506{
507 struct map_symbol *ms = browser->b.priv;
508 struct symbol *sym = ms->sym;
509 struct annotation *notes = symbol__annotation(sym);
29ed6e76 510 struct disasm_line *pos = browser->selection;
d3d1f61a
ACM
511
512 *idx = browser->b.index;
513 list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
29ed6e76 514 if (disasm_line__filter(&browser->b, &pos->node))
d3d1f61a
ACM
515 continue;
516
517 --*idx;
518
519 if (pos->line && strstr(pos->line, s) != NULL)
520 return pos;
521 }
522
523 return NULL;
524}
525
526static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
527{
29ed6e76 528 struct disasm_line *dl;
d3d1f61a
ACM
529 s64 idx;
530
29ed6e76
ACM
531 dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
532 if (dl == NULL) {
d3d1f61a
ACM
533 ui_helpline__puts("String not found!");
534 return false;
535 }
536
29ed6e76 537 annotate_browser__set_top(browser, dl, idx);
d3d1f61a
ACM
538 browser->searching_backwards = true;
539 return true;
540}
541
542static bool annotate_browser__search_window(struct annotate_browser *browser,
543 int delay_secs)
544{
545 if (ui_browser__input_window("Search", "String: ", browser->search_bf,
546 "ENTER: OK, ESC: Cancel",
547 delay_secs * 2) != K_ENTER ||
548 !*browser->search_bf)
549 return false;
550
551 return true;
552}
553
554static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
555{
556 if (annotate_browser__search_window(browser, delay_secs))
557 return __annotate_browser__search(browser);
558
559 return false;
560}
561
562static bool annotate_browser__continue_search(struct annotate_browser *browser,
563 int delay_secs)
564{
565 if (!*browser->search_bf)
566 return annotate_browser__search(browser, delay_secs);
567
568 return __annotate_browser__search(browser);
569}
570
571static bool annotate_browser__search_reverse(struct annotate_browser *browser,
572 int delay_secs)
573{
574 if (annotate_browser__search_window(browser, delay_secs))
575 return __annotate_browser__search_reverse(browser);
576
577 return false;
578}
579
580static
581bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
582 int delay_secs)
583{
584 if (!*browser->search_bf)
585 return annotate_browser__search_reverse(browser, delay_secs);
586
587 return __annotate_browser__search_reverse(browser);
588}
589
e9823b21
ACM
590static void annotate_browser__update_addr_width(struct annotate_browser *browser)
591{
592 if (annotate_browser__opts.use_offset)
593 browser->target_width = browser->min_addr_width;
594 else
595 browser->target_width = browser->max_addr_width;
596
597 browser->addr_width = browser->target_width;
598
599 if (annotate_browser__opts.show_nr_jumps)
600 browser->addr_width += browser->jumps_width + 1;
601}
602
c97cf422 603static int annotate_browser__run(struct annotate_browser *self, int evidx,
d04b35f8 604 void(*timer)(void *arg),
34958544 605 void *arg, int delay_secs)
c97cf422
ACM
606{
607 struct rb_node *nd = NULL;
34958544
ACM
608 struct map_symbol *ms = self->b.priv;
609 struct symbol *sym = ms->sym;
54e7a4e8 610 const char *help = "Press 'h' for help on key bindings";
b50e003d 611 int key;
f1e9214c 612
0361fc25 613 if (ui_browser__show(&self->b, sym->name, help) < 0)
f1e9214c 614 return -1;
c97cf422 615
c97cf422
ACM
616 annotate_browser__calc_percent(self, evidx);
617
d3d1f61a 618 if (self->curr_hot) {
b0ffb2c4 619 annotate_browser__set_rb_top(self, self->curr_hot);
d3d1f61a
ACM
620 self->b.navkeypressed = false;
621 }
f1e9214c
ACM
622
623 nd = self->curr_hot;
c97cf422 624
f1e9214c 625 while (1) {
3af6e338 626 key = ui_browser__run(&self->b, delay_secs);
f1e9214c 627
81cce8de 628 if (delay_secs != 0) {
c97cf422
ACM
629 annotate_browser__calc_percent(self, evidx);
630 /*
631 * Current line focus got out of the list of most active
632 * lines, NULL it so that if TAB|UNTAB is pressed, we
633 * move to curr_hot (current hottest line).
634 */
635 if (nd != NULL && RB_EMPTY_NODE(nd))
636 nd = NULL;
637 }
638
b50e003d 639 switch (key) {
cf958003 640 case K_TIMER:
81cce8de
ACM
641 if (timer != NULL)
642 timer(arg);
643
644 if (delay_secs != 0)
c97cf422
ACM
645 symbol__annotate_decay_histogram(sym, evidx);
646 continue;
cf958003 647 case K_TAB:
c97cf422
ACM
648 if (nd != NULL) {
649 nd = rb_prev(nd);
650 if (nd == NULL)
651 nd = rb_last(&self->entries);
652 } else
653 nd = self->curr_hot;
f1e9214c 654 break;
cf958003 655 case K_UNTAB:
c97cf422
ACM
656 if (nd != NULL)
657 nd = rb_next(nd);
658 if (nd == NULL)
659 nd = rb_first(&self->entries);
660 else
661 nd = self->curr_hot;
662 break;
54e7a4e8 663 case K_F1:
ef7c5372 664 case 'h':
54e7a4e8
ACM
665 ui_browser__help_window(&self->b,
666 "UP/DOWN/PGUP\n"
667 "PGDN/SPACE Navigate\n"
668 "q/ESC/CTRL+C Exit\n\n"
669 "-> Go to target\n"
670 "<- Exit\n"
671 "h Cycle thru hottest instructions\n"
672 "j Toggle showing jump to target arrows\n"
673 "J Toggle showing number of jump sources on targets\n"
674 "n Search next string\n"
675 "o Toggle disassembler output/simplified view\n"
676 "s Toggle source code view\n"
677 "/ Search string\n"
678 "? Search previous string\n");
679 continue;
680 case 'H':
c97cf422 681 nd = self->curr_hot;
f1e9214c 682 break;
ef7c5372 683 case 's':
0361fc25
ACM
684 if (annotate_browser__toggle_source(self))
685 ui_helpline__puts(help);
686 continue;
e235f3f3 687 case 'o':
e9823b21
ACM
688 annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset;
689 annotate_browser__update_addr_width(self);
e235f3f3 690 continue;
9d1ef56d 691 case 'j':
e9823b21 692 annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows;
9d1ef56d 693 continue;
2402e4a9 694 case 'J':
e9823b21
ACM
695 annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps;
696 annotate_browser__update_addr_width(self);
697 continue;
d3d1f61a
ACM
698 case '/':
699 if (annotate_browser__search(self, delay_secs)) {
700show_help:
701 ui_helpline__puts(help);
702 }
703 continue;
704 case 'n':
705 if (self->searching_backwards ?
706 annotate_browser__continue_search_reverse(self, delay_secs) :
707 annotate_browser__continue_search(self, delay_secs))
708 goto show_help;
709 continue;
710 case '?':
711 if (annotate_browser__search_reverse(self, delay_secs))
712 goto show_help;
713 continue;
e9823b21
ACM
714 case 'D': {
715 static int seq;
716 ui_helpline__pop();
717 ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
718 seq++, self->b.nr_entries,
719 self->b.height,
720 self->b.index,
721 self->b.top_idx,
722 self->nr_asm_entries);
723 }
724 continue;
cf958003
ACM
725 case K_ENTER:
726 case K_RIGHT:
60521702 727 if (self->selection == NULL)
234a5375 728 ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
60521702 729 else if (self->selection->offset == -1)
234a5375 730 ui_helpline__puts("Actions are only available for assembly lines.");
c4cceae3
ACM
731 else if (!self->selection->ins) {
732 if (strcmp(self->selection->name, "retq"))
733 goto show_sup_ins;
734 goto out;
735 } else if (!(annotate_browser__jump(self) ||
736 annotate_browser__callq(self, evidx, timer, arg, delay_secs))) {
737show_sup_ins:
738 ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions.");
739 }
fe46e64c 740 continue;
cf958003
ACM
741 case K_LEFT:
742 case K_ESC:
ed7e5662
ACM
743 case 'q':
744 case CTRL('c'):
f1e9214c 745 goto out;
ed7e5662
ACM
746 default:
747 continue;
f1e9214c 748 }
c97cf422
ACM
749
750 if (nd != NULL)
b0ffb2c4 751 annotate_browser__set_rb_top(self, nd);
f1e9214c
ACM
752 }
753out:
59e8fe32 754 ui_browser__hide(&self->b);
b50e003d 755 return key;
f1e9214c
ACM
756}
757
d04b35f8 758int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
81cce8de 759 void(*timer)(void *arg), void *arg, int delay_secs)
78f7defe 760{
d04b35f8 761 return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx,
81cce8de 762 timer, arg, delay_secs);
78f7defe
ACM
763}
764
b793a401
ACM
765static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
766 size_t size)
767{
768 u64 offset;
769
770 for (offset = 0; offset < size; ++offset) {
771 struct disasm_line *dl = browser->offsets[offset], *dlt;
772 struct browser_disasm_line *bdlt;
773
38b31bd0
ACM
774 if (!dl || !dl->ins || !ins__is_jump(dl->ins) ||
775 !disasm_line__has_offset(dl))
b793a401
ACM
776 continue;
777
44d1a3ed 778 if (dl->ops.target.offset >= size) {
b793a401
ACM
779 ui__error("jump to after symbol!\n"
780 "size: %zx, jump target: %" PRIx64,
44d1a3ed 781 size, dl->ops.target.offset);
b793a401
ACM
782 continue;
783 }
784
44d1a3ed 785 dlt = browser->offsets[dl->ops.target.offset];
9481ede9
ACM
786 /*
787 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
788 * have to adjust to the previous offset?
789 */
790 if (dlt == NULL)
791 continue;
792
b793a401 793 bdlt = disasm_line__browser(dlt);
2402e4a9
ACM
794 if (++bdlt->jump_sources > browser->max_jump_sources)
795 browser->max_jump_sources = bdlt->jump_sources;
796
797 ++browser->nr_jumps;
b793a401
ACM
798 }
799
800}
801
2402e4a9
ACM
802static inline int width_jumps(int n)
803{
804 if (n >= 100)
805 return 5;
806 if (n / 10)
807 return 2;
808 return 1;
809}
810
c97cf422 811int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
d04b35f8 812 void(*timer)(void *arg), void *arg,
34958544 813 int delay_secs)
211ef127 814{
29ed6e76 815 struct disasm_line *pos, *n;
db9a9cbc 816 struct annotation *notes;
b793a401 817 const size_t size = symbol__size(sym);
34958544
ACM
818 struct map_symbol ms = {
819 .map = map,
820 .sym = sym,
821 };
92221162
ACM
822 struct annotate_browser browser = {
823 .b = {
a3f895be 824 .refresh = annotate_browser__refresh,
92221162
ACM
825 .seek = ui_browser__list_head_seek,
826 .write = annotate_browser__write,
29ed6e76 827 .filter = disasm_line__filter,
34958544 828 .priv = &ms,
c172f742 829 .use_navkeypressed = true,
92221162 830 },
211ef127 831 };
b793a401 832 int ret = -1;
211ef127 833
78f7defe 834 if (sym == NULL)
211ef127
ACM
835 return -1;
836
78f7defe 837 if (map->dso->annotate_warned)
211ef127
ACM
838 return -1;
839
b793a401
ACM
840 browser.offsets = zalloc(size * sizeof(struct disasm_line *));
841 if (browser.offsets == NULL) {
842 ui__error("Not enough memory!");
843 return -1;
844 }
845
887c0066 846 if (symbol__annotate(sym, map, sizeof(struct browser_disasm_line)) < 0) {
ae55795e 847 ui__error("%s", ui_helpline__last_msg);
b793a401 848 goto out_free_offsets;
211ef127
ACM
849 }
850
851 ui_helpline__push("Press <- or ESC to exit");
852
db9a9cbc 853 notes = symbol__annotation(sym);
058b4cc9 854 browser.start = map__rip_2objdump(map, sym->start);
db9a9cbc 855
ce6f4fab 856 list_for_each_entry(pos, &notes->src->source, node) {
887c0066 857 struct browser_disasm_line *bpos;
211ef127 858 size_t line_len = strlen(pos->line);
c97cf422 859
92221162
ACM
860 if (browser.b.width < line_len)
861 browser.b.width = line_len;
887c0066
ACM
862 bpos = disasm_line__browser(pos);
863 bpos->idx = browser.nr_entries++;
b793a401 864 if (pos->offset != -1) {
887c0066 865 bpos->idx_asm = browser.nr_asm_entries++;
97148a97
ACM
866 /*
867 * FIXME: short term bandaid to cope with assembly
868 * routines that comes with labels in the same column
869 * as the address in objdump, sigh.
870 *
871 * E.g. copy_user_generic_unrolled
872 */
873 if (pos->offset < (s64)size)
874 browser.offsets[pos->offset] = pos;
b793a401 875 } else
887c0066 876 bpos->idx_asm = -1;
92221162
ACM
877 }
878
b793a401
ACM
879 annotate_browser__mark_jump_targets(&browser, size);
880
2402e4a9 881 browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
83b1f2aa 882 browser.max_addr_width = hex_width(sym->end);
2402e4a9 883 browser.jumps_width = width_jumps(browser.max_jump_sources);
0361fc25 884 browser.b.nr_entries = browser.nr_entries;
db9a9cbc 885 browser.b.entries = &notes->src->source,
92221162 886 browser.b.width += 18; /* Percentage */
e9823b21
ACM
887
888 if (annotate_browser__opts.hide_src_code)
889 annotate_browser__init_asm_mode(&browser);
890
891 annotate_browser__update_addr_width(&browser);
892
d04b35f8 893 ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs);
ce6f4fab 894 list_for_each_entry_safe(pos, n, &notes->src->source, node) {
211ef127 895 list_del(&pos->node);
29ed6e76 896 disasm_line__free(pos);
211ef127 897 }
b793a401
ACM
898
899out_free_offsets:
900 free(browser.offsets);
211ef127
ACM
901 return ret;
902}