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