aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/perf_counter/Documentation/perf-annotate.txt9
-rw-r--r--Documentation/perf_counter/builtin-annotate.c304
-rw-r--r--Documentation/perf_counter/util/symbol.c50
-rw-r--r--Documentation/perf_counter/util/symbol.h5
4 files changed, 229 insertions, 139 deletions
diff --git a/Documentation/perf_counter/Documentation/perf-annotate.txt b/Documentation/perf_counter/Documentation/perf-annotate.txt
index a9d6d5ee2701..c9dcade06831 100644
--- a/Documentation/perf_counter/Documentation/perf-annotate.txt
+++ b/Documentation/perf_counter/Documentation/perf-annotate.txt
@@ -3,7 +3,7 @@ perf-annotate(1)
3 3
4NAME 4NAME
5---- 5----
6perf-annotate - Read perf.data (created by perf record) and annotate functions 6perf-annotate - Read perf.data (created by perf record) and display annotated code
7 7
8SYNOPSIS 8SYNOPSIS
9-------- 9--------
@@ -12,8 +12,11 @@ SYNOPSIS
12 12
13DESCRIPTION 13DESCRIPTION
14----------- 14-----------
15This command displays the performance counter profile information recorded 15This command reads the input file and displays an annotated version of the
16via perf record. 16code. If the object file has debug symbols then the source code will be
17displayed alongside assembly code.
18
19If there is no debug info in the object, then annotated assembly is displayed.
17 20
18OPTIONS 21OPTIONS
19------- 22-------
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();
diff --git a/Documentation/perf_counter/util/symbol.c b/Documentation/perf_counter/util/symbol.c
index a06bbfba8350..23f4f7b3b83d 100644
--- a/Documentation/perf_counter/util/symbol.c
+++ b/Documentation/perf_counter/util/symbol.c
@@ -7,21 +7,36 @@
7#include <gelf.h> 7#include <gelf.h>
8#include <elf.h> 8#include <elf.h>
9 9
10const char *sym_hist_filter;
11
10static struct symbol *symbol__new(uint64_t start, uint64_t len, 12static struct symbol *symbol__new(uint64_t start, uint64_t len,
11 const char *name, unsigned int priv_size) 13 const char *name, unsigned int priv_size,
14 uint64_t obj_start, int verbose)
12{ 15{
13 size_t namelen = strlen(name) + 1; 16 size_t namelen = strlen(name) + 1;
14 struct symbol *self = malloc(priv_size + sizeof(*self) + namelen); 17 struct symbol *self = calloc(1, priv_size + sizeof(*self) + namelen);
15 18
16 if (self != NULL) { 19 if (!self)
17 if (priv_size) { 20 return NULL;
18 memset(self, 0, priv_size); 21
19 self = ((void *)self) + priv_size; 22 if (verbose >= 2)
20 } 23 printf("new symbol: %016Lx [%08lx]: %s, hist: %p, obj_start: %p\n",
21 self->start = start; 24 (__u64)start, len, name, self->hist, (void *)obj_start);
22 self->end = start + len - 1; 25
23 memcpy(self->name, name, namelen); 26 self->obj_start= obj_start;
27 self->hist = NULL;
28 self->hist_sum = 0;
29
30 if (sym_hist_filter && !strcmp(name, sym_hist_filter))
31 self->hist = calloc(sizeof(__u64), len);
32
33 if (priv_size) {
34 memset(self, 0, priv_size);
35 self = ((void *)self) + priv_size;
24 } 36 }
37 self->start = start;
38 self->end = start + len - 1;
39 memcpy(self->name, name, namelen);
25 40
26 return self; 41 return self;
27} 42}
@@ -166,7 +181,7 @@ static int dso__load_kallsyms(struct dso *self, symbol_filter_t filter, int verb
166 * Well fix up the end later, when we have all sorted. 181 * Well fix up the end later, when we have all sorted.
167 */ 182 */
168 sym = symbol__new(start, 0xdead, line + len + 2, 183 sym = symbol__new(start, 0xdead, line + len + 2,
169 self->sym_priv_size); 184 self->sym_priv_size, 0, verbose);
170 185
171 if (sym == NULL) 186 if (sym == NULL)
172 goto out_delete_line; 187 goto out_delete_line;
@@ -272,7 +287,7 @@ static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,
272static int dso__synthesize_plt_symbols(struct dso *self, Elf *elf, 287static int dso__synthesize_plt_symbols(struct dso *self, Elf *elf,
273 GElf_Ehdr *ehdr, Elf_Scn *scn_dynsym, 288 GElf_Ehdr *ehdr, Elf_Scn *scn_dynsym,
274 GElf_Shdr *shdr_dynsym, 289 GElf_Shdr *shdr_dynsym,
275 size_t dynsym_idx) 290 size_t dynsym_idx, int verbose)
276{ 291{
277 uint32_t nr_rel_entries, idx; 292 uint32_t nr_rel_entries, idx;
278 GElf_Sym sym; 293 GElf_Sym sym;
@@ -335,7 +350,7 @@ static int dso__synthesize_plt_symbols(struct dso *self, Elf *elf,
335 "%s@plt", elf_sym__name(&sym, symstrs)); 350 "%s@plt", elf_sym__name(&sym, symstrs));
336 351
337 f = symbol__new(plt_offset, shdr_plt.sh_entsize, 352 f = symbol__new(plt_offset, shdr_plt.sh_entsize,
338 sympltname, self->sym_priv_size); 353 sympltname, self->sym_priv_size, 0, verbose);
339 if (!f) 354 if (!f)
340 return -1; 355 return -1;
341 356
@@ -353,7 +368,7 @@ static int dso__synthesize_plt_symbols(struct dso *self, Elf *elf,
353 "%s@plt", elf_sym__name(&sym, symstrs)); 368 "%s@plt", elf_sym__name(&sym, symstrs));
354 369
355 f = symbol__new(plt_offset, shdr_plt.sh_entsize, 370 f = symbol__new(plt_offset, shdr_plt.sh_entsize,
356 sympltname, self->sym_priv_size); 371 sympltname, self->sym_priv_size, 0, verbose);
357 if (!f) 372 if (!f)
358 return -1; 373 return -1;
359 374
@@ -410,7 +425,7 @@ static int dso__load_sym(struct dso *self, int fd, const char *name,
410 if (sec_dynsym != NULL) { 425 if (sec_dynsym != NULL) {
411 nr = dso__synthesize_plt_symbols(self, elf, &ehdr, 426 nr = dso__synthesize_plt_symbols(self, elf, &ehdr,
412 sec_dynsym, &shdr, 427 sec_dynsym, &shdr,
413 dynsym_idx); 428 dynsym_idx, verbose);
414 if (nr < 0) 429 if (nr < 0)
415 goto out_elf_end; 430 goto out_elf_end;
416 } 431 }
@@ -444,6 +459,7 @@ static int dso__load_sym(struct dso *self, int fd, const char *name,
444 459
445 elf_symtab__for_each_symbol(syms, nr_syms, index, sym) { 460 elf_symtab__for_each_symbol(syms, nr_syms, index, sym) {
446 struct symbol *f; 461 struct symbol *f;
462 uint64_t obj_start;
447 463
448 if (!elf_sym__is_function(&sym)) 464 if (!elf_sym__is_function(&sym))
449 continue; 465 continue;
@@ -453,11 +469,13 @@ static int dso__load_sym(struct dso *self, int fd, const char *name,
453 goto out_elf_end; 469 goto out_elf_end;
454 470
455 gelf_getshdr(sec, &shdr); 471 gelf_getshdr(sec, &shdr);
472 obj_start = sym.st_value;
473
456 sym.st_value -= shdr.sh_addr - shdr.sh_offset; 474 sym.st_value -= shdr.sh_addr - shdr.sh_offset;
457 475
458 f = symbol__new(sym.st_value, sym.st_size, 476 f = symbol__new(sym.st_value, sym.st_size,
459 elf_sym__name(&sym, symstrs), 477 elf_sym__name(&sym, symstrs),
460 self->sym_priv_size); 478 self->sym_priv_size, obj_start, verbose);
461 if (!f) 479 if (!f)
462 goto out_elf_end; 480 goto out_elf_end;
463 481
diff --git a/Documentation/perf_counter/util/symbol.h b/Documentation/perf_counter/util/symbol.h
index e23cc3126684..4839d68f14f0 100644
--- a/Documentation/perf_counter/util/symbol.h
+++ b/Documentation/perf_counter/util/symbol.h
@@ -9,6 +9,9 @@ struct symbol {
9 struct rb_node rb_node; 9 struct rb_node rb_node;
10 __u64 start; 10 __u64 start;
11 __u64 end; 11 __u64 end;
12 __u64 obj_start;
13 __u64 hist_sum;
14 __u64 *hist;
12 char name[0]; 15 char name[0];
13}; 16};
14 17
@@ -20,6 +23,8 @@ struct dso {
20 char name[0]; 23 char name[0];
21}; 24};
22 25
26const char *sym_hist_filter;
27
23typedef int (*symbol_filter_t)(struct dso *self, struct symbol *sym); 28typedef int (*symbol_filter_t)(struct dso *self, struct symbol *sym);
24 29
25struct dso *dso__new(const char *name, unsigned int sym_priv_size); 30struct dso *dso__new(const char *name, unsigned int sym_priv_size);