diff options
| author | Ingo Molnar <mingo@elte.hu> | 2009-06-06 09:48:52 -0400 |
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2009-06-06 12:58:31 -0400 |
| commit | 0b73da3f40128eab6ca2a07508f424029a1edaeb (patch) | |
| tree | 06c6c2f92bb5dc0b8ae2b03e0fa7320171d6b492 | |
| parent | 8035e4288078cb806e7dd6bafe4d3e54d44cab3f (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>
| -rw-r--r-- | Documentation/perf_counter/Documentation/perf-annotate.txt | 9 | ||||
| -rw-r--r-- | Documentation/perf_counter/builtin-annotate.c | 304 | ||||
| -rw-r--r-- | Documentation/perf_counter/util/symbol.c | 50 | ||||
| -rw-r--r-- | Documentation/perf_counter/util/symbol.h | 5 |
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 | ||
| 4 | NAME | 4 | NAME |
| 5 | ---- | 5 | ---- |
| 6 | perf-annotate - Read perf.data (created by perf record) and annotate functions | 6 | perf-annotate - Read perf.data (created by perf record) and display annotated code |
| 7 | 7 | ||
| 8 | SYNOPSIS | 8 | SYNOPSIS |
| 9 | -------- | 9 | -------- |
| @@ -12,8 +12,11 @@ SYNOPSIS | |||
| 12 | 12 | ||
| 13 | DESCRIPTION | 13 | DESCRIPTION |
| 14 | ----------- | 14 | ----------- |
| 15 | This command displays the performance counter profile information recorded | 15 | This command reads the input file and displays an annotated version of the |
| 16 | via perf record. | 16 | code. If the object file has debug symbols then the source code will be |
| 17 | displayed alongside assembly code. | ||
| 18 | |||
| 19 | If there is no debug info in the object, then annotated assembly is displayed. | ||
| 17 | 20 | ||
| 18 | OPTIONS | 21 | OPTIONS |
| 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 @@ | |||
| 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(); |
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 | ||
| 10 | const char *sym_hist_filter; | ||
| 11 | |||
| 10 | static struct symbol *symbol__new(uint64_t start, uint64_t len, | 12 | static 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, | |||
| 272 | static int dso__synthesize_plt_symbols(struct dso *self, Elf *elf, | 287 | static 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 | ||
| 26 | const char *sym_hist_filter; | ||
| 27 | |||
| 23 | typedef int (*symbol_filter_t)(struct dso *self, struct symbol *sym); | 28 | typedef int (*symbol_filter_t)(struct dso *self, struct symbol *sym); |
| 24 | 29 | ||
| 25 | struct dso *dso__new(const char *name, unsigned int sym_priv_size); | 30 | struct dso *dso__new(const char *name, unsigned int sym_priv_size); |
