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