aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/util/annotate.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util/annotate.c')
-rw-r--r--tools/perf/util/annotate.c739
1 files changed, 692 insertions, 47 deletions
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index 28b233c3dcbe..3a428d7c59b9 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -14,6 +14,7 @@
14#include "sort.h" 14#include "sort.h"
15#include "build-id.h" 15#include "build-id.h"
16#include "color.h" 16#include "color.h"
17#include "config.h"
17#include "cache.h" 18#include "cache.h"
18#include "symbol.h" 19#include "symbol.h"
19#include "debug.h" 20#include "debug.h"
@@ -27,8 +28,25 @@
27#include <linux/bitops.h> 28#include <linux/bitops.h>
28#include <linux/kernel.h> 29#include <linux/kernel.h>
29 30
31/* FIXME: For the HE_COLORSET */
32#include "ui/browser.h"
33
34/*
35 * FIXME: Using the same values as slang.h,
36 * but that header may not be available everywhere
37 */
38#define LARROW_CHAR ((unsigned char)',')
39#define RARROW_CHAR ((unsigned char)'+')
40#define DARROW_CHAR ((unsigned char)'.')
41#define UARROW_CHAR ((unsigned char)'-')
42
30#include "sane_ctype.h" 43#include "sane_ctype.h"
31 44
45struct annotation_options annotation__default_options = {
46 .use_offset = true,
47 .jump_arrows = true,
48};
49
32const char *disassembler_style; 50const char *disassembler_style;
33const char *objdump_path; 51const char *objdump_path;
34static regex_t file_lineno; 52static regex_t file_lineno;
@@ -184,9 +202,13 @@ bool ins__is_fused(struct arch *arch, const char *ins1, const char *ins2)
184 return arch->ins_is_fused(arch, ins1, ins2); 202 return arch->ins_is_fused(arch, ins1, ins2);
185} 203}
186 204
187static int call__parse(struct arch *arch, struct ins_operands *ops, struct map *map) 205static int call__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms)
188{ 206{
189 char *endptr, *tok, *name; 207 char *endptr, *tok, *name;
208 struct map *map = ms->map;
209 struct addr_map_symbol target = {
210 .map = map,
211 };
190 212
191 ops->target.addr = strtoull(ops->raw, &endptr, 16); 213 ops->target.addr = strtoull(ops->raw, &endptr, 16);
192 214
@@ -208,32 +230,36 @@ static int call__parse(struct arch *arch, struct ins_operands *ops, struct map *
208 ops->target.name = strdup(name); 230 ops->target.name = strdup(name);
209 *tok = '>'; 231 *tok = '>';
210 232
211 return ops->target.name == NULL ? -1 : 0; 233 if (ops->target.name == NULL)
234 return -1;
235find_target:
236 target.addr = map__objdump_2mem(map, ops->target.addr);
212 237
213indirect_call: 238 if (map_groups__find_ams(&target) == 0 &&
214 tok = strchr(endptr, '*'); 239 map__rip_2objdump(target.map, map->map_ip(target.map, target.addr)) == ops->target.addr)
215 if (tok == NULL) { 240 ops->target.sym = target.sym;
216 struct symbol *sym = map__find_symbol(map, map->map_ip(map, ops->target.addr));
217 if (sym != NULL)
218 ops->target.name = strdup(sym->name);
219 else
220 ops->target.addr = 0;
221 return 0;
222 }
223 241
224 ops->target.addr = strtoull(tok + 1, NULL, 16);
225 return 0; 242 return 0;
243
244indirect_call:
245 tok = strchr(endptr, '*');
246 if (tok != NULL)
247 ops->target.addr = strtoull(tok + 1, NULL, 16);
248 goto find_target;
226} 249}
227 250
228static int call__scnprintf(struct ins *ins, char *bf, size_t size, 251static int call__scnprintf(struct ins *ins, char *bf, size_t size,
229 struct ins_operands *ops) 252 struct ins_operands *ops)
230{ 253{
231 if (ops->target.name) 254 if (ops->target.sym)
232 return scnprintf(bf, size, "%-6s %s", ins->name, ops->target.name); 255 return scnprintf(bf, size, "%-6s %s", ins->name, ops->target.sym->name);
233 256
234 if (ops->target.addr == 0) 257 if (ops->target.addr == 0)
235 return ins__raw_scnprintf(ins, bf, size, ops); 258 return ins__raw_scnprintf(ins, bf, size, ops);
236 259
260 if (ops->target.name)
261 return scnprintf(bf, size, "%-6s %s", ins->name, ops->target.name);
262
237 return scnprintf(bf, size, "%-6s *%" PRIx64, ins->name, ops->target.addr); 263 return scnprintf(bf, size, "%-6s *%" PRIx64, ins->name, ops->target.addr);
238} 264}
239 265
@@ -244,14 +270,29 @@ static struct ins_ops call_ops = {
244 270
245bool ins__is_call(const struct ins *ins) 271bool ins__is_call(const struct ins *ins)
246{ 272{
247 return ins->ops == &call_ops; 273 return ins->ops == &call_ops || ins->ops == &s390_call_ops;
248} 274}
249 275
250static int jump__parse(struct arch *arch __maybe_unused, struct ins_operands *ops, struct map *map __maybe_unused) 276static int jump__parse(struct arch *arch __maybe_unused, struct ins_operands *ops, struct map_symbol *ms)
251{ 277{
252 const char *s = strchr(ops->raw, '+'); 278 struct map *map = ms->map;
279 struct symbol *sym = ms->sym;
280 struct addr_map_symbol target = {
281 .map = map,
282 };
253 const char *c = strchr(ops->raw, ','); 283 const char *c = strchr(ops->raw, ',');
254 284 u64 start, end;
285 /*
286 * Examples of lines to parse for the _cpp_lex_token@@Base
287 * function:
288 *
289 * 1159e6c: jne 115aa32 <_cpp_lex_token@@Base+0xf92>
290 * 1159e8b: jne c469be <cpp_named_operator2name@@Base+0xa72>
291 *
292 * The first is a jump to an offset inside the same function,
293 * the second is to another function, i.e. that 0xa72 is an
294 * offset in the cpp_named_operator2name@@base function.
295 */
255 /* 296 /*
256 * skip over possible up to 2 operands to get to address, e.g.: 297 * skip over possible up to 2 operands to get to address, e.g.:
257 * tbnz w0, #26, ffff0000083cd190 <security_file_permission+0xd0> 298 * tbnz w0, #26, ffff0000083cd190 <security_file_permission+0xd0>
@@ -267,8 +308,36 @@ static int jump__parse(struct arch *arch __maybe_unused, struct ins_operands *op
267 ops->target.addr = strtoull(ops->raw, NULL, 16); 308 ops->target.addr = strtoull(ops->raw, NULL, 16);
268 } 309 }
269 310
270 if (s++ != NULL) { 311 target.addr = map__objdump_2mem(map, ops->target.addr);
271 ops->target.offset = strtoull(s, NULL, 16); 312 start = map->unmap_ip(map, sym->start),
313 end = map->unmap_ip(map, sym->end);
314
315 ops->target.outside = target.addr < start || target.addr > end;
316
317 /*
318 * FIXME: things like this in _cpp_lex_token (gcc's cc1 program):
319
320 cpp_named_operator2name@@Base+0xa72
321
322 * Point to a place that is after the cpp_named_operator2name
323 * boundaries, i.e. in the ELF symbol table for cc1
324 * cpp_named_operator2name is marked as being 32-bytes long, but it in
325 * fact is much larger than that, so we seem to need a symbols__find()
326 * routine that looks for >= current->start and < next_symbol->start,
327 * possibly just for C++ objects?
328 *
329 * For now lets just make some progress by marking jumps to outside the
330 * current function as call like.
331 *
332 * Actual navigation will come next, with further understanding of how
333 * the symbol searching and disassembly should be done.
334 */
335 if (map_groups__find_ams(&target) == 0 &&
336 map__rip_2objdump(target.map, map->map_ip(target.map, target.addr)) == ops->target.addr)
337 ops->target.sym = target.sym;
338
339 if (!ops->target.outside) {
340 ops->target.offset = target.addr - start;
272 ops->target.offset_avail = true; 341 ops->target.offset_avail = true;
273 } else { 342 } else {
274 ops->target.offset_avail = false; 343 ops->target.offset_avail = false;
@@ -280,11 +349,15 @@ static int jump__parse(struct arch *arch __maybe_unused, struct ins_operands *op
280static int jump__scnprintf(struct ins *ins, char *bf, size_t size, 349static int jump__scnprintf(struct ins *ins, char *bf, size_t size,
281 struct ins_operands *ops) 350 struct ins_operands *ops)
282{ 351{
283 const char *c = strchr(ops->raw, ','); 352 const char *c;
284 353
285 if (!ops->target.addr || ops->target.offset < 0) 354 if (!ops->target.addr || ops->target.offset < 0)
286 return ins__raw_scnprintf(ins, bf, size, ops); 355 return ins__raw_scnprintf(ins, bf, size, ops);
287 356
357 if (ops->target.outside && ops->target.sym != NULL)
358 return scnprintf(bf, size, "%-6s %s", ins->name, ops->target.sym->name);
359
360 c = strchr(ops->raw, ',');
288 if (c != NULL) { 361 if (c != NULL) {
289 const char *c2 = strchr(c + 1, ','); 362 const char *c2 = strchr(c + 1, ',');
290 363
@@ -340,7 +413,7 @@ static int comment__symbol(char *raw, char *comment, u64 *addrp, char **namep)
340 return 0; 413 return 0;
341} 414}
342 415
343static int lock__parse(struct arch *arch, struct ins_operands *ops, struct map *map) 416static int lock__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms)
344{ 417{
345 ops->locked.ops = zalloc(sizeof(*ops->locked.ops)); 418 ops->locked.ops = zalloc(sizeof(*ops->locked.ops));
346 if (ops->locked.ops == NULL) 419 if (ops->locked.ops == NULL)
@@ -355,7 +428,7 @@ static int lock__parse(struct arch *arch, struct ins_operands *ops, struct map *
355 goto out_free_ops; 428 goto out_free_ops;
356 429
357 if (ops->locked.ins.ops->parse && 430 if (ops->locked.ins.ops->parse &&
358 ops->locked.ins.ops->parse(arch, ops->locked.ops, map) < 0) 431 ops->locked.ins.ops->parse(arch, ops->locked.ops, ms) < 0)
359 goto out_free_ops; 432 goto out_free_ops;
360 433
361 return 0; 434 return 0;
@@ -398,7 +471,7 @@ static struct ins_ops lock_ops = {
398 .scnprintf = lock__scnprintf, 471 .scnprintf = lock__scnprintf,
399}; 472};
400 473
401static int mov__parse(struct arch *arch, struct ins_operands *ops, struct map *map __maybe_unused) 474static int mov__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms __maybe_unused)
402{ 475{
403 char *s = strchr(ops->raw, ','), *target, *comment, prev; 476 char *s = strchr(ops->raw, ','), *target, *comment, prev;
404 477
@@ -459,7 +532,7 @@ static struct ins_ops mov_ops = {
459 .scnprintf = mov__scnprintf, 532 .scnprintf = mov__scnprintf,
460}; 533};
461 534
462static int dec__parse(struct arch *arch __maybe_unused, struct ins_operands *ops, struct map *map __maybe_unused) 535static int dec__parse(struct arch *arch __maybe_unused, struct ins_operands *ops, struct map_symbol *ms __maybe_unused)
463{ 536{
464 char *target, *comment, *s, prev; 537 char *target, *comment, *s, prev;
465 538
@@ -826,6 +899,66 @@ int addr_map_symbol__account_cycles(struct addr_map_symbol *ams,
826 return err; 899 return err;
827} 900}
828 901
902static unsigned annotation__count_insn(struct annotation *notes, u64 start, u64 end)
903{
904 unsigned n_insn = 0;
905 u64 offset;
906
907 for (offset = start; offset <= end; offset++) {
908 if (notes->offsets[offset])
909 n_insn++;
910 }
911 return n_insn;
912}
913
914static void annotation__count_and_fill(struct annotation *notes, u64 start, u64 end, struct cyc_hist *ch)
915{
916 unsigned n_insn;
917 u64 offset;
918
919 n_insn = annotation__count_insn(notes, start, end);
920 if (n_insn && ch->num && ch->cycles) {
921 float ipc = n_insn / ((double)ch->cycles / (double)ch->num);
922
923 /* Hide data when there are too many overlaps. */
924 if (ch->reset >= 0x7fff || ch->reset >= ch->num / 2)
925 return;
926
927 for (offset = start; offset <= end; offset++) {
928 struct annotation_line *al = notes->offsets[offset];
929
930 if (al)
931 al->ipc = ipc;
932 }
933 }
934}
935
936void annotation__compute_ipc(struct annotation *notes, size_t size)
937{
938 u64 offset;
939
940 if (!notes->src || !notes->src->cycles_hist)
941 return;
942
943 pthread_mutex_lock(&notes->lock);
944 for (offset = 0; offset < size; ++offset) {
945 struct cyc_hist *ch;
946
947 ch = &notes->src->cycles_hist[offset];
948 if (ch && ch->cycles) {
949 struct annotation_line *al;
950
951 if (ch->have_start)
952 annotation__count_and_fill(notes, ch->start, offset, ch);
953 al = notes->offsets[offset];
954 if (al && ch->num_aggr)
955 al->cycles = ch->cycles_aggr / ch->num_aggr;
956 notes->have_cycles = true;
957 }
958 }
959 pthread_mutex_unlock(&notes->lock);
960}
961
829int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, struct perf_sample *sample, 962int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, struct perf_sample *sample,
830 int evidx) 963 int evidx)
831{ 964{
@@ -838,14 +971,14 @@ int hist_entry__inc_addr_samples(struct hist_entry *he, struct perf_sample *samp
838 return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip, sample); 971 return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip, sample);
839} 972}
840 973
841static void disasm_line__init_ins(struct disasm_line *dl, struct arch *arch, struct map *map) 974static void disasm_line__init_ins(struct disasm_line *dl, struct arch *arch, struct map_symbol *ms)
842{ 975{
843 dl->ins.ops = ins__find(arch, dl->ins.name); 976 dl->ins.ops = ins__find(arch, dl->ins.name);
844 977
845 if (!dl->ins.ops) 978 if (!dl->ins.ops)
846 return; 979 return;
847 980
848 if (dl->ins.ops->parse && dl->ins.ops->parse(arch, &dl->ops, map) < 0) 981 if (dl->ins.ops->parse && dl->ins.ops->parse(arch, &dl->ops, ms) < 0)
849 dl->ins.ops = NULL; 982 dl->ins.ops = NULL;
850} 983}
851 984
@@ -882,7 +1015,7 @@ out_free_name:
882struct annotate_args { 1015struct annotate_args {
883 size_t privsize; 1016 size_t privsize;
884 struct arch *arch; 1017 struct arch *arch;
885 struct map *map; 1018 struct map_symbol ms;
886 struct perf_evsel *evsel; 1019 struct perf_evsel *evsel;
887 s64 offset; 1020 s64 offset;
888 char *line; 1021 char *line;
@@ -964,7 +1097,7 @@ static struct disasm_line *disasm_line__new(struct annotate_args *args)
964 if (disasm_line__parse(dl->al.line, &dl->ins.name, &dl->ops.raw) < 0) 1097 if (disasm_line__parse(dl->al.line, &dl->ins.name, &dl->ops.raw) < 0)
965 goto out_free_line; 1098 goto out_free_line;
966 1099
967 disasm_line__init_ins(dl, args->arch, args->map); 1100 disasm_line__init_ins(dl, args->arch, &args->ms);
968 } 1101 }
969 } 1102 }
970 1103
@@ -1222,7 +1355,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, FILE *file,
1222 struct annotate_args *args, 1355 struct annotate_args *args,
1223 int *line_nr) 1356 int *line_nr)
1224{ 1357{
1225 struct map *map = args->map; 1358 struct map *map = args->ms.map;
1226 struct annotation *notes = symbol__annotation(sym); 1359 struct annotation *notes = symbol__annotation(sym);
1227 struct disasm_line *dl; 1360 struct disasm_line *dl;
1228 char *line = NULL, *parsed_line, *tmp, *tmp2; 1361 char *line = NULL, *parsed_line, *tmp, *tmp2;
@@ -1269,6 +1402,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, FILE *file,
1269 args->offset = offset; 1402 args->offset = offset;
1270 args->line = parsed_line; 1403 args->line = parsed_line;
1271 args->line_nr = *line_nr; 1404 args->line_nr = *line_nr;
1405 args->ms.sym = sym;
1272 1406
1273 dl = disasm_line__new(args); 1407 dl = disasm_line__new(args);
1274 free(line); 1408 free(line);
@@ -1277,14 +1411,14 @@ static int symbol__parse_objdump_line(struct symbol *sym, FILE *file,
1277 if (dl == NULL) 1411 if (dl == NULL)
1278 return -1; 1412 return -1;
1279 1413
1280 if (!disasm_line__has_offset(dl)) { 1414 if (!disasm_line__has_local_offset(dl)) {
1281 dl->ops.target.offset = dl->ops.target.addr - 1415 dl->ops.target.offset = dl->ops.target.addr -
1282 map__rip_2objdump(map, sym->start); 1416 map__rip_2objdump(map, sym->start);
1283 dl->ops.target.offset_avail = true; 1417 dl->ops.target.offset_avail = true;
1284 } 1418 }
1285 1419
1286 /* kcore has no symbols, so add the call target name */ 1420 /* kcore has no symbols, so add the call target symbol */
1287 if (dl->ins.ops && ins__is_call(&dl->ins) && !dl->ops.target.name) { 1421 if (dl->ins.ops && ins__is_call(&dl->ins) && !dl->ops.target.sym) {
1288 struct addr_map_symbol target = { 1422 struct addr_map_symbol target = {
1289 .map = map, 1423 .map = map,
1290 .addr = dl->ops.target.addr, 1424 .addr = dl->ops.target.addr,
@@ -1292,7 +1426,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, FILE *file,
1292 1426
1293 if (!map_groups__find_ams(&target) && 1427 if (!map_groups__find_ams(&target) &&
1294 target.sym->start == target.al_addr) 1428 target.sym->start == target.al_addr)
1295 dl->ops.target.name = strdup(target.sym->name); 1429 dl->ops.target.sym = target.sym;
1296 } 1430 }
1297 1431
1298 annotation_line__add(&dl->al, &notes->src->source); 1432 annotation_line__add(&dl->al, &notes->src->source);
@@ -1421,9 +1555,9 @@ fallback:
1421 1555
1422static int symbol__disassemble(struct symbol *sym, struct annotate_args *args) 1556static int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
1423{ 1557{
1424 struct map *map = args->map; 1558 struct map *map = args->ms.map;
1425 struct dso *dso = map->dso; 1559 struct dso *dso = map->dso;
1426 char command[PATH_MAX * 2]; 1560 char *command;
1427 FILE *file; 1561 FILE *file;
1428 char symfs_filename[PATH_MAX]; 1562 char symfs_filename[PATH_MAX];
1429 struct kcore_extract kce; 1563 struct kcore_extract kce;
@@ -1464,7 +1598,7 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
1464 strcpy(symfs_filename, tmp); 1598 strcpy(symfs_filename, tmp);
1465 } 1599 }
1466 1600
1467 snprintf(command, sizeof(command), 1601 err = asprintf(&command,
1468 "%s %s%s --start-address=0x%016" PRIx64 1602 "%s %s%s --start-address=0x%016" PRIx64
1469 " --stop-address=0x%016" PRIx64 1603 " --stop-address=0x%016" PRIx64
1470 " -l -d %s %s -C \"%s\" 2>/dev/null|grep -v \"%s:\"|expand", 1604 " -l -d %s %s -C \"%s\" 2>/dev/null|grep -v \"%s:\"|expand",
@@ -1477,12 +1611,17 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
1477 symbol_conf.annotate_src ? "-S" : "", 1611 symbol_conf.annotate_src ? "-S" : "",
1478 symfs_filename, symfs_filename); 1612 symfs_filename, symfs_filename);
1479 1613
1614 if (err < 0) {
1615 pr_err("Failure allocating memory for the command to run\n");
1616 goto out_remove_tmp;
1617 }
1618
1480 pr_debug("Executing: %s\n", command); 1619 pr_debug("Executing: %s\n", command);
1481 1620
1482 err = -1; 1621 err = -1;
1483 if (pipe(stdout_fd) < 0) { 1622 if (pipe(stdout_fd) < 0) {
1484 pr_err("Failure creating the pipe to run %s\n", command); 1623 pr_err("Failure creating the pipe to run %s\n", command);
1485 goto out_remove_tmp; 1624 goto out_free_command;
1486 } 1625 }
1487 1626
1488 pid = fork(); 1627 pid = fork();
@@ -1509,7 +1648,7 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
1509 * If we were using debug info should retry with 1648 * If we were using debug info should retry with
1510 * original binary. 1649 * original binary.
1511 */ 1650 */
1512 goto out_remove_tmp; 1651 goto out_free_command;
1513 } 1652 }
1514 1653
1515 nline = 0; 1654 nline = 0;
@@ -1537,6 +1676,8 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
1537 1676
1538 fclose(file); 1677 fclose(file);
1539 err = 0; 1678 err = 0;
1679out_free_command:
1680 free(command);
1540out_remove_tmp: 1681out_remove_tmp:
1541 close(stdout_fd[0]); 1682 close(stdout_fd[0]);
1542 1683
@@ -1550,7 +1691,7 @@ out:
1550 1691
1551out_close_stdout: 1692out_close_stdout:
1552 close(stdout_fd[1]); 1693 close(stdout_fd[1]);
1553 goto out_remove_tmp; 1694 goto out_free_command;
1554} 1695}
1555 1696
1556static void calc_percent(struct sym_hist *hist, 1697static void calc_percent(struct sym_hist *hist,
@@ -1613,7 +1754,6 @@ int symbol__annotate(struct symbol *sym, struct map *map,
1613{ 1754{
1614 struct annotate_args args = { 1755 struct annotate_args args = {
1615 .privsize = privsize, 1756 .privsize = privsize,
1616 .map = map,
1617 .evsel = evsel, 1757 .evsel = evsel,
1618 }; 1758 };
1619 struct perf_env *env = perf_evsel__env(evsel); 1759 struct perf_env *env = perf_evsel__env(evsel);
@@ -1639,6 +1779,9 @@ int symbol__annotate(struct symbol *sym, struct map *map,
1639 } 1779 }
1640 } 1780 }
1641 1781
1782 args.ms.map = map;
1783 args.ms.sym = sym;
1784
1642 return symbol__disassemble(sym, &args); 1785 return symbol__disassemble(sym, &args);
1643} 1786}
1644 1787
@@ -1879,6 +2022,103 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map,
1879 return more; 2022 return more;
1880} 2023}
1881 2024
2025static void FILE__set_percent_color(void *fp __maybe_unused,
2026 double percent __maybe_unused,
2027 bool current __maybe_unused)
2028{
2029}
2030
2031static int FILE__set_jumps_percent_color(void *fp __maybe_unused,
2032 int nr __maybe_unused, bool current __maybe_unused)
2033{
2034 return 0;
2035}
2036
2037static int FILE__set_color(void *fp __maybe_unused, int color __maybe_unused)
2038{
2039 return 0;
2040}
2041
2042static void FILE__printf(void *fp, const char *fmt, ...)
2043{
2044 va_list args;
2045
2046 va_start(args, fmt);
2047 vfprintf(fp, fmt, args);
2048 va_end(args);
2049}
2050
2051static void FILE__write_graph(void *fp, int graph)
2052{
2053 const char *s;
2054 switch (graph) {
2055
2056 case DARROW_CHAR: s = "↓"; break;
2057 case UARROW_CHAR: s = "↑"; break;
2058 case LARROW_CHAR: s = "←"; break;
2059 case RARROW_CHAR: s = "→"; break;
2060 default: s = "?"; break;
2061 }
2062
2063 fputs(s, fp);
2064}
2065
2066int symbol__annotate_fprintf2(struct symbol *sym, FILE *fp)
2067{
2068 struct annotation *notes = symbol__annotation(sym);
2069 struct annotation_write_ops ops = {
2070 .first_line = true,
2071 .obj = fp,
2072 .set_color = FILE__set_color,
2073 .set_percent_color = FILE__set_percent_color,
2074 .set_jumps_percent_color = FILE__set_jumps_percent_color,
2075 .printf = FILE__printf,
2076 .write_graph = FILE__write_graph,
2077 };
2078 struct annotation_line *al;
2079
2080 list_for_each_entry(al, &notes->src->source, node) {
2081 if (annotation_line__filter(al, notes))
2082 continue;
2083 annotation_line__write(al, notes, &ops);
2084 fputc('\n', fp);
2085 ops.first_line = false;
2086 }
2087
2088 return 0;
2089}
2090
2091int map_symbol__annotation_dump(struct map_symbol *ms, struct perf_evsel *evsel)
2092{
2093 const char *ev_name = perf_evsel__name(evsel);
2094 char buf[1024];
2095 char *filename;
2096 int err = -1;
2097 FILE *fp;
2098
2099 if (asprintf(&filename, "%s.annotation", ms->sym->name) < 0)
2100 return -1;
2101
2102 fp = fopen(filename, "w");
2103 if (fp == NULL)
2104 goto out_free_filename;
2105
2106 if (perf_evsel__is_group_event(evsel)) {
2107 perf_evsel__group_desc(evsel, buf, sizeof(buf));
2108 ev_name = buf;
2109 }
2110
2111 fprintf(fp, "%s() %s\nEvent: %s\n\n",
2112 ms->sym->name, ms->map->dso->long_name, ev_name);
2113 symbol__annotate_fprintf2(ms->sym, fp);
2114
2115 fclose(fp);
2116 err = 0;
2117out_free_filename:
2118 free(filename);
2119 return err;
2120}
2121
1882void symbol__annotate_zero_histogram(struct symbol *sym, int evidx) 2122void symbol__annotate_zero_histogram(struct symbol *sym, int evidx)
1883{ 2123{
1884 struct annotation *notes = symbol__annotation(sym); 2124 struct annotation *notes = symbol__annotation(sym);
@@ -1938,8 +2178,109 @@ size_t disasm__fprintf(struct list_head *head, FILE *fp)
1938 return printed; 2178 return printed;
1939} 2179}
1940 2180
2181bool disasm_line__is_valid_local_jump(struct disasm_line *dl, struct symbol *sym)
2182{
2183 if (!dl || !dl->ins.ops || !ins__is_jump(&dl->ins) ||
2184 !disasm_line__has_local_offset(dl) || dl->ops.target.offset < 0 ||
2185 dl->ops.target.offset >= (s64)symbol__size(sym))
2186 return false;
2187
2188 return true;
2189}
2190
2191void annotation__mark_jump_targets(struct annotation *notes, struct symbol *sym)
2192{
2193 u64 offset, size = symbol__size(sym);
2194
2195 /* PLT symbols contain external offsets */
2196 if (strstr(sym->name, "@plt"))
2197 return;
2198
2199 for (offset = 0; offset < size; ++offset) {
2200 struct annotation_line *al = notes->offsets[offset];
2201 struct disasm_line *dl;
2202
2203 dl = disasm_line(al);
2204
2205 if (!disasm_line__is_valid_local_jump(dl, sym))
2206 continue;
2207
2208 al = notes->offsets[dl->ops.target.offset];
2209
2210 /*
2211 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
2212 * have to adjust to the previous offset?
2213 */
2214 if (al == NULL)
2215 continue;
2216
2217 if (++al->jump_sources > notes->max_jump_sources)
2218 notes->max_jump_sources = al->jump_sources;
2219
2220 ++notes->nr_jumps;
2221 }
2222}
2223
2224void annotation__set_offsets(struct annotation *notes, s64 size)
2225{
2226 struct annotation_line *al;
2227
2228 notes->max_line_len = 0;
2229
2230 list_for_each_entry(al, &notes->src->source, node) {
2231 size_t line_len = strlen(al->line);
2232
2233 if (notes->max_line_len < line_len)
2234 notes->max_line_len = line_len;
2235 al->idx = notes->nr_entries++;
2236 if (al->offset != -1) {
2237 al->idx_asm = notes->nr_asm_entries++;
2238 /*
2239 * FIXME: short term bandaid to cope with assembly
2240 * routines that comes with labels in the same column
2241 * as the address in objdump, sigh.
2242 *
2243 * E.g. copy_user_generic_unrolled
2244 */
2245 if (al->offset < size)
2246 notes->offsets[al->offset] = al;
2247 } else
2248 al->idx_asm = -1;
2249 }
2250}
2251
2252static inline int width_jumps(int n)
2253{
2254 if (n >= 100)
2255 return 5;
2256 if (n / 10)
2257 return 2;
2258 return 1;
2259}
2260
2261void annotation__init_column_widths(struct annotation *notes, struct symbol *sym)
2262{
2263 notes->widths.addr = notes->widths.target =
2264 notes->widths.min_addr = hex_width(symbol__size(sym));
2265 notes->widths.max_addr = hex_width(sym->end);
2266 notes->widths.jumps = width_jumps(notes->max_jump_sources);
2267}
2268
2269void annotation__update_column_widths(struct annotation *notes)
2270{
2271 if (notes->options->use_offset)
2272 notes->widths.target = notes->widths.min_addr;
2273 else
2274 notes->widths.target = notes->widths.max_addr;
2275
2276 notes->widths.addr = notes->widths.target;
2277
2278 if (notes->options->show_nr_jumps)
2279 notes->widths.addr += notes->widths.jumps + 1;
2280}
2281
1941static void annotation__calc_lines(struct annotation *notes, struct map *map, 2282static void annotation__calc_lines(struct annotation *notes, struct map *map,
1942 struct rb_root *root, u64 start) 2283 struct rb_root *root)
1943{ 2284{
1944 struct annotation_line *al; 2285 struct annotation_line *al;
1945 struct rb_root tmp_root = RB_ROOT; 2286 struct rb_root tmp_root = RB_ROOT;
@@ -1960,8 +2301,8 @@ static void annotation__calc_lines(struct annotation *notes, struct map *map,
1960 if (percent_max <= 0.5) 2301 if (percent_max <= 0.5)
1961 continue; 2302 continue;
1962 2303
1963 al->path = get_srcline(map->dso, start + al->offset, NULL, 2304 al->path = get_srcline(map->dso, notes->start + al->offset, NULL,
1964 false, true, start + al->offset); 2305 false, true, notes->start + al->offset);
1965 insert_source_line(&tmp_root, al); 2306 insert_source_line(&tmp_root, al);
1966 } 2307 }
1967 2308
@@ -1972,9 +2313,40 @@ static void symbol__calc_lines(struct symbol *sym, struct map *map,
1972 struct rb_root *root) 2313 struct rb_root *root)
1973{ 2314{
1974 struct annotation *notes = symbol__annotation(sym); 2315 struct annotation *notes = symbol__annotation(sym);
1975 u64 start = map__rip_2objdump(map, sym->start);
1976 2316
1977 annotation__calc_lines(notes, map, root, start); 2317 annotation__calc_lines(notes, map, root);
2318}
2319
2320int symbol__tty_annotate2(struct symbol *sym, struct map *map,
2321 struct perf_evsel *evsel, bool print_lines,
2322 bool full_paths)
2323{
2324 struct dso *dso = map->dso;
2325 struct rb_root source_line = RB_ROOT;
2326 struct annotation_options opts = annotation__default_options;
2327 const char *ev_name = perf_evsel__name(evsel);
2328 char buf[1024];
2329
2330 if (symbol__annotate2(sym, map, evsel, &opts, NULL) < 0)
2331 return -1;
2332
2333 if (print_lines) {
2334 srcline_full_filename = full_paths;
2335 symbol__calc_lines(sym, map, &source_line);
2336 print_summary(&source_line, dso->long_name);
2337 }
2338
2339 if (perf_evsel__is_group_event(evsel)) {
2340 perf_evsel__group_desc(evsel, buf, sizeof(buf));
2341 ev_name = buf;
2342 }
2343
2344 fprintf(stdout, "%s() %s\nEvent: %s\n\n", sym->name, dso->long_name, ev_name);
2345 symbol__annotate_fprintf2(sym, stdout);
2346
2347 annotated_source__purge(symbol__annotation(sym)->src);
2348
2349 return 0;
1978} 2350}
1979 2351
1980int symbol__tty_annotate(struct symbol *sym, struct map *map, 2352int symbol__tty_annotate(struct symbol *sym, struct map *map,
@@ -2007,3 +2379,276 @@ bool ui__has_annotation(void)
2007{ 2379{
2008 return use_browser == 1 && perf_hpp_list.sym; 2380 return use_browser == 1 && perf_hpp_list.sym;
2009} 2381}
2382
2383
2384double annotation_line__max_percent(struct annotation_line *al, struct annotation *notes)
2385{
2386 double percent_max = 0.0;
2387 int i;
2388
2389 for (i = 0; i < notes->nr_events; i++) {
2390 if (al->samples[i].percent > percent_max)
2391 percent_max = al->samples[i].percent;
2392 }
2393
2394 return percent_max;
2395}
2396
2397static void disasm_line__write(struct disasm_line *dl, struct annotation *notes,
2398 void *obj, char *bf, size_t size,
2399 void (*obj__printf)(void *obj, const char *fmt, ...),
2400 void (*obj__write_graph)(void *obj, int graph))
2401{
2402 if (dl->ins.ops && dl->ins.ops->scnprintf) {
2403 if (ins__is_jump(&dl->ins)) {
2404 bool fwd;
2405
2406 if (dl->ops.target.outside)
2407 goto call_like;
2408 fwd = dl->ops.target.offset > dl->al.offset;
2409 obj__write_graph(obj, fwd ? DARROW_CHAR : UARROW_CHAR);
2410 obj__printf(obj, " ");
2411 } else if (ins__is_call(&dl->ins)) {
2412call_like:
2413 obj__write_graph(obj, RARROW_CHAR);
2414 obj__printf(obj, " ");
2415 } else if (ins__is_ret(&dl->ins)) {
2416 obj__write_graph(obj, LARROW_CHAR);
2417 obj__printf(obj, " ");
2418 } else {
2419 obj__printf(obj, " ");
2420 }
2421 } else {
2422 obj__printf(obj, " ");
2423 }
2424
2425 disasm_line__scnprintf(dl, bf, size, !notes->options->use_offset);
2426}
2427
2428static void __annotation_line__write(struct annotation_line *al, struct annotation *notes,
2429 bool first_line, bool current_entry, bool change_color, int width,
2430 void *obj,
2431 int (*obj__set_color)(void *obj, int color),
2432 void (*obj__set_percent_color)(void *obj, double percent, bool current),
2433 int (*obj__set_jumps_percent_color)(void *obj, int nr, bool current),
2434 void (*obj__printf)(void *obj, const char *fmt, ...),
2435 void (*obj__write_graph)(void *obj, int graph))
2436
2437{
2438 double percent_max = annotation_line__max_percent(al, notes);
2439 int pcnt_width = annotation__pcnt_width(notes),
2440 cycles_width = annotation__cycles_width(notes);
2441 bool show_title = false;
2442 char bf[256];
2443 int printed;
2444
2445 if (first_line && (al->offset == -1 || percent_max == 0.0)) {
2446 if (notes->have_cycles) {
2447 if (al->ipc == 0.0 && al->cycles == 0)
2448 show_title = true;
2449 } else
2450 show_title = true;
2451 }
2452
2453 if (al->offset != -1 && percent_max != 0.0) {
2454 int i;
2455
2456 for (i = 0; i < notes->nr_events; i++) {
2457 obj__set_percent_color(obj, al->samples[i].percent, current_entry);
2458 if (notes->options->show_total_period) {
2459 obj__printf(obj, "%11" PRIu64 " ", al->samples[i].he.period);
2460 } else if (notes->options->show_nr_samples) {
2461 obj__printf(obj, "%6" PRIu64 " ",
2462 al->samples[i].he.nr_samples);
2463 } else {
2464 obj__printf(obj, "%6.2f ",
2465 al->samples[i].percent);
2466 }
2467 }
2468 } else {
2469 obj__set_percent_color(obj, 0, current_entry);
2470
2471 if (!show_title)
2472 obj__printf(obj, "%-*s", pcnt_width, " ");
2473 else {
2474 obj__printf(obj, "%-*s", pcnt_width,
2475 notes->options->show_total_period ? "Period" :
2476 notes->options->show_nr_samples ? "Samples" : "Percent");
2477 }
2478 }
2479
2480 if (notes->have_cycles) {
2481 if (al->ipc)
2482 obj__printf(obj, "%*.2f ", ANNOTATION__IPC_WIDTH - 1, al->ipc);
2483 else if (!show_title)
2484 obj__printf(obj, "%*s", ANNOTATION__IPC_WIDTH, " ");
2485 else
2486 obj__printf(obj, "%*s ", ANNOTATION__IPC_WIDTH - 1, "IPC");
2487
2488 if (al->cycles)
2489 obj__printf(obj, "%*" PRIu64 " ",
2490 ANNOTATION__CYCLES_WIDTH - 1, al->cycles);
2491 else if (!show_title)
2492 obj__printf(obj, "%*s", ANNOTATION__CYCLES_WIDTH, " ");
2493 else
2494 obj__printf(obj, "%*s ", ANNOTATION__CYCLES_WIDTH - 1, "Cycle");
2495 }
2496
2497 obj__printf(obj, " ");
2498
2499 if (!*al->line)
2500 obj__printf(obj, "%-*s", width - pcnt_width - cycles_width, " ");
2501 else if (al->offset == -1) {
2502 if (al->line_nr && notes->options->show_linenr)
2503 printed = scnprintf(bf, sizeof(bf), "%-*d ", notes->widths.addr + 1, al->line_nr);
2504 else
2505 printed = scnprintf(bf, sizeof(bf), "%-*s ", notes->widths.addr, " ");
2506 obj__printf(obj, bf);
2507 obj__printf(obj, "%-*s", width - printed - pcnt_width - cycles_width + 1, al->line);
2508 } else {
2509 u64 addr = al->offset;
2510 int color = -1;
2511
2512 if (!notes->options->use_offset)
2513 addr += notes->start;
2514
2515 if (!notes->options->use_offset) {
2516 printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
2517 } else {
2518 if (al->jump_sources) {
2519 if (notes->options->show_nr_jumps) {
2520 int prev;
2521 printed = scnprintf(bf, sizeof(bf), "%*d ",
2522 notes->widths.jumps,
2523 al->jump_sources);
2524 prev = obj__set_jumps_percent_color(obj, al->jump_sources,
2525 current_entry);
2526 obj__printf(obj, bf);
2527 obj__set_color(obj, prev);
2528 }
2529
2530 printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
2531 notes->widths.target, addr);
2532 } else {
2533 printed = scnprintf(bf, sizeof(bf), "%-*s ",
2534 notes->widths.addr, " ");
2535 }
2536 }
2537
2538 if (change_color)
2539 color = obj__set_color(obj, HE_COLORSET_ADDR);
2540 obj__printf(obj, bf);
2541 if (change_color)
2542 obj__set_color(obj, color);
2543
2544 disasm_line__write(disasm_line(al), notes, obj, bf, sizeof(bf), obj__printf, obj__write_graph);
2545
2546 obj__printf(obj, "%-*s", width - pcnt_width - cycles_width - 3 - printed, bf);
2547 }
2548
2549}
2550
2551void annotation_line__write(struct annotation_line *al, struct annotation *notes,
2552 struct annotation_write_ops *ops)
2553{
2554 __annotation_line__write(al, notes, ops->first_line, ops->current_entry,
2555 ops->change_color, ops->width, ops->obj,
2556 ops->set_color, ops->set_percent_color,
2557 ops->set_jumps_percent_color, ops->printf,
2558 ops->write_graph);
2559}
2560
2561int symbol__annotate2(struct symbol *sym, struct map *map, struct perf_evsel *evsel,
2562 struct annotation_options *options, struct arch **parch)
2563{
2564 struct annotation *notes = symbol__annotation(sym);
2565 size_t size = symbol__size(sym);
2566 int nr_pcnt = 1, err;
2567
2568 notes->offsets = zalloc(size * sizeof(struct annotation_line *));
2569 if (notes->offsets == NULL)
2570 return -1;
2571
2572 if (perf_evsel__is_group_event(evsel))
2573 nr_pcnt = evsel->nr_members;
2574
2575 err = symbol__annotate(sym, map, evsel, 0, parch);
2576 if (err)
2577 goto out_free_offsets;
2578
2579 notes->options = options;
2580
2581 symbol__calc_percent(sym, evsel);
2582
2583 notes->start = map__rip_2objdump(map, sym->start);
2584
2585 annotation__set_offsets(notes, size);
2586 annotation__mark_jump_targets(notes, sym);
2587 annotation__compute_ipc(notes, size);
2588 annotation__init_column_widths(notes, sym);
2589 notes->nr_events = nr_pcnt;
2590
2591 annotation__update_column_widths(notes);
2592
2593 return 0;
2594
2595out_free_offsets:
2596 zfree(&notes->offsets);
2597 return -1;
2598}
2599
2600#define ANNOTATION__CFG(n) \
2601 { .name = #n, .value = &annotation__default_options.n, }
2602
2603/*
2604 * Keep the entries sorted, they are bsearch'ed
2605 */
2606static struct annotation_config {
2607 const char *name;
2608 bool *value;
2609} annotation__configs[] = {
2610 ANNOTATION__CFG(hide_src_code),
2611 ANNOTATION__CFG(jump_arrows),
2612 ANNOTATION__CFG(show_linenr),
2613 ANNOTATION__CFG(show_nr_jumps),
2614 ANNOTATION__CFG(show_nr_samples),
2615 ANNOTATION__CFG(show_total_period),
2616 ANNOTATION__CFG(use_offset),
2617};
2618
2619#undef ANNOTATION__CFG
2620
2621static int annotation_config__cmp(const void *name, const void *cfgp)
2622{
2623 const struct annotation_config *cfg = cfgp;
2624
2625 return strcmp(name, cfg->name);
2626}
2627
2628static int annotation__config(const char *var, const char *value,
2629 void *data __maybe_unused)
2630{
2631 struct annotation_config *cfg;
2632 const char *name;
2633
2634 if (!strstarts(var, "annotate."))
2635 return 0;
2636
2637 name = var + 9;
2638 cfg = bsearch(name, annotation__configs, ARRAY_SIZE(annotation__configs),
2639 sizeof(struct annotation_config), annotation_config__cmp);
2640
2641 if (cfg == NULL)
2642 pr_debug("%s variable unknown, ignoring...", var);
2643 else
2644 *cfg->value = perf_config_bool(name, value);
2645 return 0;
2646}
2647
2648void annotation_config__init(void)
2649{
2650 perf_config(annotation__config, NULL);
2651
2652 annotation__default_options.show_total_period = symbol_conf.show_total_period;
2653 annotation__default_options.show_nr_samples = symbol_conf.show_nr_samples;
2654}