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