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.c226
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
56static int call__parse(struct ins_operands *ops) 57static 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
83indirect_call: 84indirect_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
120static int jump__parse(struct ins_operands *ops) 121static 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
175static int lock__parse(struct ins_operands *ops) 176static 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
239static int mov__parse(struct ins_operands *ops) 240static 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
306static int dec__parse(struct ins_operands *ops) 307static 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
494int symbol__annotate_init(struct map *map __maybe_unused, struct symbol *sym)
495{
496 struct annotation *notes = symbol__annotation(sym);
497 pthread_mutex_init(&notes->lock, NULL);
498 return 0;
499}
500
501int symbol__alloc_hist(struct symbol *sym) 495int 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
718static void disasm_line__init_ins(struct disasm_line *dl) 712static 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
769static struct disasm_line *disasm_line__new(s64 offset, char *line, 763static 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
864static 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
881static 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
894static 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
869static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 start, 947static 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(&notes->src->source, dl); 968 next = disasm__get_next_ip_line(&notes->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
1165int symbol__disassemble(struct symbol *sym, struct map *map, size_t privsize) 1248static 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)) {
1193fallback: 1271fallback:
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; 1283int 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);
1336out_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) 1424out:
1340 free(filename);
1341 return err; 1425 return err;
1342 1426
1343out_close_stdout: 1427out_close_stdout: