aboutsummaryrefslogtreecommitdiffstats
path: root/Documentation/perf_counter/builtin-annotate.c
diff options
context:
space:
mode:
authorIngo Molnar <mingo@elte.hu>2009-06-06 09:48:52 -0400
committerIngo Molnar <mingo@elte.hu>2009-06-06 12:58:31 -0400
commit0b73da3f40128eab6ca2a07508f424029a1edaeb (patch)
tree06c6c2f92bb5dc0b8ae2b03e0fa7320171d6b492 /Documentation/perf_counter/builtin-annotate.c
parent8035e4288078cb806e7dd6bafe4d3e54d44cab3f (diff)
perf_counter tools: Add 'perf annotate' feature
Add new perf sub-command to display annotated source code: $ perf annotate decode_tree_entry ------------------------------------------------ Percent | Source code & Disassembly of /home/mingo/git/git ------------------------------------------------ : : /home/mingo/git/git: file format elf64-x86-64 : : : Disassembly of section .text: : : 00000000004a0da0 <decode_tree_entry>: : *modep = mode; : return str; : } : : static void decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned long size) : { 3.82 : 4a0da0: 41 54 push %r12 : const char *path; : unsigned int mode, len; : : if (size < 24 || buf[size - 21]) 0.17 : 4a0da2: 48 83 fa 17 cmp $0x17,%rdx : *modep = mode; : return str; : } : : static void decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned long size) : { 0.00 : 4a0da6: 49 89 fc mov %rdi,%r12 0.00 : 4a0da9: 55 push %rbp 3.37 : 4a0daa: 53 push %rbx : const char *path; : unsigned int mode, len; : : if (size < 24 || buf[size - 21]) 0.08 : 4a0dab: 76 73 jbe 4a0e20 <decode_tree_entry+0x80> 0.00 : 4a0dad: 80 7c 16 eb 00 cmpb $0x0,-0x15(%rsi,%rdx,1) 3.48 : 4a0db2: 75 6c jne 4a0e20 <decode_tree_entry+0x80> : static const char *get_mode(const char *str, unsigned int *modep) : { : unsigned char c; : unsigned int mode = 0; : : if (*str == ' ') 1.94 : 4a0db4: 0f b6 06 movzbl (%rsi),%eax 0.39 : 4a0db7: 3c 20 cmp $0x20,%al 0.00 : 4a0db9: 74 65 je 4a0e20 <decode_tree_entry+0x80> : return NULL; : : while ((c = *str++) != ' ') { 0.06 : 4a0dbb: 89 c2 mov %eax,%edx : if (c < '0' || c > '7') 1.99 : 4a0dbd: 31 ed xor %ebp,%ebp : unsigned int mode = 0; : : if (*str == ' ') : return NULL; : : while ((c = *str++) != ' ') { 1.74 : 4a0dbf: 48 8d 5e 01 lea 0x1(%rsi),%rbx : if (c < '0' || c > '7') 0.00 : 4a0dc3: 8d 42 d0 lea -0x30(%rdx),%eax 0.17 : 4a0dc6: 3c 07 cmp $0x7,%al 0.00 : 4a0dc8: 76 0d jbe 4a0dd7 <decode_tree_entry+0x37> 0.00 : 4a0dca: eb 54 jmp 4a0e20 <decode_tree_entry+0x80> 0.00 : 4a0dcc: 0f 1f 40 00 nopl 0x0(%rax) 16.57 : 4a0dd0: 8d 42 d0 lea -0x30(%rdx),%eax 0.14 : 4a0dd3: 3c 07 cmp $0x7,%al 0.00 : 4a0dd5: 77 49 ja 4a0e20 <decode_tree_entry+0x80> : return NULL; : mode = (mode << 3) + (c - '0'); 3.12 : 4a0dd7: 0f b6 c2 movzbl %dl,%eax : unsigned int mode = 0; : : if (*str == ' ') : return NULL; : : while ((c = *str++) != ' ') { 0.00 : 4a0dda: 0f b6 13 movzbl (%rbx),%edx 16.74 : 4a0ddd: 48 83 c3 01 add $0x1,%rbx : if (c < '0' || c > '7') : return NULL; : mode = (mode << 3) + (c - '0'); The first column is the percentage of samples that arrived on that particular line - relative to the total cost of the function. Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Mike Galbraith <efault@gmx.de> Cc: Paul Mackerras <paulus@samba.org> Cc: Arnaldo Carvalho de Melo <acme@redhat.com> LKML-Reference: <new-submission> Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'Documentation/perf_counter/builtin-annotate.c')
-rw-r--r--Documentation/perf_counter/builtin-annotate.c304
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 @@
28static char const *input_name = "perf.data"; 28static char const *input_name = "perf.data";
29static char *vmlinux = NULL; 29static char *vmlinux = NULL;
30 30
31static char default_sort_order[] = "comm,dso"; 31static char default_sort_order[] = "comm,symbol";
32static char *sort_order = default_sort_order; 32static char *sort_order = default_sort_order;
33 33
34static int input; 34static 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
40static int verbose; 40static int verbose;
41static int full_paths;
42 41
43static unsigned long page_size; 42static unsigned long page_size;
44static unsigned long mmap_window = 32; 43static unsigned long mmap_window = 32;
@@ -89,6 +88,7 @@ static LIST_HEAD(dsos);
89static struct dso *kernel_dso; 88static struct dso *kernel_dso;
90static struct dso *vdso; 89static struct dso *vdso;
91 90
91
92static void dsos__add(struct dso *dso) 92static 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
179static char __cwd[PATH_MAX];
180static char *cwd = __cwd;
181static int cwdlen;
182
183static 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
193struct map { 179struct 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
672static size_t 647/*
673hist_entry__fprintf(FILE *fp, struct hist_entry *self, uint64_t total_samples) 648 * collect histogram counts
649 */
650static 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
710static int 677static int
711hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, 678hist_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
859static 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
903static void register_idle_thread(void) 827static 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
1033static int
1034parse_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
1111static 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
1154static 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
1109static int __cmd_annotate(void) 1176static 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 }
1153remap: 1210remap:
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[] = {
1242static const struct option options[] = { 1300static 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();