aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArnaldo Carvalho de Melo <acme@redhat.com>2009-11-04 15:50:43 -0500
committerIngo Molnar <mingo@elte.hu>2009-11-08 04:44:36 -0500
commit8d06367fa79c053a4a56a2ce0bb9e840f5da1236 (patch)
tree8ea057102e390c8411b77aeab7ea9c06ec0e0561
parent2643ce11457a99a85c5bed8dd631e35968e6ca5a (diff)
perf symbols: Use the buildids if present
With this change 'perf record' will intercept PERF_RECORD_MMAP calls, creating a linked list of DSOs, then when the session finishes, it will traverse this list and read the buildids, stashing them at the end of the file and will set up a new feature bit in the header bitmask. 'perf report' will then notice this feature and populate the 'dsos' list and set the build ids. When reading the symtabs it will refuse to load from a file that doesn't have the same build id. This improves the reliability of the profiler output, as symbols and profiling data is more guaranteed to match. Example: [root@doppio ~]# perf report | head /home/acme/bin/perf with build id b1ea544ac3746e7538972548a09aadecc5753868 not found, continuing without symbols # Samples: 2621434559 # # Overhead Command Shared Object Symbol # ........ ............... ............................. ...... # 7.91% init [kernel] [k] read_hpet 7.64% init [kernel] [k] mwait_idle_with_hints 7.60% swapper [kernel] [k] read_hpet 7.60% swapper [kernel] [k] mwait_idle_with_hints 3.65% init [kernel] [k] 0xffffffffa02339d9 [root@doppio ~]# In this case the 'perf' binary was an older one, vanished, so its symbols probably wouldn't match or would cause subtly different (and misleading) output. Next patches will support the kernel as well, reading the build id notes for it and the modules from /sys. Another patch should also introduce a new plumbing command: 'perf list-buildids' that will then be used in porcelain that is distro specific to fetch -debuginfo packages where such buildids are present. This will in turn allow for one to run 'perf record' in one machine and 'perf report' in another. Future work on having the buildid sent directly from the kernel in the PERF_RECORD_MMAP event is needed to close races, as the DSO can be changed during a 'perf record' session, but this patch at least helps with non-corner cases and current/older kernels. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com> Cc: Christoph Hellwig <hch@infradead.org> Cc: Frank Ch. Eigler <fche@redhat.com> Cc: Frederic Weisbecker <fweisbec@gmail.com> Cc: Jason Baron <jbaron@redhat.com> Cc: Jim Keniston <jkenisto@us.ibm.com> Cc: K. Prasad <prasad@linux.vnet.ibm.com> Cc: Masami Hiramatsu <mhiramat@redhat.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Roland McGrath <roland@redhat.com> Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com> Cc: Steven Rostedt <rostedt@goodmis.org> LKML-Reference: <1257367843-26224-1-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r--tools/perf/builtin-record.c49
-rw-r--r--tools/perf/util/data_map.c37
-rw-r--r--tools/perf/util/event.h7
-rw-r--r--tools/perf/util/header.c10
-rw-r--r--tools/perf/util/header.h4
-rw-r--r--tools/perf/util/map.c14
-rw-r--r--tools/perf/util/symbol.c78
-rw-r--r--tools/perf/util/symbol.h10
8 files changed, 179 insertions, 30 deletions
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 4a73d89ce5d..ab333812ace 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -17,6 +17,7 @@
17#include "util/header.h" 17#include "util/header.h"
18#include "util/event.h" 18#include "util/event.h"
19#include "util/debug.h" 19#include "util/debug.h"
20#include "util/symbol.h"
20 21
21#include <unistd.h> 22#include <unistd.h>
22#include <sched.h> 23#include <sched.h>
@@ -109,9 +110,21 @@ static void write_output(void *buf, size_t size)
109 } 110 }
110} 111}
111 112
113static void write_event(event_t *buf, size_t size)
114{
115 /*
116 * Add it to the list of DSOs, so that when we finish this
117 * record session we can pick the available build-ids.
118 */
119 if (buf->header.type == PERF_RECORD_MMAP)
120 dsos__findnew(buf->mmap.filename);
121
122 write_output(buf, size);
123}
124
112static int process_synthesized_event(event_t *event) 125static int process_synthesized_event(event_t *event)
113{ 126{
114 write_output(event, event->header.size); 127 write_event(event, event->header.size);
115 return 0; 128 return 0;
116} 129}
117 130
@@ -163,14 +176,14 @@ static void mmap_read(struct mmap_data *md)
163 size = md->mask + 1 - (old & md->mask); 176 size = md->mask + 1 - (old & md->mask);
164 old += size; 177 old += size;
165 178
166 write_output(buf, size); 179 write_event(buf, size);
167 } 180 }
168 181
169 buf = &data[old & md->mask]; 182 buf = &data[old & md->mask];
170 size = head - old; 183 size = head - old;
171 old += size; 184 old += size;
172 185
173 write_output(buf, size); 186 write_event(buf, size);
174 187
175 md->prev = old; 188 md->prev = old;
176 mmap_write_tail(md, old); 189 mmap_write_tail(md, old);
@@ -365,10 +378,38 @@ static void open_counters(int cpu, pid_t pid)
365 nr_cpu++; 378 nr_cpu++;
366} 379}
367 380
381static bool write_buildid_table(void)
382{
383 struct dso *pos;
384 bool have_buildid = false;
385
386 list_for_each_entry(pos, &dsos, node) {
387 struct build_id_event b;
388 size_t len;
389
390 if (filename__read_build_id(pos->long_name,
391 &b.build_id,
392 sizeof(b.build_id)) < 0)
393 continue;
394 have_buildid = true;
395 memset(&b.header, 0, sizeof(b.header));
396 len = strlen(pos->long_name) + 1;
397 len = ALIGN(len, 64);
398 b.header.size = sizeof(b) + len;
399 write_output(&b, sizeof(b));
400 write_output(pos->long_name, len);
401 }
402
403 return have_buildid;
404}
405
368static void atexit_header(void) 406static void atexit_header(void)
369{ 407{
370 header->data_size += bytes_written; 408 header->data_size += bytes_written;
371 409
410 if (write_buildid_table())
411 perf_header__set_feat(header, HEADER_BUILD_ID);
412
372 perf_header__write(header, output); 413 perf_header__write(header, output);
373} 414}
374 415
@@ -572,6 +613,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
572{ 613{
573 int counter; 614 int counter;
574 615
616 symbol__init(0);
617
575 argc = parse_options(argc, argv, options, record_usage, 618 argc = parse_options(argc, argv, options, record_usage,
576 PARSE_OPT_STOP_AT_NON_OPTION); 619 PARSE_OPT_STOP_AT_NON_OPTION);
577 if (!argc && target_pid == -1 && !system_wide) 620 if (!argc && target_pid == -1 && !system_wide)
diff --git a/tools/perf/util/data_map.c b/tools/perf/util/data_map.c
index c458db9ede6..00a9c114c8d 100644
--- a/tools/perf/util/data_map.c
+++ b/tools/perf/util/data_map.c
@@ -70,6 +70,39 @@ process_event(event_t *event, unsigned long offset, unsigned long head)
70 } 70 }
71} 71}
72 72
73static int perf_header__read_build_ids(const struct perf_header *self,
74 int input, off_t file_size)
75{
76 off_t offset = self->data_offset + self->data_size;
77 struct build_id_event bev;
78 char filename[PATH_MAX];
79 int err = -1;
80
81 if (lseek(input, offset, SEEK_SET) < 0)
82 return -1;
83
84 while (offset < file_size) {
85 struct dso *dso;
86 ssize_t len;
87
88 if (read(input, &bev, sizeof(bev)) != sizeof(bev))
89 goto out;
90
91 len = bev.header.size - sizeof(bev);
92 if (read(input, filename, len) != len)
93 goto out;
94
95 dso = dsos__findnew(filename);
96 if (dso != NULL)
97 dso__set_build_id(dso, &bev.build_id);
98
99 offset += bev.header.size;
100 }
101 err = 0;
102out:
103 return err;
104}
105
73int mmap_dispatch_perf_file(struct perf_header **pheader, 106int mmap_dispatch_perf_file(struct perf_header **pheader,
74 const char *input_name, 107 const char *input_name,
75 int force, 108 int force,
@@ -130,6 +163,10 @@ int mmap_dispatch_perf_file(struct perf_header **pheader,
130 if (curr_handler->sample_type_check(sample_type) < 0) 163 if (curr_handler->sample_type_check(sample_type) < 0)
131 exit(-1); 164 exit(-1);
132 165
166 if (perf_header__has_feat(header, HEADER_BUILD_ID) &&
167 perf_header__read_build_ids(header, input, input_stat.st_size))
168 pr_debug("failed to read buildids, continuing...\n");
169
133 if (load_kernel(NULL) < 0) { 170 if (load_kernel(NULL) < 0) {
134 perror("failed to load kernel symbols"); 171 perror("failed to load kernel symbols");
135 return EXIT_FAILURE; 172 return EXIT_FAILURE;
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index 0a443bea68d..34c6fcb82d9 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -61,6 +61,13 @@ struct sample_event{
61 u64 array[]; 61 u64 array[];
62}; 62};
63 63
64#define BUILD_ID_SIZE 20
65
66struct build_id_event {
67 struct perf_event_header header;
68 u8 build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))];
69 char filename[];
70};
64 71
65typedef union event_union { 72typedef union event_union {
66 struct perf_event_header header; 73 struct perf_event_header header;
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 7d26659b806..050f543fd96 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -149,6 +149,16 @@ void perf_header__feat_trace_info(struct perf_header *header)
149 set_bit(HEADER_TRACE_INFO, header->adds_features); 149 set_bit(HEADER_TRACE_INFO, header->adds_features);
150} 150}
151 151
152void perf_header__set_feat(struct perf_header *self, int feat)
153{
154 set_bit(feat, self->adds_features);
155}
156
157bool perf_header__has_feat(const struct perf_header *self, int feat)
158{
159 return test_bit(feat, self->adds_features);
160}
161
152static void do_write(int fd, void *buf, size_t size) 162static void do_write(int fd, void *buf, size_t size)
153{ 163{
154 while (size) { 164 while (size) {
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
index 2ea9dfb1236..2f233c5db7e 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -3,6 +3,7 @@
3 3
4#include "../../../include/linux/perf_event.h" 4#include "../../../include/linux/perf_event.h"
5#include <sys/types.h> 5#include <sys/types.h>
6#include <stdbool.h>
6#include "types.h" 7#include "types.h"
7 8
8#include <linux/bitmap.h> 9#include <linux/bitmap.h>
@@ -15,6 +16,7 @@ struct perf_header_attr {
15}; 16};
16 17
17#define HEADER_TRACE_INFO 1 18#define HEADER_TRACE_INFO 1
19#define HEADER_BUILD_ID 2
18 20
19#define HEADER_FEAT_BITS 256 21#define HEADER_FEAT_BITS 256
20 22
@@ -48,6 +50,8 @@ u64 perf_header__sample_type(struct perf_header *header);
48struct perf_event_attr * 50struct perf_event_attr *
49perf_header__find_attr(u64 id, struct perf_header *header); 51perf_header__find_attr(u64 id, struct perf_header *header);
50void perf_header__feat_trace_info(struct perf_header *header); 52void perf_header__feat_trace_info(struct perf_header *header);
53void perf_header__set_feat(struct perf_header *self, int feat);
54bool perf_header__has_feat(const struct perf_header *self, int feat);
51 55
52struct perf_header *perf_header__new(void); 56struct perf_header *perf_header__new(void);
53 57
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index 33f868420d7..94ca95073c4 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -84,8 +84,18 @@ map__find_symbol(struct map *self, u64 ip, symbol_filter_t filter)
84 int nr = dso__load(self->dso, self, filter); 84 int nr = dso__load(self->dso, self, filter);
85 85
86 if (nr < 0) { 86 if (nr < 0) {
87 pr_warning("Failed to open %s, continuing without symbols\n", 87 if (self->dso->has_build_id) {
88 self->dso->long_name); 88 char sbuild_id[BUILD_ID_SIZE * 2 + 1];
89
90 build_id__sprintf(self->dso->build_id,
91 sizeof(self->dso->build_id),
92 sbuild_id);
93 pr_warning("%s with build id %s not found",
94 self->dso->long_name, sbuild_id);
95 } else
96 pr_warning("Failed to open %s",
97 self->dso->long_name);
98 pr_warning(", continuing without symbols\n");
89 return NULL; 99 return NULL;
90 } else if (nr == 0) { 100 } else if (nr == 0) {
91 const char *name = self->dso->long_name; 101 const char *name = self->dso->long_name;
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index e7c7cdb851c..a2e95ce1f22 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -121,7 +121,8 @@ struct dso *dso__new(const char *name)
121 self->find_symbol = dso__find_symbol; 121 self->find_symbol = dso__find_symbol;
122 self->slen_calculated = 0; 122 self->slen_calculated = 0;
123 self->origin = DSO__ORIG_NOT_FOUND; 123 self->origin = DSO__ORIG_NOT_FOUND;
124 self->loaded = false; 124 self->loaded = 0;
125 self->has_build_id = 0;
125 } 126 }
126 127
127 return self; 128 return self;
@@ -148,6 +149,12 @@ void dso__delete(struct dso *self)
148 free(self); 149 free(self);
149} 150}
150 151
152void dso__set_build_id(struct dso *self, void *build_id)
153{
154 memcpy(self->build_id, build_id, sizeof(self->build_id));
155 self->has_build_id = 1;
156}
157
151static void dso__insert_symbol(struct dso *self, struct symbol *sym) 158static void dso__insert_symbol(struct dso *self, struct symbol *sym)
152{ 159{
153 struct rb_node **p = &self->syms.rb_node; 160 struct rb_node **p = &self->syms.rb_node;
@@ -190,11 +197,30 @@ struct symbol *dso__find_symbol(struct dso *self, u64 ip)
190 return NULL; 197 return NULL;
191} 198}
192 199
193size_t dso__fprintf(struct dso *self, FILE *fp) 200int build_id__sprintf(u8 *self, int len, char *bf)
194{ 201{
195 size_t ret = fprintf(fp, "dso: %s\n", self->short_name); 202 char *bid = bf;
203 u8 *raw = self;
204 int i;
196 205
206 for (i = 0; i < len; ++i) {
207 sprintf(bid, "%02x", *raw);
208 ++raw;
209 bid += 2;
210 }
211
212 return raw - self;
213}
214
215size_t dso__fprintf(struct dso *self, FILE *fp)
216{
217 char sbuild_id[BUILD_ID_SIZE * 2 + 1];
197 struct rb_node *nd; 218 struct rb_node *nd;
219 size_t ret;
220
221 build_id__sprintf(self->build_id, sizeof(self->build_id), sbuild_id);
222 ret = fprintf(fp, "dso: %s (%s)\n", self->short_name, sbuild_id);
223
198 for (nd = rb_first(&self->syms); nd; nd = rb_next(nd)) { 224 for (nd = rb_first(&self->syms); nd; nd = rb_next(nd)) {
199 struct symbol *pos = rb_entry(nd, struct symbol, rb_node); 225 struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
200 ret += symbol__fprintf(pos, fp); 226 ret += symbol__fprintf(pos, fp);
@@ -825,8 +851,6 @@ out_close:
825 return err; 851 return err;
826} 852}
827 853
828#define BUILD_ID_SIZE 20
829
830int filename__read_build_id(const char *filename, void *bf, size_t size) 854int filename__read_build_id(const char *filename, void *bf, size_t size)
831{ 855{
832 int fd, err = -1; 856 int fd, err = -1;
@@ -845,7 +869,7 @@ int filename__read_build_id(const char *filename, void *bf, size_t size)
845 869
846 elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); 870 elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
847 if (elf == NULL) { 871 if (elf == NULL) {
848 pr_err("%s: cannot read %s ELF file.\n", __func__, filename); 872 pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename);
849 goto out_close; 873 goto out_close;
850 } 874 }
851 875
@@ -874,9 +898,9 @@ out:
874 898
875static char *dso__read_build_id(struct dso *self) 899static char *dso__read_build_id(struct dso *self)
876{ 900{
877 int i, len; 901 int len;
878 char *build_id = NULL, *bid; 902 char *build_id = NULL;
879 unsigned char rawbf[BUILD_ID_SIZE], *raw; 903 unsigned char rawbf[BUILD_ID_SIZE];
880 904
881 len = filename__read_build_id(self->long_name, rawbf, sizeof(rawbf)); 905 len = filename__read_build_id(self->long_name, rawbf, sizeof(rawbf));
882 if (len < 0) 906 if (len < 0)
@@ -885,15 +909,8 @@ static char *dso__read_build_id(struct dso *self)
885 build_id = malloc(len * 2 + 1); 909 build_id = malloc(len * 2 + 1);
886 if (build_id == NULL) 910 if (build_id == NULL)
887 goto out; 911 goto out;
888 bid = build_id;
889 912
890 raw = rawbf; 913 build_id__sprintf(rawbf, len, build_id);
891 for (i = 0; i < len; ++i) {
892 sprintf(bid, "%02x", *raw);
893 ++raw;
894 bid += 2;
895 }
896 pr_debug2("%s(%s): %s\n", __func__, self->long_name, build_id);
897out: 914out:
898 return build_id; 915 return build_id;
899} 916}
@@ -922,7 +939,7 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter)
922 int ret = -1; 939 int ret = -1;
923 int fd; 940 int fd;
924 941
925 self->loaded = true; 942 self->loaded = 1;
926 943
927 if (!name) 944 if (!name)
928 return -1; 945 return -1;
@@ -940,6 +957,8 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter)
940 957
941more: 958more:
942 do { 959 do {
960 int berr = 0;
961
943 self->origin++; 962 self->origin++;
944 switch (self->origin) { 963 switch (self->origin) {
945 case DSO__ORIG_FEDORA: 964 case DSO__ORIG_FEDORA:
@@ -956,8 +975,7 @@ more:
956 snprintf(name, size, 975 snprintf(name, size,
957 "/usr/lib/debug/.build-id/%.2s/%s.debug", 976 "/usr/lib/debug/.build-id/%.2s/%s.debug",
958 build_id, build_id + 2); 977 build_id, build_id + 2);
959 free(build_id); 978 goto compare_build_id;
960 break;
961 } 979 }
962 self->origin++; 980 self->origin++;
963 /* Fall thru */ 981 /* Fall thru */
@@ -969,6 +987,22 @@ more:
969 goto out; 987 goto out;
970 } 988 }
971 989
990 if (self->has_build_id) {
991 bool match;
992 build_id = malloc(BUILD_ID_SIZE);
993 if (build_id == NULL)
994 goto more;
995 berr = filename__read_build_id(name, build_id,
996 BUILD_ID_SIZE);
997compare_build_id:
998 match = berr > 0 && memcmp(build_id, self->build_id,
999 sizeof(self->build_id)) == 0;
1000 free(build_id);
1001 build_id = NULL;
1002 if (!match)
1003 goto more;
1004 }
1005
972 fd = open(name, O_RDONLY); 1006 fd = open(name, O_RDONLY);
973 } while (fd < 0); 1007 } while (fd < 0);
974 1008
@@ -1034,7 +1068,7 @@ static int dso__load_module_sym(struct dso *self, struct map *map,
1034{ 1068{
1035 int err = 0, fd = open(self->long_name, O_RDONLY); 1069 int err = 0, fd = open(self->long_name, O_RDONLY);
1036 1070
1037 self->loaded = true; 1071 self->loaded = 1;
1038 1072
1039 if (fd < 0) { 1073 if (fd < 0) {
1040 pr_err("%s: cannot open %s\n", __func__, self->long_name); 1074 pr_err("%s: cannot open %s\n", __func__, self->long_name);
@@ -1225,7 +1259,7 @@ static int dso__load_vmlinux(struct dso *self, struct map *map,
1225{ 1259{
1226 int err, fd = open(vmlinux, O_RDONLY); 1260 int err, fd = open(vmlinux, O_RDONLY);
1227 1261
1228 self->loaded = true; 1262 self->loaded = 1;
1229 1263
1230 if (fd < 0) 1264 if (fd < 0)
1231 return -1; 1265 return -1;
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index e0d4a583f8d..f8c1899af48 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -60,10 +60,12 @@ struct dso {
60 struct list_head node; 60 struct list_head node;
61 struct rb_root syms; 61 struct rb_root syms;
62 struct symbol *(*find_symbol)(struct dso *, u64 ip); 62 struct symbol *(*find_symbol)(struct dso *, u64 ip);
63 unsigned char adjust_symbols; 63 u8 adjust_symbols:1;
64 unsigned char slen_calculated; 64 u8 slen_calculated:1;
65 bool loaded; 65 u8 loaded:1;
66 u8 has_build_id:1;
66 unsigned char origin; 67 unsigned char origin;
68 u8 build_id[BUILD_ID_SIZE];
67 const char *short_name; 69 const char *short_name;
68 char *long_name; 70 char *long_name;
69 char name[0]; 71 char name[0];
@@ -81,8 +83,10 @@ void dsos__fprintf(FILE *fp);
81 83
82size_t dso__fprintf(struct dso *self, FILE *fp); 84size_t dso__fprintf(struct dso *self, FILE *fp);
83char dso__symtab_origin(const struct dso *self); 85char dso__symtab_origin(const struct dso *self);
86void dso__set_build_id(struct dso *self, void *build_id);
84 87
85int filename__read_build_id(const char *filename, void *bf, size_t size); 88int filename__read_build_id(const char *filename, void *bf, size_t size);
89int build_id__sprintf(u8 *self, int len, char *bf);
86 90
87int load_kernel(symbol_filter_t filter); 91int load_kernel(symbol_filter_t filter);
88 92