diff options
Diffstat (limited to 'tools/perf/builtin-annotate.c')
-rw-r--r-- | tools/perf/builtin-annotate.c | 262 |
1 files changed, 214 insertions, 48 deletions
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index b1ed5f766cb3..7e58e3ad1508 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
@@ -25,6 +25,10 @@ | |||
25 | #define SHOW_USER 2 | 25 | #define SHOW_USER 2 |
26 | #define SHOW_HV 4 | 26 | #define SHOW_HV 4 |
27 | 27 | ||
28 | #define MIN_GREEN 0.5 | ||
29 | #define MIN_RED 5.0 | ||
30 | |||
31 | |||
28 | static char const *input_name = "perf.data"; | 32 | static char const *input_name = "perf.data"; |
29 | static char *vmlinux = "vmlinux"; | 33 | static char *vmlinux = "vmlinux"; |
30 | 34 | ||
@@ -39,40 +43,42 @@ static int dump_trace = 0; | |||
39 | 43 | ||
40 | static int verbose; | 44 | static int verbose; |
41 | 45 | ||
46 | static int print_line; | ||
47 | |||
42 | static unsigned long page_size; | 48 | static unsigned long page_size; |
43 | static unsigned long mmap_window = 32; | 49 | static unsigned long mmap_window = 32; |
44 | 50 | ||
45 | struct ip_event { | 51 | struct ip_event { |
46 | struct perf_event_header header; | 52 | struct perf_event_header header; |
47 | __u64 ip; | 53 | u64 ip; |
48 | __u32 pid, tid; | 54 | u32 pid, tid; |
49 | }; | 55 | }; |
50 | 56 | ||
51 | struct mmap_event { | 57 | struct mmap_event { |
52 | struct perf_event_header header; | 58 | struct perf_event_header header; |
53 | __u32 pid, tid; | 59 | u32 pid, tid; |
54 | __u64 start; | 60 | u64 start; |
55 | __u64 len; | 61 | u64 len; |
56 | __u64 pgoff; | 62 | u64 pgoff; |
57 | char filename[PATH_MAX]; | 63 | char filename[PATH_MAX]; |
58 | }; | 64 | }; |
59 | 65 | ||
60 | struct comm_event { | 66 | struct comm_event { |
61 | struct perf_event_header header; | 67 | struct perf_event_header header; |
62 | __u32 pid, tid; | 68 | u32 pid, tid; |
63 | char comm[16]; | 69 | char comm[16]; |
64 | }; | 70 | }; |
65 | 71 | ||
66 | struct fork_event { | 72 | struct fork_event { |
67 | struct perf_event_header header; | 73 | struct perf_event_header header; |
68 | __u32 pid, ppid; | 74 | u32 pid, ppid; |
69 | }; | 75 | }; |
70 | 76 | ||
71 | struct period_event { | 77 | struct period_event { |
72 | struct perf_event_header header; | 78 | struct perf_event_header header; |
73 | __u64 time; | 79 | u64 time; |
74 | __u64 id; | 80 | u64 id; |
75 | __u64 sample_period; | 81 | u64 sample_period; |
76 | }; | 82 | }; |
77 | 83 | ||
78 | typedef union event_union { | 84 | typedef union event_union { |
@@ -84,6 +90,13 @@ typedef union event_union { | |||
84 | struct period_event period; | 90 | struct period_event period; |
85 | } event_t; | 91 | } event_t; |
86 | 92 | ||
93 | |||
94 | struct sym_ext { | ||
95 | struct rb_node node; | ||
96 | double percent; | ||
97 | char *path; | ||
98 | }; | ||
99 | |||
87 | static LIST_HEAD(dsos); | 100 | static LIST_HEAD(dsos); |
88 | static struct dso *kernel_dso; | 101 | static struct dso *kernel_dso; |
89 | static struct dso *vdso; | 102 | static struct dso *vdso; |
@@ -145,7 +158,7 @@ static void dsos__fprintf(FILE *fp) | |||
145 | dso__fprintf(pos, fp); | 158 | dso__fprintf(pos, fp); |
146 | } | 159 | } |
147 | 160 | ||
148 | static struct symbol *vdso__find_symbol(struct dso *dso, __u64 ip) | 161 | static struct symbol *vdso__find_symbol(struct dso *dso, u64 ip) |
149 | { | 162 | { |
150 | return dso__find_symbol(kernel_dso, ip); | 163 | return dso__find_symbol(kernel_dso, ip); |
151 | } | 164 | } |
@@ -178,19 +191,19 @@ static int load_kernel(void) | |||
178 | 191 | ||
179 | struct map { | 192 | struct map { |
180 | struct list_head node; | 193 | struct list_head node; |
181 | __u64 start; | 194 | u64 start; |
182 | __u64 end; | 195 | u64 end; |
183 | __u64 pgoff; | 196 | u64 pgoff; |
184 | __u64 (*map_ip)(struct map *, __u64); | 197 | u64 (*map_ip)(struct map *, u64); |
185 | struct dso *dso; | 198 | struct dso *dso; |
186 | }; | 199 | }; |
187 | 200 | ||
188 | static __u64 map__map_ip(struct map *map, __u64 ip) | 201 | static u64 map__map_ip(struct map *map, u64 ip) |
189 | { | 202 | { |
190 | return ip - map->start + map->pgoff; | 203 | return ip - map->start + map->pgoff; |
191 | } | 204 | } |
192 | 205 | ||
193 | static __u64 vdso__map_ip(struct map *map, __u64 ip) | 206 | static u64 vdso__map_ip(struct map *map, u64 ip) |
194 | { | 207 | { |
195 | return ip; | 208 | return ip; |
196 | } | 209 | } |
@@ -373,7 +386,7 @@ static int thread__fork(struct thread *self, struct thread *parent) | |||
373 | return 0; | 386 | return 0; |
374 | } | 387 | } |
375 | 388 | ||
376 | static struct map *thread__find_map(struct thread *self, __u64 ip) | 389 | static struct map *thread__find_map(struct thread *self, u64 ip) |
377 | { | 390 | { |
378 | struct map *pos; | 391 | struct map *pos; |
379 | 392 | ||
@@ -414,7 +427,7 @@ struct hist_entry { | |||
414 | struct map *map; | 427 | struct map *map; |
415 | struct dso *dso; | 428 | struct dso *dso; |
416 | struct symbol *sym; | 429 | struct symbol *sym; |
417 | __u64 ip; | 430 | u64 ip; |
418 | char level; | 431 | char level; |
419 | 432 | ||
420 | uint32_t count; | 433 | uint32_t count; |
@@ -519,7 +532,7 @@ sort__dso_print(FILE *fp, struct hist_entry *self) | |||
519 | if (self->dso) | 532 | if (self->dso) |
520 | return fprintf(fp, "%-25s", self->dso->name); | 533 | return fprintf(fp, "%-25s", self->dso->name); |
521 | 534 | ||
522 | return fprintf(fp, "%016llx ", (__u64)self->ip); | 535 | return fprintf(fp, "%016llx ", (u64)self->ip); |
523 | } | 536 | } |
524 | 537 | ||
525 | static struct sort_entry sort_dso = { | 538 | static struct sort_entry sort_dso = { |
@@ -533,7 +546,7 @@ static struct sort_entry sort_dso = { | |||
533 | static int64_t | 546 | static int64_t |
534 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) | 547 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) |
535 | { | 548 | { |
536 | __u64 ip_l, ip_r; | 549 | u64 ip_l, ip_r; |
537 | 550 | ||
538 | if (left->sym == right->sym) | 551 | if (left->sym == right->sym) |
539 | return 0; | 552 | return 0; |
@@ -550,13 +563,13 @@ sort__sym_print(FILE *fp, struct hist_entry *self) | |||
550 | size_t ret = 0; | 563 | size_t ret = 0; |
551 | 564 | ||
552 | if (verbose) | 565 | if (verbose) |
553 | ret += fprintf(fp, "%#018llx ", (__u64)self->ip); | 566 | ret += fprintf(fp, "%#018llx ", (u64)self->ip); |
554 | 567 | ||
555 | if (self->sym) { | 568 | if (self->sym) { |
556 | ret += fprintf(fp, "[%c] %s", | 569 | ret += fprintf(fp, "[%c] %s", |
557 | self->dso == kernel_dso ? 'k' : '.', self->sym->name); | 570 | self->dso == kernel_dso ? 'k' : '.', self->sym->name); |
558 | } else { | 571 | } else { |
559 | ret += fprintf(fp, "%#016llx", (__u64)self->ip); | 572 | ret += fprintf(fp, "%#016llx", (u64)self->ip); |
560 | } | 573 | } |
561 | 574 | ||
562 | return ret; | 575 | return ret; |
@@ -647,7 +660,7 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) | |||
647 | /* | 660 | /* |
648 | * collect histogram counts | 661 | * collect histogram counts |
649 | */ | 662 | */ |
650 | static void hist_hit(struct hist_entry *he, __u64 ip) | 663 | static void hist_hit(struct hist_entry *he, u64 ip) |
651 | { | 664 | { |
652 | unsigned int sym_size, offset; | 665 | unsigned int sym_size, offset; |
653 | struct symbol *sym = he->sym; | 666 | struct symbol *sym = he->sym; |
@@ -676,7 +689,7 @@ static void hist_hit(struct hist_entry *he, __u64 ip) | |||
676 | 689 | ||
677 | static int | 690 | static int |
678 | hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, | 691 | hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, |
679 | struct symbol *sym, __u64 ip, char level) | 692 | struct symbol *sym, u64 ip, char level) |
680 | { | 693 | { |
681 | struct rb_node **p = &hist.rb_node; | 694 | struct rb_node **p = &hist.rb_node; |
682 | struct rb_node *parent = NULL; | 695 | struct rb_node *parent = NULL; |
@@ -848,7 +861,7 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head) | |||
848 | int show = 0; | 861 | int show = 0; |
849 | struct dso *dso = NULL; | 862 | struct dso *dso = NULL; |
850 | struct thread *thread = threads__findnew(event->ip.pid); | 863 | struct thread *thread = threads__findnew(event->ip.pid); |
851 | __u64 ip = event->ip.ip; | 864 | u64 ip = event->ip.ip; |
852 | struct map *map = NULL; | 865 | struct map *map = NULL; |
853 | 866 | ||
854 | dprintf("%p [%p]: PERF_EVENT (IP, %d): %d: %p\n", | 867 | dprintf("%p [%p]: PERF_EVENT (IP, %d): %d: %p\n", |
@@ -1030,13 +1043,33 @@ process_event(event_t *event, unsigned long offset, unsigned long head) | |||
1030 | return 0; | 1043 | return 0; |
1031 | } | 1044 | } |
1032 | 1045 | ||
1046 | static char *get_color(double percent) | ||
1047 | { | ||
1048 | char *color = PERF_COLOR_NORMAL; | ||
1049 | |||
1050 | /* | ||
1051 | * We color high-overhead entries in red, mid-overhead | ||
1052 | * entries in green - and keep the low overhead places | ||
1053 | * normal: | ||
1054 | */ | ||
1055 | if (percent >= MIN_RED) | ||
1056 | color = PERF_COLOR_RED; | ||
1057 | else { | ||
1058 | if (percent > MIN_GREEN) | ||
1059 | color = PERF_COLOR_GREEN; | ||
1060 | } | ||
1061 | return color; | ||
1062 | } | ||
1063 | |||
1033 | static int | 1064 | static int |
1034 | parse_line(FILE *file, struct symbol *sym, __u64 start, __u64 len) | 1065 | parse_line(FILE *file, struct symbol *sym, u64 start, u64 len) |
1035 | { | 1066 | { |
1036 | char *line = NULL, *tmp, *tmp2; | 1067 | char *line = NULL, *tmp, *tmp2; |
1068 | static const char *prev_line; | ||
1069 | static const char *prev_color; | ||
1037 | unsigned int offset; | 1070 | unsigned int offset; |
1038 | size_t line_len; | 1071 | size_t line_len; |
1039 | __u64 line_ip; | 1072 | u64 line_ip; |
1040 | int ret; | 1073 | int ret; |
1041 | char *c; | 1074 | char *c; |
1042 | 1075 | ||
@@ -1073,27 +1106,36 @@ parse_line(FILE *file, struct symbol *sym, __u64 start, __u64 len) | |||
1073 | } | 1106 | } |
1074 | 1107 | ||
1075 | if (line_ip != -1) { | 1108 | if (line_ip != -1) { |
1109 | const char *path = NULL; | ||
1076 | unsigned int hits = 0; | 1110 | unsigned int hits = 0; |
1077 | double percent = 0.0; | 1111 | double percent = 0.0; |
1078 | char *color = PERF_COLOR_NORMAL; | 1112 | char *color; |
1113 | struct sym_ext *sym_ext = sym->priv; | ||
1079 | 1114 | ||
1080 | offset = line_ip - start; | 1115 | offset = line_ip - start; |
1081 | if (offset < len) | 1116 | if (offset < len) |
1082 | hits = sym->hist[offset]; | 1117 | hits = sym->hist[offset]; |
1083 | 1118 | ||
1084 | if (sym->hist_sum) | 1119 | if (offset < len && sym_ext) { |
1120 | path = sym_ext[offset].path; | ||
1121 | percent = sym_ext[offset].percent; | ||
1122 | } else if (sym->hist_sum) | ||
1085 | percent = 100.0 * hits / sym->hist_sum; | 1123 | percent = 100.0 * hits / sym->hist_sum; |
1086 | 1124 | ||
1125 | color = get_color(percent); | ||
1126 | |||
1087 | /* | 1127 | /* |
1088 | * We color high-overhead entries in red, mid-overhead | 1128 | * Also color the filename and line if needed, with |
1089 | * entries in green - and keep the low overhead places | 1129 | * the same color than the percentage. Don't print it |
1090 | * normal: | 1130 | * twice for close colored ip with the same filename:line |
1091 | */ | 1131 | */ |
1092 | if (percent >= 5.0) | 1132 | if (path) { |
1093 | color = PERF_COLOR_RED; | 1133 | if (!prev_line || strcmp(prev_line, path) |
1094 | else { | 1134 | || color != prev_color) { |
1095 | if (percent > 0.5) | 1135 | color_fprintf(stdout, color, " %s", path); |
1096 | color = PERF_COLOR_GREEN; | 1136 | prev_line = path; |
1137 | prev_color = color; | ||
1138 | } | ||
1097 | } | 1139 | } |
1098 | 1140 | ||
1099 | color_fprintf(stdout, color, " %7.2f", percent); | 1141 | color_fprintf(stdout, color, " %7.2f", percent); |
@@ -1109,10 +1151,125 @@ parse_line(FILE *file, struct symbol *sym, __u64 start, __u64 len) | |||
1109 | return 0; | 1151 | return 0; |
1110 | } | 1152 | } |
1111 | 1153 | ||
1154 | static struct rb_root root_sym_ext; | ||
1155 | |||
1156 | static void insert_source_line(struct sym_ext *sym_ext) | ||
1157 | { | ||
1158 | struct sym_ext *iter; | ||
1159 | struct rb_node **p = &root_sym_ext.rb_node; | ||
1160 | struct rb_node *parent = NULL; | ||
1161 | |||
1162 | while (*p != NULL) { | ||
1163 | parent = *p; | ||
1164 | iter = rb_entry(parent, struct sym_ext, node); | ||
1165 | |||
1166 | if (sym_ext->percent > iter->percent) | ||
1167 | p = &(*p)->rb_left; | ||
1168 | else | ||
1169 | p = &(*p)->rb_right; | ||
1170 | } | ||
1171 | |||
1172 | rb_link_node(&sym_ext->node, parent, p); | ||
1173 | rb_insert_color(&sym_ext->node, &root_sym_ext); | ||
1174 | } | ||
1175 | |||
1176 | static void free_source_line(struct symbol *sym, int len) | ||
1177 | { | ||
1178 | struct sym_ext *sym_ext = sym->priv; | ||
1179 | int i; | ||
1180 | |||
1181 | if (!sym_ext) | ||
1182 | return; | ||
1183 | |||
1184 | for (i = 0; i < len; i++) | ||
1185 | free(sym_ext[i].path); | ||
1186 | free(sym_ext); | ||
1187 | |||
1188 | sym->priv = NULL; | ||
1189 | root_sym_ext = RB_ROOT; | ||
1190 | } | ||
1191 | |||
1192 | /* Get the filename:line for the colored entries */ | ||
1193 | static void | ||
1194 | get_source_line(struct symbol *sym, u64 start, int len, char *filename) | ||
1195 | { | ||
1196 | int i; | ||
1197 | char cmd[PATH_MAX * 2]; | ||
1198 | struct sym_ext *sym_ext; | ||
1199 | |||
1200 | if (!sym->hist_sum) | ||
1201 | return; | ||
1202 | |||
1203 | sym->priv = calloc(len, sizeof(struct sym_ext)); | ||
1204 | if (!sym->priv) | ||
1205 | return; | ||
1206 | |||
1207 | sym_ext = sym->priv; | ||
1208 | |||
1209 | for (i = 0; i < len; i++) { | ||
1210 | char *path = NULL; | ||
1211 | size_t line_len; | ||
1212 | u64 offset; | ||
1213 | FILE *fp; | ||
1214 | |||
1215 | sym_ext[i].percent = 100.0 * sym->hist[i] / sym->hist_sum; | ||
1216 | if (sym_ext[i].percent <= 0.5) | ||
1217 | continue; | ||
1218 | |||
1219 | offset = start + i; | ||
1220 | sprintf(cmd, "addr2line -e %s %016llx", filename, offset); | ||
1221 | fp = popen(cmd, "r"); | ||
1222 | if (!fp) | ||
1223 | continue; | ||
1224 | |||
1225 | if (getline(&path, &line_len, fp) < 0 || !line_len) | ||
1226 | goto next; | ||
1227 | |||
1228 | sym_ext[i].path = malloc(sizeof(char) * line_len + 1); | ||
1229 | if (!sym_ext[i].path) | ||
1230 | goto next; | ||
1231 | |||
1232 | strcpy(sym_ext[i].path, path); | ||
1233 | insert_source_line(&sym_ext[i]); | ||
1234 | |||
1235 | next: | ||
1236 | pclose(fp); | ||
1237 | } | ||
1238 | } | ||
1239 | |||
1240 | static void print_summary(char *filename) | ||
1241 | { | ||
1242 | struct sym_ext *sym_ext; | ||
1243 | struct rb_node *node; | ||
1244 | |||
1245 | printf("\nSorted summary for file %s\n", filename); | ||
1246 | printf("----------------------------------------------\n\n"); | ||
1247 | |||
1248 | if (RB_EMPTY_ROOT(&root_sym_ext)) { | ||
1249 | printf(" Nothing higher than %1.1f%%\n", MIN_GREEN); | ||
1250 | return; | ||
1251 | } | ||
1252 | |||
1253 | node = rb_first(&root_sym_ext); | ||
1254 | while (node) { | ||
1255 | double percent; | ||
1256 | char *color; | ||
1257 | char *path; | ||
1258 | |||
1259 | sym_ext = rb_entry(node, struct sym_ext, node); | ||
1260 | percent = sym_ext->percent; | ||
1261 | color = get_color(percent); | ||
1262 | path = sym_ext->path; | ||
1263 | |||
1264 | color_fprintf(stdout, color, " %7.2f %s", percent, path); | ||
1265 | node = rb_next(node); | ||
1266 | } | ||
1267 | } | ||
1268 | |||
1112 | static void annotate_sym(struct dso *dso, struct symbol *sym) | 1269 | static void annotate_sym(struct dso *dso, struct symbol *sym) |
1113 | { | 1270 | { |
1114 | char *filename = dso->name; | 1271 | char *filename = dso->name; |
1115 | __u64 start, end, len; | 1272 | u64 start, end, len; |
1116 | char command[PATH_MAX*2]; | 1273 | char command[PATH_MAX*2]; |
1117 | FILE *file; | 1274 | FILE *file; |
1118 | 1275 | ||
@@ -1121,13 +1278,6 @@ static void annotate_sym(struct dso *dso, struct symbol *sym) | |||
1121 | if (dso == kernel_dso) | 1278 | if (dso == kernel_dso) |
1122 | filename = vmlinux; | 1279 | filename = vmlinux; |
1123 | 1280 | ||
1124 | printf("\n------------------------------------------------\n"); | ||
1125 | printf(" Percent | Source code & Disassembly of %s\n", filename); | ||
1126 | printf("------------------------------------------------\n"); | ||
1127 | |||
1128 | if (verbose >= 2) | ||
1129 | printf("annotating [%p] %30s : [%p] %30s\n", dso, dso->name, sym, sym->name); | ||
1130 | |||
1131 | start = sym->obj_start; | 1281 | start = sym->obj_start; |
1132 | if (!start) | 1282 | if (!start) |
1133 | start = sym->start; | 1283 | start = sym->start; |
@@ -1135,7 +1285,19 @@ static void annotate_sym(struct dso *dso, struct symbol *sym) | |||
1135 | end = start + sym->end - sym->start + 1; | 1285 | end = start + sym->end - sym->start + 1; |
1136 | len = sym->end - sym->start; | 1286 | len = sym->end - sym->start; |
1137 | 1287 | ||
1138 | sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s", (__u64)start, (__u64)end, filename); | 1288 | if (print_line) { |
1289 | get_source_line(sym, start, len, filename); | ||
1290 | print_summary(filename); | ||
1291 | } | ||
1292 | |||
1293 | printf("\n\n------------------------------------------------\n"); | ||
1294 | printf(" Percent | Source code & Disassembly of %s\n", filename); | ||
1295 | printf("------------------------------------------------\n"); | ||
1296 | |||
1297 | if (verbose >= 2) | ||
1298 | printf("annotating [%p] %30s : [%p] %30s\n", dso, dso->name, sym, sym->name); | ||
1299 | |||
1300 | sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s", (u64)start, (u64)end, filename); | ||
1139 | 1301 | ||
1140 | if (verbose >= 3) | 1302 | if (verbose >= 3) |
1141 | printf("doing: %s\n", command); | 1303 | printf("doing: %s\n", command); |
@@ -1150,6 +1312,8 @@ static void annotate_sym(struct dso *dso, struct symbol *sym) | |||
1150 | } | 1312 | } |
1151 | 1313 | ||
1152 | pclose(file); | 1314 | pclose(file); |
1315 | if (print_line) | ||
1316 | free_source_line(sym, len); | ||
1153 | } | 1317 | } |
1154 | 1318 | ||
1155 | static void find_annotations(void) | 1319 | static void find_annotations(void) |
@@ -1308,6 +1472,8 @@ static const struct option options[] = { | |||
1308 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | 1472 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, |
1309 | "dump raw trace in ASCII"), | 1473 | "dump raw trace in ASCII"), |
1310 | OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"), | 1474 | OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"), |
1475 | OPT_BOOLEAN('l', "print-line", &print_line, | ||
1476 | "print matching source lines (may be slow)"), | ||
1311 | OPT_END() | 1477 | OPT_END() |
1312 | }; | 1478 | }; |
1313 | 1479 | ||