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