diff options
Diffstat (limited to 'tools/perf/util/annotate.c')
-rw-r--r-- | tools/perf/util/annotate.c | 739 |
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 | ||
45 | struct annotation_options annotation__default_options = { | ||
46 | .use_offset = true, | ||
47 | .jump_arrows = true, | ||
48 | }; | ||
49 | |||
32 | const char *disassembler_style; | 50 | const char *disassembler_style; |
33 | const char *objdump_path; | 51 | const char *objdump_path; |
34 | static regex_t file_lineno; | 52 | static 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 | ||
187 | static int call__parse(struct arch *arch, struct ins_operands *ops, struct map *map) | 205 | static 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; | ||
235 | find_target: | ||
236 | target.addr = map__objdump_2mem(map, ops->target.addr); | ||
212 | 237 | ||
213 | indirect_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 | |||
244 | indirect_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 | ||
228 | static int call__scnprintf(struct ins *ins, char *bf, size_t size, | 251 | static 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 | ||
245 | bool ins__is_call(const struct ins *ins) | 271 | bool 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 | ||
250 | static int jump__parse(struct arch *arch __maybe_unused, struct ins_operands *ops, struct map *map __maybe_unused) | 276 | static 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 | |||
280 | static int jump__scnprintf(struct ins *ins, char *bf, size_t size, | 349 | static 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 | ||
343 | static int lock__parse(struct arch *arch, struct ins_operands *ops, struct map *map) | 416 | static 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 | ||
401 | static int mov__parse(struct arch *arch, struct ins_operands *ops, struct map *map __maybe_unused) | 474 | static 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 | ||
462 | static int dec__parse(struct arch *arch __maybe_unused, struct ins_operands *ops, struct map *map __maybe_unused) | 535 | static 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 | ||
902 | static 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 | |||
914 | static 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 | |||
936 | void 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(¬es->lock); | ||
944 | for (offset = 0; offset < size; ++offset) { | ||
945 | struct cyc_hist *ch; | ||
946 | |||
947 | ch = ¬es->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(¬es->lock); | ||
960 | } | ||
961 | |||
829 | int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, struct perf_sample *sample, | 962 | int 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 | ||
841 | static void disasm_line__init_ins(struct disasm_line *dl, struct arch *arch, struct map *map) | 974 | static 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: | |||
882 | struct annotate_args { | 1015 | struct 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, ¬es->src->source); | 1432 | annotation_line__add(&dl->al, ¬es->src->source); |
@@ -1421,9 +1555,9 @@ fallback: | |||
1421 | 1555 | ||
1422 | static int symbol__disassemble(struct symbol *sym, struct annotate_args *args) | 1556 | static 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; |
1679 | out_free_command: | ||
1680 | free(command); | ||
1540 | out_remove_tmp: | 1681 | out_remove_tmp: |
1541 | close(stdout_fd[0]); | 1682 | close(stdout_fd[0]); |
1542 | 1683 | ||
@@ -1550,7 +1691,7 @@ out: | |||
1550 | 1691 | ||
1551 | out_close_stdout: | 1692 | out_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 | ||
1556 | static void calc_percent(struct sym_hist *hist, | 1697 | static 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 | ||
2025 | static void FILE__set_percent_color(void *fp __maybe_unused, | ||
2026 | double percent __maybe_unused, | ||
2027 | bool current __maybe_unused) | ||
2028 | { | ||
2029 | } | ||
2030 | |||
2031 | static 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 | |||
2037 | static int FILE__set_color(void *fp __maybe_unused, int color __maybe_unused) | ||
2038 | { | ||
2039 | return 0; | ||
2040 | } | ||
2041 | |||
2042 | static 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 | |||
2051 | static 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 | |||
2066 | int 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, ¬es->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 | |||
2091 | int 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; | ||
2117 | out_free_filename: | ||
2118 | free(filename); | ||
2119 | return err; | ||
2120 | } | ||
2121 | |||
1882 | void symbol__annotate_zero_histogram(struct symbol *sym, int evidx) | 2122 | void 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 | ||
2181 | bool 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 | |||
2191 | void 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 | |||
2224 | void 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, ¬es->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 | |||
2252 | static 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 | |||
2261 | void 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 | |||
2269 | void 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 | |||
1941 | static void annotation__calc_lines(struct annotation *notes, struct map *map, | 2282 | static 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 | |||
2320 | int 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 | ||
1980 | int symbol__tty_annotate(struct symbol *sym, struct map *map, | 2352 | int 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 | |||
2384 | double 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 | |||
2397 | static 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)) { | ||
2412 | call_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 | |||
2428 | static 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 | |||
2551 | void 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 | |||
2561 | int 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 | |||
2595 | out_free_offsets: | ||
2596 | zfree(¬es->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 | */ | ||
2606 | static 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 | |||
2621 | static 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 | |||
2628 | static 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 | |||
2648 | void 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 | } | ||