diff options
Diffstat (limited to 'tools/perf/util/annotate.c')
-rw-r--r-- | tools/perf/util/annotate.c | 226 |
1 files changed, 155 insertions, 71 deletions
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 4024d309bb00..aeb5a441bd74 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include "debug.h" | 17 | #include "debug.h" |
18 | #include "annotate.h" | 18 | #include "annotate.h" |
19 | #include "evsel.h" | 19 | #include "evsel.h" |
20 | #include "block-range.h" | ||
20 | #include <regex.h> | 21 | #include <regex.h> |
21 | #include <pthread.h> | 22 | #include <pthread.h> |
22 | #include <linux/bitops.h> | 23 | #include <linux/bitops.h> |
@@ -53,7 +54,7 @@ int ins__scnprintf(struct ins *ins, char *bf, size_t size, | |||
53 | return ins__raw_scnprintf(ins, bf, size, ops); | 54 | return ins__raw_scnprintf(ins, bf, size, ops); |
54 | } | 55 | } |
55 | 56 | ||
56 | static int call__parse(struct ins_operands *ops) | 57 | static int call__parse(struct ins_operands *ops, struct map *map) |
57 | { | 58 | { |
58 | char *endptr, *tok, *name; | 59 | char *endptr, *tok, *name; |
59 | 60 | ||
@@ -81,16 +82,16 @@ static int call__parse(struct ins_operands *ops) | |||
81 | return ops->target.name == NULL ? -1 : 0; | 82 | return ops->target.name == NULL ? -1 : 0; |
82 | 83 | ||
83 | indirect_call: | 84 | indirect_call: |
84 | tok = strchr(endptr, '('); | 85 | tok = strchr(endptr, '*'); |
85 | if (tok != NULL) { | 86 | if (tok == NULL) { |
86 | ops->target.addr = 0; | 87 | struct symbol *sym = map__find_symbol(map, map->map_ip(map, ops->target.addr)); |
88 | if (sym != NULL) | ||
89 | ops->target.name = strdup(sym->name); | ||
90 | else | ||
91 | ops->target.addr = 0; | ||
87 | return 0; | 92 | return 0; |
88 | } | 93 | } |
89 | 94 | ||
90 | tok = strchr(endptr, '*'); | ||
91 | if (tok == NULL) | ||
92 | return -1; | ||
93 | |||
94 | ops->target.addr = strtoull(tok + 1, NULL, 16); | 95 | ops->target.addr = strtoull(tok + 1, NULL, 16); |
95 | return 0; | 96 | return 0; |
96 | } | 97 | } |
@@ -117,7 +118,7 @@ bool ins__is_call(const struct ins *ins) | |||
117 | return ins->ops == &call_ops; | 118 | return ins->ops == &call_ops; |
118 | } | 119 | } |
119 | 120 | ||
120 | static int jump__parse(struct ins_operands *ops) | 121 | static int jump__parse(struct ins_operands *ops, struct map *map __maybe_unused) |
121 | { | 122 | { |
122 | const char *s = strchr(ops->raw, '+'); | 123 | const char *s = strchr(ops->raw, '+'); |
123 | 124 | ||
@@ -172,7 +173,7 @@ static int comment__symbol(char *raw, char *comment, u64 *addrp, char **namep) | |||
172 | return 0; | 173 | return 0; |
173 | } | 174 | } |
174 | 175 | ||
175 | static int lock__parse(struct ins_operands *ops) | 176 | static int lock__parse(struct ins_operands *ops, struct map *map) |
176 | { | 177 | { |
177 | char *name; | 178 | char *name; |
178 | 179 | ||
@@ -193,7 +194,7 @@ static int lock__parse(struct ins_operands *ops) | |||
193 | return 0; | 194 | return 0; |
194 | 195 | ||
195 | if (ops->locked.ins->ops->parse && | 196 | if (ops->locked.ins->ops->parse && |
196 | ops->locked.ins->ops->parse(ops->locked.ops) < 0) | 197 | ops->locked.ins->ops->parse(ops->locked.ops, map) < 0) |
197 | goto out_free_ops; | 198 | goto out_free_ops; |
198 | 199 | ||
199 | return 0; | 200 | return 0; |
@@ -236,7 +237,7 @@ static struct ins_ops lock_ops = { | |||
236 | .scnprintf = lock__scnprintf, | 237 | .scnprintf = lock__scnprintf, |
237 | }; | 238 | }; |
238 | 239 | ||
239 | static int mov__parse(struct ins_operands *ops) | 240 | static int mov__parse(struct ins_operands *ops, struct map *map __maybe_unused) |
240 | { | 241 | { |
241 | char *s = strchr(ops->raw, ','), *target, *comment, prev; | 242 | char *s = strchr(ops->raw, ','), *target, *comment, prev; |
242 | 243 | ||
@@ -303,7 +304,7 @@ static struct ins_ops mov_ops = { | |||
303 | .scnprintf = mov__scnprintf, | 304 | .scnprintf = mov__scnprintf, |
304 | }; | 305 | }; |
305 | 306 | ||
306 | static int dec__parse(struct ins_operands *ops) | 307 | static int dec__parse(struct ins_operands *ops, struct map *map __maybe_unused) |
307 | { | 308 | { |
308 | char *target, *comment, *s, prev; | 309 | char *target, *comment, *s, prev; |
309 | 310 | ||
@@ -491,13 +492,6 @@ static struct ins *ins__find(const char *name) | |||
491 | return bsearch(name, instructions, nmemb, sizeof(struct ins), ins__key_cmp); | 492 | return bsearch(name, instructions, nmemb, sizeof(struct ins), ins__key_cmp); |
492 | } | 493 | } |
493 | 494 | ||
494 | int symbol__annotate_init(struct map *map __maybe_unused, struct symbol *sym) | ||
495 | { | ||
496 | struct annotation *notes = symbol__annotation(sym); | ||
497 | pthread_mutex_init(¬es->lock, NULL); | ||
498 | return 0; | ||
499 | } | ||
500 | |||
501 | int symbol__alloc_hist(struct symbol *sym) | 495 | int symbol__alloc_hist(struct symbol *sym) |
502 | { | 496 | { |
503 | struct annotation *notes = symbol__annotation(sym); | 497 | struct annotation *notes = symbol__annotation(sym); |
@@ -715,7 +709,7 @@ int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 ip) | |||
715 | return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip); | 709 | return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip); |
716 | } | 710 | } |
717 | 711 | ||
718 | static void disasm_line__init_ins(struct disasm_line *dl) | 712 | static void disasm_line__init_ins(struct disasm_line *dl, struct map *map) |
719 | { | 713 | { |
720 | dl->ins = ins__find(dl->name); | 714 | dl->ins = ins__find(dl->name); |
721 | 715 | ||
@@ -725,7 +719,7 @@ static void disasm_line__init_ins(struct disasm_line *dl) | |||
725 | if (!dl->ins->ops) | 719 | if (!dl->ins->ops) |
726 | return; | 720 | return; |
727 | 721 | ||
728 | if (dl->ins->ops->parse && dl->ins->ops->parse(&dl->ops) < 0) | 722 | if (dl->ins->ops->parse && dl->ins->ops->parse(&dl->ops, map) < 0) |
729 | dl->ins = NULL; | 723 | dl->ins = NULL; |
730 | } | 724 | } |
731 | 725 | ||
@@ -767,7 +761,8 @@ out_free_name: | |||
767 | } | 761 | } |
768 | 762 | ||
769 | static struct disasm_line *disasm_line__new(s64 offset, char *line, | 763 | static struct disasm_line *disasm_line__new(s64 offset, char *line, |
770 | size_t privsize, int line_nr) | 764 | size_t privsize, int line_nr, |
765 | struct map *map) | ||
771 | { | 766 | { |
772 | struct disasm_line *dl = zalloc(sizeof(*dl) + privsize); | 767 | struct disasm_line *dl = zalloc(sizeof(*dl) + privsize); |
773 | 768 | ||
@@ -782,7 +777,7 @@ static struct disasm_line *disasm_line__new(s64 offset, char *line, | |||
782 | if (disasm_line__parse(dl->line, &dl->name, &dl->ops.raw) < 0) | 777 | if (disasm_line__parse(dl->line, &dl->name, &dl->ops.raw) < 0) |
783 | goto out_free_line; | 778 | goto out_free_line; |
784 | 779 | ||
785 | disasm_line__init_ins(dl); | 780 | disasm_line__init_ins(dl, map); |
786 | } | 781 | } |
787 | } | 782 | } |
788 | 783 | ||
@@ -866,6 +861,89 @@ double disasm__calc_percent(struct annotation *notes, int evidx, s64 offset, | |||
866 | return percent; | 861 | return percent; |
867 | } | 862 | } |
868 | 863 | ||
864 | static const char *annotate__address_color(struct block_range *br) | ||
865 | { | ||
866 | double cov = block_range__coverage(br); | ||
867 | |||
868 | if (cov >= 0) { | ||
869 | /* mark red for >75% coverage */ | ||
870 | if (cov > 0.75) | ||
871 | return PERF_COLOR_RED; | ||
872 | |||
873 | /* mark dull for <1% coverage */ | ||
874 | if (cov < 0.01) | ||
875 | return PERF_COLOR_NORMAL; | ||
876 | } | ||
877 | |||
878 | return PERF_COLOR_MAGENTA; | ||
879 | } | ||
880 | |||
881 | static const char *annotate__asm_color(struct block_range *br) | ||
882 | { | ||
883 | double cov = block_range__coverage(br); | ||
884 | |||
885 | if (cov >= 0) { | ||
886 | /* mark dull for <1% coverage */ | ||
887 | if (cov < 0.01) | ||
888 | return PERF_COLOR_NORMAL; | ||
889 | } | ||
890 | |||
891 | return PERF_COLOR_BLUE; | ||
892 | } | ||
893 | |||
894 | static void annotate__branch_printf(struct block_range *br, u64 addr) | ||
895 | { | ||
896 | bool emit_comment = true; | ||
897 | |||
898 | if (!br) | ||
899 | return; | ||
900 | |||
901 | #if 1 | ||
902 | if (br->is_target && br->start == addr) { | ||
903 | struct block_range *branch = br; | ||
904 | double p; | ||
905 | |||
906 | /* | ||
907 | * Find matching branch to our target. | ||
908 | */ | ||
909 | while (!branch->is_branch) | ||
910 | branch = block_range__next(branch); | ||
911 | |||
912 | p = 100 *(double)br->entry / branch->coverage; | ||
913 | |||
914 | if (p > 0.1) { | ||
915 | if (emit_comment) { | ||
916 | emit_comment = false; | ||
917 | printf("\t#"); | ||
918 | } | ||
919 | |||
920 | /* | ||
921 | * The percentage of coverage joined at this target in relation | ||
922 | * to the next branch. | ||
923 | */ | ||
924 | printf(" +%.2f%%", p); | ||
925 | } | ||
926 | } | ||
927 | #endif | ||
928 | if (br->is_branch && br->end == addr) { | ||
929 | double p = 100*(double)br->taken / br->coverage; | ||
930 | |||
931 | if (p > 0.1) { | ||
932 | if (emit_comment) { | ||
933 | emit_comment = false; | ||
934 | printf("\t#"); | ||
935 | } | ||
936 | |||
937 | /* | ||
938 | * The percentage of coverage leaving at this branch, and | ||
939 | * its prediction ratio. | ||
940 | */ | ||
941 | printf(" -%.2f%% (p:%.2f%%)", p, 100*(double)br->pred / br->taken); | ||
942 | } | ||
943 | } | ||
944 | } | ||
945 | |||
946 | |||
869 | static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 start, | 947 | static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 start, |
870 | struct perf_evsel *evsel, u64 len, int min_pcnt, int printed, | 948 | struct perf_evsel *evsel, u64 len, int min_pcnt, int printed, |
871 | int max_lines, struct disasm_line *queue) | 949 | int max_lines, struct disasm_line *queue) |
@@ -885,6 +963,7 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st | |||
885 | s64 offset = dl->offset; | 963 | s64 offset = dl->offset; |
886 | const u64 addr = start + offset; | 964 | const u64 addr = start + offset; |
887 | struct disasm_line *next; | 965 | struct disasm_line *next; |
966 | struct block_range *br; | ||
888 | 967 | ||
889 | next = disasm__get_next_ip_line(¬es->src->source, dl); | 968 | next = disasm__get_next_ip_line(¬es->src->source, dl); |
890 | 969 | ||
@@ -954,8 +1033,12 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st | |||
954 | } | 1033 | } |
955 | 1034 | ||
956 | printf(" : "); | 1035 | printf(" : "); |
957 | color_fprintf(stdout, PERF_COLOR_MAGENTA, " %" PRIx64 ":", addr); | 1036 | |
958 | color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", dl->line); | 1037 | br = block_range__find(addr); |
1038 | color_fprintf(stdout, annotate__address_color(br), " %" PRIx64 ":", addr); | ||
1039 | color_fprintf(stdout, annotate__asm_color(br), "%s", dl->line); | ||
1040 | annotate__branch_printf(br, addr); | ||
1041 | printf("\n"); | ||
959 | 1042 | ||
960 | if (ppercents != &percent) | 1043 | if (ppercents != &percent) |
961 | free(ppercents); | 1044 | free(ppercents); |
@@ -1066,7 +1149,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, | |||
1066 | parsed_line = tmp2 + 1; | 1149 | parsed_line = tmp2 + 1; |
1067 | } | 1150 | } |
1068 | 1151 | ||
1069 | dl = disasm_line__new(offset, parsed_line, privsize, *line_nr); | 1152 | dl = disasm_line__new(offset, parsed_line, privsize, *line_nr, map); |
1070 | free(line); | 1153 | free(line); |
1071 | (*line_nr)++; | 1154 | (*line_nr)++; |
1072 | 1155 | ||
@@ -1084,7 +1167,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, | |||
1084 | .addr = dl->ops.target.addr, | 1167 | .addr = dl->ops.target.addr, |
1085 | }; | 1168 | }; |
1086 | 1169 | ||
1087 | if (!map_groups__find_ams(&target, NULL) && | 1170 | if (!map_groups__find_ams(&target) && |
1088 | target.sym->start == target.al_addr) | 1171 | target.sym->start == target.al_addr) |
1089 | dl->ops.target.name = strdup(target.sym->name); | 1172 | dl->ops.target.name = strdup(target.sym->name); |
1090 | } | 1173 | } |
@@ -1162,53 +1245,60 @@ int symbol__strerror_disassemble(struct symbol *sym __maybe_unused, struct map * | |||
1162 | return 0; | 1245 | return 0; |
1163 | } | 1246 | } |
1164 | 1247 | ||
1165 | int symbol__disassemble(struct symbol *sym, struct map *map, size_t privsize) | 1248 | static int dso__disassemble_filename(struct dso *dso, char *filename, size_t filename_size) |
1166 | { | 1249 | { |
1167 | struct dso *dso = map->dso; | 1250 | char linkname[PATH_MAX]; |
1168 | char *filename = dso__build_id_filename(dso, NULL, 0); | 1251 | char *build_id_filename; |
1169 | bool free_filename = true; | ||
1170 | char command[PATH_MAX * 2]; | ||
1171 | FILE *file; | ||
1172 | int err = 0; | ||
1173 | char symfs_filename[PATH_MAX]; | ||
1174 | struct kcore_extract kce; | ||
1175 | bool delete_extract = false; | ||
1176 | int stdout_fd[2]; | ||
1177 | int lineno = 0; | ||
1178 | int nline; | ||
1179 | pid_t pid; | ||
1180 | 1252 | ||
1181 | if (filename) | 1253 | if (dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS && |
1182 | symbol__join_symfs(symfs_filename, filename); | 1254 | !dso__is_kcore(dso)) |
1255 | return SYMBOL_ANNOTATE_ERRNO__NO_VMLINUX; | ||
1183 | 1256 | ||
1184 | if (filename == NULL) { | 1257 | build_id_filename = dso__build_id_filename(dso, NULL, 0); |
1258 | if (build_id_filename) { | ||
1259 | __symbol__join_symfs(filename, filename_size, build_id_filename); | ||
1260 | free(build_id_filename); | ||
1261 | } else { | ||
1185 | if (dso->has_build_id) | 1262 | if (dso->has_build_id) |
1186 | return ENOMEM; | 1263 | return ENOMEM; |
1187 | goto fallback; | 1264 | goto fallback; |
1188 | } else if (dso__is_kcore(dso) || | 1265 | } |
1189 | readlink(symfs_filename, command, sizeof(command)) < 0 || | 1266 | |
1190 | strstr(command, DSO__NAME_KALLSYMS) || | 1267 | if (dso__is_kcore(dso) || |
1191 | access(symfs_filename, R_OK)) { | 1268 | readlink(filename, linkname, sizeof(linkname)) < 0 || |
1192 | free(filename); | 1269 | strstr(linkname, DSO__NAME_KALLSYMS) || |
1270 | access(filename, R_OK)) { | ||
1193 | fallback: | 1271 | fallback: |
1194 | /* | 1272 | /* |
1195 | * If we don't have build-ids or the build-id file isn't in the | 1273 | * If we don't have build-ids or the build-id file isn't in the |
1196 | * cache, or is just a kallsyms file, well, lets hope that this | 1274 | * cache, or is just a kallsyms file, well, lets hope that this |
1197 | * DSO is the same as when 'perf record' ran. | 1275 | * DSO is the same as when 'perf record' ran. |
1198 | */ | 1276 | */ |
1199 | filename = (char *)dso->long_name; | 1277 | __symbol__join_symfs(filename, filename_size, dso->long_name); |
1200 | symbol__join_symfs(symfs_filename, filename); | ||
1201 | free_filename = false; | ||
1202 | } | 1278 | } |
1203 | 1279 | ||
1204 | if (dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS && | 1280 | return 0; |
1205 | !dso__is_kcore(dso)) { | 1281 | } |
1206 | err = SYMBOL_ANNOTATE_ERRNO__NO_VMLINUX; | 1282 | |
1207 | goto out_free_filename; | 1283 | int symbol__disassemble(struct symbol *sym, struct map *map, size_t privsize) |
1208 | } | 1284 | { |
1285 | struct dso *dso = map->dso; | ||
1286 | char command[PATH_MAX * 2]; | ||
1287 | FILE *file; | ||
1288 | char symfs_filename[PATH_MAX]; | ||
1289 | struct kcore_extract kce; | ||
1290 | bool delete_extract = false; | ||
1291 | int stdout_fd[2]; | ||
1292 | int lineno = 0; | ||
1293 | int nline; | ||
1294 | pid_t pid; | ||
1295 | int err = dso__disassemble_filename(dso, symfs_filename, sizeof(symfs_filename)); | ||
1296 | |||
1297 | if (err) | ||
1298 | return err; | ||
1209 | 1299 | ||
1210 | pr_debug("%s: filename=%s, sym=%s, start=%#" PRIx64 ", end=%#" PRIx64 "\n", __func__, | 1300 | pr_debug("%s: filename=%s, sym=%s, start=%#" PRIx64 ", end=%#" PRIx64 "\n", __func__, |
1211 | filename, sym->name, map->unmap_ip(map, sym->start), | 1301 | symfs_filename, sym->name, map->unmap_ip(map, sym->start), |
1212 | map->unmap_ip(map, sym->end)); | 1302 | map->unmap_ip(map, sym->end)); |
1213 | 1303 | ||
1214 | pr_debug("annotating [%p] %30s : [%p] %30s\n", | 1304 | pr_debug("annotating [%p] %30s : [%p] %30s\n", |
@@ -1223,11 +1313,6 @@ fallback: | |||
1223 | delete_extract = true; | 1313 | delete_extract = true; |
1224 | strlcpy(symfs_filename, kce.extract_filename, | 1314 | strlcpy(symfs_filename, kce.extract_filename, |
1225 | sizeof(symfs_filename)); | 1315 | sizeof(symfs_filename)); |
1226 | if (free_filename) { | ||
1227 | free(filename); | ||
1228 | free_filename = false; | ||
1229 | } | ||
1230 | filename = symfs_filename; | ||
1231 | } | 1316 | } |
1232 | } else if (dso__needs_decompress(dso)) { | 1317 | } else if (dso__needs_decompress(dso)) { |
1233 | char tmp[PATH_MAX]; | 1318 | char tmp[PATH_MAX]; |
@@ -1236,14 +1321,14 @@ fallback: | |||
1236 | bool ret; | 1321 | bool ret; |
1237 | 1322 | ||
1238 | if (kmod_path__parse_ext(&m, symfs_filename)) | 1323 | if (kmod_path__parse_ext(&m, symfs_filename)) |
1239 | goto out_free_filename; | 1324 | goto out; |
1240 | 1325 | ||
1241 | snprintf(tmp, PATH_MAX, "/tmp/perf-kmod-XXXXXX"); | 1326 | snprintf(tmp, PATH_MAX, "/tmp/perf-kmod-XXXXXX"); |
1242 | 1327 | ||
1243 | fd = mkstemp(tmp); | 1328 | fd = mkstemp(tmp); |
1244 | if (fd < 0) { | 1329 | if (fd < 0) { |
1245 | free(m.ext); | 1330 | free(m.ext); |
1246 | goto out_free_filename; | 1331 | goto out; |
1247 | } | 1332 | } |
1248 | 1333 | ||
1249 | ret = decompress_to_file(m.ext, symfs_filename, fd); | 1334 | ret = decompress_to_file(m.ext, symfs_filename, fd); |
@@ -1255,7 +1340,7 @@ fallback: | |||
1255 | close(fd); | 1340 | close(fd); |
1256 | 1341 | ||
1257 | if (!ret) | 1342 | if (!ret) |
1258 | goto out_free_filename; | 1343 | goto out; |
1259 | 1344 | ||
1260 | strcpy(symfs_filename, tmp); | 1345 | strcpy(symfs_filename, tmp); |
1261 | } | 1346 | } |
@@ -1271,7 +1356,7 @@ fallback: | |||
1271 | map__rip_2objdump(map, sym->end), | 1356 | map__rip_2objdump(map, sym->end), |
1272 | symbol_conf.annotate_asm_raw ? "" : "--no-show-raw", | 1357 | symbol_conf.annotate_asm_raw ? "" : "--no-show-raw", |
1273 | symbol_conf.annotate_src ? "-S" : "", | 1358 | symbol_conf.annotate_src ? "-S" : "", |
1274 | symfs_filename, filename); | 1359 | symfs_filename, symfs_filename); |
1275 | 1360 | ||
1276 | pr_debug("Executing: %s\n", command); | 1361 | pr_debug("Executing: %s\n", command); |
1277 | 1362 | ||
@@ -1333,11 +1418,10 @@ out_remove_tmp: | |||
1333 | 1418 | ||
1334 | if (dso__needs_decompress(dso)) | 1419 | if (dso__needs_decompress(dso)) |
1335 | unlink(symfs_filename); | 1420 | unlink(symfs_filename); |
1336 | out_free_filename: | 1421 | |
1337 | if (delete_extract) | 1422 | if (delete_extract) |
1338 | kcore_extract__delete(&kce); | 1423 | kcore_extract__delete(&kce); |
1339 | if (free_filename) | 1424 | out: |
1340 | free(filename); | ||
1341 | return err; | 1425 | return err; |
1342 | 1426 | ||
1343 | out_close_stdout: | 1427 | out_close_stdout: |