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