diff options
-rw-r--r-- | tools/perf/builtin-annotate.c | 98 | ||||
-rw-r--r-- | tools/perf/util/symbol.h | 1 |
2 files changed, 98 insertions, 1 deletions
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index b1ed5f766cb3..6a08da41f76b 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
@@ -39,6 +39,8 @@ static int dump_trace = 0; | |||
39 | 39 | ||
40 | static int verbose; | 40 | static int verbose; |
41 | 41 | ||
42 | static int print_line; | ||
43 | |||
42 | static unsigned long page_size; | 44 | static unsigned long page_size; |
43 | static unsigned long mmap_window = 32; | 45 | static unsigned long mmap_window = 32; |
44 | 46 | ||
@@ -84,6 +86,12 @@ typedef union event_union { | |||
84 | struct period_event period; | 86 | struct period_event period; |
85 | } event_t; | 87 | } event_t; |
86 | 88 | ||
89 | |||
90 | struct sym_ext { | ||
91 | double percent; | ||
92 | char *path; | ||
93 | }; | ||
94 | |||
87 | static LIST_HEAD(dsos); | 95 | static LIST_HEAD(dsos); |
88 | static struct dso *kernel_dso; | 96 | static struct dso *kernel_dso; |
89 | static struct dso *vdso; | 97 | static struct dso *vdso; |
@@ -1034,6 +1042,8 @@ static int | |||
1034 | parse_line(FILE *file, struct symbol *sym, __u64 start, __u64 len) | 1042 | parse_line(FILE *file, struct symbol *sym, __u64 start, __u64 len) |
1035 | { | 1043 | { |
1036 | char *line = NULL, *tmp, *tmp2; | 1044 | char *line = NULL, *tmp, *tmp2; |
1045 | static const char *prev_line; | ||
1046 | static const char *prev_color; | ||
1037 | unsigned int offset; | 1047 | unsigned int offset; |
1038 | size_t line_len; | 1048 | size_t line_len; |
1039 | __u64 line_ip; | 1049 | __u64 line_ip; |
@@ -1073,15 +1083,20 @@ parse_line(FILE *file, struct symbol *sym, __u64 start, __u64 len) | |||
1073 | } | 1083 | } |
1074 | 1084 | ||
1075 | if (line_ip != -1) { | 1085 | if (line_ip != -1) { |
1086 | const char *path = NULL; | ||
1076 | unsigned int hits = 0; | 1087 | unsigned int hits = 0; |
1077 | double percent = 0.0; | 1088 | double percent = 0.0; |
1078 | char *color = PERF_COLOR_NORMAL; | 1089 | char *color = PERF_COLOR_NORMAL; |
1090 | struct sym_ext *sym_ext = sym->priv; | ||
1079 | 1091 | ||
1080 | offset = line_ip - start; | 1092 | offset = line_ip - start; |
1081 | if (offset < len) | 1093 | if (offset < len) |
1082 | hits = sym->hist[offset]; | 1094 | hits = sym->hist[offset]; |
1083 | 1095 | ||
1084 | if (sym->hist_sum) | 1096 | if (sym_ext) { |
1097 | path = sym_ext[offset].path; | ||
1098 | percent = sym_ext[offset].percent; | ||
1099 | } else if (sym->hist_sum) | ||
1085 | percent = 100.0 * hits / sym->hist_sum; | 1100 | percent = 100.0 * hits / sym->hist_sum; |
1086 | 1101 | ||
1087 | /* | 1102 | /* |
@@ -1096,6 +1111,20 @@ parse_line(FILE *file, struct symbol *sym, __u64 start, __u64 len) | |||
1096 | color = PERF_COLOR_GREEN; | 1111 | color = PERF_COLOR_GREEN; |
1097 | } | 1112 | } |
1098 | 1113 | ||
1114 | /* | ||
1115 | * Also color the filename and line if needed, with | ||
1116 | * the same color than the percentage. Don't print it | ||
1117 | * twice for close colored ip with the same filename:line | ||
1118 | */ | ||
1119 | if (path) { | ||
1120 | if (!prev_line || strcmp(prev_line, path) | ||
1121 | || color != prev_color) { | ||
1122 | color_fprintf(stdout, color, " %s", path); | ||
1123 | prev_line = path; | ||
1124 | prev_color = color; | ||
1125 | } | ||
1126 | } | ||
1127 | |||
1099 | color_fprintf(stdout, color, " %7.2f", percent); | 1128 | color_fprintf(stdout, color, " %7.2f", percent); |
1100 | printf(" : "); | 1129 | printf(" : "); |
1101 | color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", line); | 1130 | color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", line); |
@@ -1109,6 +1138,67 @@ parse_line(FILE *file, struct symbol *sym, __u64 start, __u64 len) | |||
1109 | return 0; | 1138 | return 0; |
1110 | } | 1139 | } |
1111 | 1140 | ||
1141 | static void free_source_line(struct symbol *sym, int len) | ||
1142 | { | ||
1143 | struct sym_ext *sym_ext = sym->priv; | ||
1144 | int i; | ||
1145 | |||
1146 | if (!sym_ext) | ||
1147 | return; | ||
1148 | |||
1149 | for (i = 0; i < len; i++) | ||
1150 | free(sym_ext[i].path); | ||
1151 | free(sym_ext); | ||
1152 | |||
1153 | sym->priv = NULL; | ||
1154 | } | ||
1155 | |||
1156 | /* Get the filename:line for the colored entries */ | ||
1157 | static void get_source_line(struct symbol *sym, __u64 start, int len) | ||
1158 | { | ||
1159 | int i; | ||
1160 | char cmd[PATH_MAX * 2]; | ||
1161 | struct sym_ext *sym_ext; | ||
1162 | |||
1163 | if (!sym->hist_sum) | ||
1164 | return; | ||
1165 | |||
1166 | sym->priv = calloc(len, sizeof(struct sym_ext)); | ||
1167 | if (!sym->priv) | ||
1168 | return; | ||
1169 | |||
1170 | sym_ext = sym->priv; | ||
1171 | |||
1172 | for (i = 0; i < len; i++) { | ||
1173 | char *path = NULL; | ||
1174 | size_t line_len; | ||
1175 | __u64 offset; | ||
1176 | FILE *fp; | ||
1177 | |||
1178 | sym_ext[i].percent = 100.0 * sym->hist[i] / sym->hist_sum; | ||
1179 | if (sym_ext[i].percent <= 0.5) | ||
1180 | continue; | ||
1181 | |||
1182 | offset = start + i; | ||
1183 | sprintf(cmd, "addr2line -e %s %016llx", vmlinux, offset); | ||
1184 | fp = popen(cmd, "r"); | ||
1185 | if (!fp) | ||
1186 | continue; | ||
1187 | |||
1188 | if (getline(&path, &line_len, fp) < 0 || !line_len) | ||
1189 | goto next; | ||
1190 | |||
1191 | sym_ext[i].path = malloc(sizeof(char) * line_len); | ||
1192 | if (!sym_ext[i].path) | ||
1193 | goto next; | ||
1194 | |||
1195 | strcpy(sym_ext[i].path, path); | ||
1196 | |||
1197 | next: | ||
1198 | pclose(fp); | ||
1199 | } | ||
1200 | } | ||
1201 | |||
1112 | static void annotate_sym(struct dso *dso, struct symbol *sym) | 1202 | static void annotate_sym(struct dso *dso, struct symbol *sym) |
1113 | { | 1203 | { |
1114 | char *filename = dso->name; | 1204 | char *filename = dso->name; |
@@ -1135,6 +1225,9 @@ static void annotate_sym(struct dso *dso, struct symbol *sym) | |||
1135 | end = start + sym->end - sym->start + 1; | 1225 | end = start + sym->end - sym->start + 1; |
1136 | len = sym->end - sym->start; | 1226 | len = sym->end - sym->start; |
1137 | 1227 | ||
1228 | if (print_line) | ||
1229 | get_source_line(sym, start, len); | ||
1230 | |||
1138 | sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s", (__u64)start, (__u64)end, filename); | 1231 | sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s", (__u64)start, (__u64)end, filename); |
1139 | 1232 | ||
1140 | if (verbose >= 3) | 1233 | if (verbose >= 3) |
@@ -1150,6 +1243,7 @@ static void annotate_sym(struct dso *dso, struct symbol *sym) | |||
1150 | } | 1243 | } |
1151 | 1244 | ||
1152 | pclose(file); | 1245 | pclose(file); |
1246 | free_source_line(sym, len); | ||
1153 | } | 1247 | } |
1154 | 1248 | ||
1155 | static void find_annotations(void) | 1249 | static void find_annotations(void) |
@@ -1308,6 +1402,8 @@ static const struct option options[] = { | |||
1308 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | 1402 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, |
1309 | "dump raw trace in ASCII"), | 1403 | "dump raw trace in ASCII"), |
1310 | OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"), | 1404 | OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"), |
1405 | OPT_BOOLEAN('l', "print-line", &print_line, | ||
1406 | "print matching source lines (may be slow)"), | ||
1311 | OPT_END() | 1407 | OPT_END() |
1312 | }; | 1408 | }; |
1313 | 1409 | ||
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 0d1292bd8270..5ad9b06c3f6f 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -12,6 +12,7 @@ struct symbol { | |||
12 | __u64 obj_start; | 12 | __u64 obj_start; |
13 | __u64 hist_sum; | 13 | __u64 hist_sum; |
14 | __u64 *hist; | 14 | __u64 *hist; |
15 | void *priv; | ||
15 | char name[0]; | 16 | char name[0]; |
16 | }; | 17 | }; |
17 | 18 | ||