diff options
Diffstat (limited to 'Documentation/perf_counter/builtin-annotate.c')
-rw-r--r-- | Documentation/perf_counter/builtin-annotate.c | 304 |
1 files changed, 184 insertions, 120 deletions
diff --git a/Documentation/perf_counter/builtin-annotate.c b/Documentation/perf_counter/builtin-annotate.c index d656484ec983..116a3978b44c 100644 --- a/Documentation/perf_counter/builtin-annotate.c +++ b/Documentation/perf_counter/builtin-annotate.c | |||
@@ -28,7 +28,7 @@ | |||
28 | static char const *input_name = "perf.data"; | 28 | static char const *input_name = "perf.data"; |
29 | static char *vmlinux = NULL; | 29 | static char *vmlinux = NULL; |
30 | 30 | ||
31 | static char default_sort_order[] = "comm,dso"; | 31 | static char default_sort_order[] = "comm,symbol"; |
32 | static char *sort_order = default_sort_order; | 32 | static char *sort_order = default_sort_order; |
33 | 33 | ||
34 | static int input; | 34 | static int input; |
@@ -38,7 +38,6 @@ static int dump_trace = 0; | |||
38 | #define dprintf(x...) do { if (dump_trace) printf(x); } while (0) | 38 | #define dprintf(x...) do { if (dump_trace) printf(x); } while (0) |
39 | 39 | ||
40 | static int verbose; | 40 | static int verbose; |
41 | static int full_paths; | ||
42 | 41 | ||
43 | static unsigned long page_size; | 42 | static unsigned long page_size; |
44 | static unsigned long mmap_window = 32; | 43 | static unsigned long mmap_window = 32; |
@@ -89,6 +88,7 @@ static LIST_HEAD(dsos); | |||
89 | static struct dso *kernel_dso; | 88 | static struct dso *kernel_dso; |
90 | static struct dso *vdso; | 89 | static struct dso *vdso; |
91 | 90 | ||
91 | |||
92 | static void dsos__add(struct dso *dso) | 92 | static void dsos__add(struct dso *dso) |
93 | { | 93 | { |
94 | list_add_tail(&dso->node, &dsos); | 94 | list_add_tail(&dso->node, &dsos); |
@@ -176,20 +176,6 @@ static int load_kernel(void) | |||
176 | return err; | 176 | return err; |
177 | } | 177 | } |
178 | 178 | ||
179 | static char __cwd[PATH_MAX]; | ||
180 | static char *cwd = __cwd; | ||
181 | static int cwdlen; | ||
182 | |||
183 | static int strcommon(const char *pathname) | ||
184 | { | ||
185 | int n = 0; | ||
186 | |||
187 | while (pathname[n] == cwd[n] && n < cwdlen) | ||
188 | ++n; | ||
189 | |||
190 | return n; | ||
191 | } | ||
192 | |||
193 | struct map { | 179 | struct map { |
194 | struct list_head node; | 180 | struct list_head node; |
195 | uint64_t start; | 181 | uint64_t start; |
@@ -215,17 +201,6 @@ static struct map *map__new(struct mmap_event *event) | |||
215 | 201 | ||
216 | if (self != NULL) { | 202 | if (self != NULL) { |
217 | const char *filename = event->filename; | 203 | const char *filename = event->filename; |
218 | char newfilename[PATH_MAX]; | ||
219 | |||
220 | if (cwd) { | ||
221 | int n = strcommon(filename); | ||
222 | |||
223 | if (n == cwdlen) { | ||
224 | snprintf(newfilename, sizeof(newfilename), | ||
225 | ".%s", filename + n); | ||
226 | filename = newfilename; | ||
227 | } | ||
228 | } | ||
229 | 204 | ||
230 | self->start = event->start; | 205 | self->start = event->start; |
231 | self->end = event->start + event->len; | 206 | self->end = event->start + event->len; |
@@ -669,44 +644,36 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) | |||
669 | return cmp; | 644 | return cmp; |
670 | } | 645 | } |
671 | 646 | ||
672 | static size_t | 647 | /* |
673 | hist_entry__fprintf(FILE *fp, struct hist_entry *self, uint64_t total_samples) | 648 | * collect histogram counts |
649 | */ | ||
650 | static void hist_hit(struct hist_entry *he, uint64_t ip) | ||
674 | { | 651 | { |
675 | struct sort_entry *se; | 652 | unsigned int sym_size, offset; |
676 | size_t ret; | 653 | struct symbol *sym = he->sym; |
677 | 654 | ||
678 | if (total_samples) { | 655 | he->count++; |
679 | double percent = self->count * 100.0 / total_samples; | ||
680 | char *color = PERF_COLOR_NORMAL; | ||
681 | 656 | ||
682 | /* | 657 | if (!sym || !sym->hist) |
683 | * We color high-overhead entries in red, low-overhead | 658 | return; |
684 | * entries in green - and keep the middle ground normal: | ||
685 | */ | ||
686 | if (percent >= 5.0) | ||
687 | color = PERF_COLOR_RED; | ||
688 | if (percent < 0.5) | ||
689 | color = PERF_COLOR_GREEN; | ||
690 | 659 | ||
691 | ret = color_fprintf(fp, color, " %6.2f%%", | 660 | sym_size = sym->end - sym->start; |
692 | (self->count * 100.0) / total_samples); | 661 | offset = ip - sym->start; |
693 | } else | ||
694 | ret = fprintf(fp, "%12d ", self->count); | ||
695 | 662 | ||
696 | list_for_each_entry(se, &hist_entry__sort_list, list) { | 663 | if (offset >= sym_size) |
697 | fprintf(fp, " "); | 664 | return; |
698 | ret += se->print(fp, self); | ||
699 | } | ||
700 | 665 | ||
701 | ret += fprintf(fp, "\n"); | 666 | sym->hist_sum++; |
667 | sym->hist[offset]++; | ||
702 | 668 | ||
703 | return ret; | 669 | if (verbose >= 3) |
670 | printf("%p %s: count++ [ip: %p, %08Lx] => %Ld\n", | ||
671 | (void *)he->sym->start, | ||
672 | he->sym->name, | ||
673 | (void *)ip, ip - he->sym->start, | ||
674 | sym->hist[offset]); | ||
704 | } | 675 | } |
705 | 676 | ||
706 | /* | ||
707 | * collect histogram counts | ||
708 | */ | ||
709 | |||
710 | static int | 677 | static int |
711 | hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, | 678 | hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, |
712 | struct symbol *sym, uint64_t ip, char level) | 679 | struct symbol *sym, uint64_t ip, char level) |
@@ -732,7 +699,8 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, | |||
732 | cmp = hist_entry__cmp(&entry, he); | 699 | cmp = hist_entry__cmp(&entry, he); |
733 | 700 | ||
734 | if (!cmp) { | 701 | if (!cmp) { |
735 | he->count++; | 702 | hist_hit(he, ip); |
703 | |||
736 | return 0; | 704 | return 0; |
737 | } | 705 | } |
738 | 706 | ||
@@ -856,50 +824,6 @@ static void output__resort(void) | |||
856 | } | 824 | } |
857 | } | 825 | } |
858 | 826 | ||
859 | static size_t output__fprintf(FILE *fp, uint64_t total_samples) | ||
860 | { | ||
861 | struct hist_entry *pos; | ||
862 | struct sort_entry *se; | ||
863 | struct rb_node *nd; | ||
864 | size_t ret = 0; | ||
865 | |||
866 | fprintf(fp, "\n"); | ||
867 | fprintf(fp, "#\n"); | ||
868 | fprintf(fp, "# (%Ld samples)\n", (__u64)total_samples); | ||
869 | fprintf(fp, "#\n"); | ||
870 | |||
871 | fprintf(fp, "# Overhead"); | ||
872 | list_for_each_entry(se, &hist_entry__sort_list, list) | ||
873 | fprintf(fp, " %s", se->header); | ||
874 | fprintf(fp, "\n"); | ||
875 | |||
876 | fprintf(fp, "# ........"); | ||
877 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
878 | int i; | ||
879 | |||
880 | fprintf(fp, " "); | ||
881 | for (i = 0; i < strlen(se->header); i++) | ||
882 | fprintf(fp, "."); | ||
883 | } | ||
884 | fprintf(fp, "\n"); | ||
885 | |||
886 | fprintf(fp, "#\n"); | ||
887 | |||
888 | for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) { | ||
889 | pos = rb_entry(nd, struct hist_entry, rb_node); | ||
890 | ret += hist_entry__fprintf(fp, pos, total_samples); | ||
891 | } | ||
892 | |||
893 | if (!strcmp(sort_order, default_sort_order)) { | ||
894 | fprintf(fp, "#\n"); | ||
895 | fprintf(fp, "# (For more details, try: perf annotate --sort comm,dso,symbol)\n"); | ||
896 | fprintf(fp, "#\n"); | ||
897 | } | ||
898 | fprintf(fp, "\n"); | ||
899 | |||
900 | return ret; | ||
901 | } | ||
902 | |||
903 | static void register_idle_thread(void) | 827 | static void register_idle_thread(void) |
904 | { | 828 | { |
905 | struct thread *thread = threads__findnew(0); | 829 | struct thread *thread = threads__findnew(0); |
@@ -1106,6 +1030,149 @@ process_event(event_t *event, unsigned long offset, unsigned long head) | |||
1106 | return 0; | 1030 | return 0; |
1107 | } | 1031 | } |
1108 | 1032 | ||
1033 | static int | ||
1034 | parse_line(FILE *file, struct symbol *sym, uint64_t start, uint64_t len) | ||
1035 | { | ||
1036 | char *line = NULL, *tmp, *tmp2; | ||
1037 | unsigned int offset; | ||
1038 | size_t line_len; | ||
1039 | __u64 line_ip; | ||
1040 | int ret; | ||
1041 | char *c; | ||
1042 | |||
1043 | if (getline(&line, &line_len, file) < 0) | ||
1044 | return -1; | ||
1045 | if (!line) | ||
1046 | return -1; | ||
1047 | |||
1048 | c = strchr(line, '\n'); | ||
1049 | if (c) | ||
1050 | *c = 0; | ||
1051 | |||
1052 | line_ip = -1; | ||
1053 | offset = 0; | ||
1054 | ret = -2; | ||
1055 | |||
1056 | /* | ||
1057 | * Strip leading spaces: | ||
1058 | */ | ||
1059 | tmp = line; | ||
1060 | while (*tmp) { | ||
1061 | if (*tmp != ' ') | ||
1062 | break; | ||
1063 | tmp++; | ||
1064 | } | ||
1065 | |||
1066 | if (*tmp) { | ||
1067 | /* | ||
1068 | * Parse hexa addresses followed by ':' | ||
1069 | */ | ||
1070 | line_ip = strtoull(tmp, &tmp2, 16); | ||
1071 | if (*tmp2 != ':') | ||
1072 | line_ip = -1; | ||
1073 | } | ||
1074 | |||
1075 | if (line_ip != -1) { | ||
1076 | unsigned int hits = 0; | ||
1077 | double percent = 0.0; | ||
1078 | char *color = PERF_COLOR_NORMAL; | ||
1079 | |||
1080 | offset = line_ip - start; | ||
1081 | if (offset < len) | ||
1082 | hits = sym->hist[offset]; | ||
1083 | |||
1084 | if (sym->hist_sum) | ||
1085 | percent = 100.0 * hits / sym->hist_sum; | ||
1086 | |||
1087 | /* | ||
1088 | * We color high-overhead entries in red, low-overhead | ||
1089 | * entries in green - and keep the middle ground normal: | ||
1090 | */ | ||
1091 | if (percent >= 5.0) | ||
1092 | color = PERF_COLOR_RED; | ||
1093 | else { | ||
1094 | if (percent > 0.5) | ||
1095 | color = PERF_COLOR_GREEN; | ||
1096 | } | ||
1097 | |||
1098 | color_fprintf(stdout, color, " %7.2f", percent); | ||
1099 | printf(" : "); | ||
1100 | color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", line); | ||
1101 | } else { | ||
1102 | if (!*line) | ||
1103 | printf(" :\n"); | ||
1104 | else | ||
1105 | printf(" : %s\n", line); | ||
1106 | } | ||
1107 | |||
1108 | return 0; | ||
1109 | } | ||
1110 | |||
1111 | static void annotate_sym(struct dso *dso, struct symbol *sym) | ||
1112 | { | ||
1113 | char *filename = dso->name; | ||
1114 | uint64_t start, end, len; | ||
1115 | char command[PATH_MAX*2]; | ||
1116 | FILE *file; | ||
1117 | |||
1118 | if (!filename) | ||
1119 | return; | ||
1120 | if (dso == kernel_dso) | ||
1121 | filename = vmlinux; | ||
1122 | |||
1123 | printf("\n------------------------------------------------\n"); | ||
1124 | printf(" Percent | Source code & Disassembly of %s\n", filename); | ||
1125 | printf("------------------------------------------------\n"); | ||
1126 | |||
1127 | if (verbose >= 2) | ||
1128 | printf("annotating [%p] %30s : [%p] %30s\n", dso, dso->name, sym, sym->name); | ||
1129 | |||
1130 | start = sym->obj_start; | ||
1131 | if (!start) | ||
1132 | start = sym->start; | ||
1133 | |||
1134 | end = start + sym->end - sym->start + 1; | ||
1135 | len = sym->end - sym->start; | ||
1136 | |||
1137 | sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s", (__u64)start, (__u64)end, filename); | ||
1138 | |||
1139 | if (verbose >= 3) | ||
1140 | printf("doing: %s\n", command); | ||
1141 | |||
1142 | file = popen(command, "r"); | ||
1143 | if (!file) | ||
1144 | return; | ||
1145 | |||
1146 | while (!feof(file)) { | ||
1147 | if (parse_line(file, sym, start, len) < 0) | ||
1148 | break; | ||
1149 | } | ||
1150 | |||
1151 | pclose(file); | ||
1152 | } | ||
1153 | |||
1154 | static void find_annotations(void) | ||
1155 | { | ||
1156 | struct rb_node *nd; | ||
1157 | struct dso *dso; | ||
1158 | int count = 0; | ||
1159 | |||
1160 | list_for_each_entry(dso, &dsos, node) { | ||
1161 | |||
1162 | for (nd = rb_first(&dso->syms); nd; nd = rb_next(nd)) { | ||
1163 | struct symbol *sym = rb_entry(nd, struct symbol, rb_node); | ||
1164 | |||
1165 | if (sym->hist) { | ||
1166 | annotate_sym(dso, sym); | ||
1167 | count++; | ||
1168 | } | ||
1169 | } | ||
1170 | } | ||
1171 | |||
1172 | if (!count) | ||
1173 | printf(" Error: symbol '%s' not present amongst the samples.\n", sym_hist_filter); | ||
1174 | } | ||
1175 | |||
1109 | static int __cmd_annotate(void) | 1176 | static int __cmd_annotate(void) |
1110 | { | 1177 | { |
1111 | int ret, rc = EXIT_FAILURE; | 1178 | int ret, rc = EXIT_FAILURE; |
@@ -1140,16 +1207,6 @@ static int __cmd_annotate(void) | |||
1140 | return EXIT_FAILURE; | 1207 | return EXIT_FAILURE; |
1141 | } | 1208 | } |
1142 | 1209 | ||
1143 | if (!full_paths) { | ||
1144 | if (getcwd(__cwd, sizeof(__cwd)) == NULL) { | ||
1145 | perror("failed to get the current directory"); | ||
1146 | return EXIT_FAILURE; | ||
1147 | } | ||
1148 | cwdlen = strlen(cwd); | ||
1149 | } else { | ||
1150 | cwd = NULL; | ||
1151 | cwdlen = 0; | ||
1152 | } | ||
1153 | remap: | 1210 | remap: |
1154 | buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ, | 1211 | buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ, |
1155 | MAP_SHARED, input, offset); | 1212 | MAP_SHARED, input, offset); |
@@ -1229,7 +1286,8 @@ more: | |||
1229 | 1286 | ||
1230 | collapse__resort(); | 1287 | collapse__resort(); |
1231 | output__resort(); | 1288 | output__resort(); |
1232 | output__fprintf(stdout, total); | 1289 | |
1290 | find_annotations(); | ||
1233 | 1291 | ||
1234 | return rc; | 1292 | return rc; |
1235 | } | 1293 | } |
@@ -1242,15 +1300,13 @@ static const char * const annotate_usage[] = { | |||
1242 | static const struct option options[] = { | 1300 | static const struct option options[] = { |
1243 | OPT_STRING('i', "input", &input_name, "file", | 1301 | OPT_STRING('i', "input", &input_name, "file", |
1244 | "input file name"), | 1302 | "input file name"), |
1303 | OPT_STRING('s', "symbol", &sym_hist_filter, "file", | ||
1304 | "symbol to annotate"), | ||
1245 | OPT_BOOLEAN('v', "verbose", &verbose, | 1305 | OPT_BOOLEAN('v', "verbose", &verbose, |
1246 | "be more verbose (show symbol address, etc)"), | 1306 | "be more verbose (show symbol address, etc)"), |
1247 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | 1307 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, |
1248 | "dump raw trace in ASCII"), | 1308 | "dump raw trace in ASCII"), |
1249 | OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"), | 1309 | OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"), |
1250 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", | ||
1251 | "sort by key(s): pid, comm, dso, symbol. Default: pid,symbol"), | ||
1252 | OPT_BOOLEAN('P', "full-paths", &full_paths, | ||
1253 | "Don't shorten the pathnames taking into account the cwd"), | ||
1254 | OPT_END() | 1310 | OPT_END() |
1255 | }; | 1311 | }; |
1256 | 1312 | ||
@@ -1279,10 +1335,18 @@ int cmd_annotate(int argc, const char **argv, const char *prefix) | |||
1279 | 1335 | ||
1280 | setup_sorting(); | 1336 | setup_sorting(); |
1281 | 1337 | ||
1282 | /* | 1338 | if (argc) { |
1283 | * Any (unrecognized) arguments left? | 1339 | /* |
1284 | */ | 1340 | * Special case: if there's an argument left then assume tha |
1285 | if (argc) | 1341 | * it's a symbol filter: |
1342 | */ | ||
1343 | if (argc > 1) | ||
1344 | usage_with_options(annotate_usage, options); | ||
1345 | |||
1346 | sym_hist_filter = argv[0]; | ||
1347 | } | ||
1348 | |||
1349 | if (!sym_hist_filter) | ||
1286 | usage_with_options(annotate_usage, options); | 1350 | usage_with_options(annotate_usage, options); |
1287 | 1351 | ||
1288 | setup_pager(); | 1352 | setup_pager(); |