diff options
author | Andrea Bastoni <bastoni@cs.unc.edu> | 2010-05-30 19:16:45 -0400 |
---|---|---|
committer | Andrea Bastoni <bastoni@cs.unc.edu> | 2010-05-30 19:16:45 -0400 |
commit | ada47b5fe13d89735805b566185f4885f5a3f750 (patch) | |
tree | 644b88f8a71896307d71438e9b3af49126ffb22b /tools/perf/util/header.c | |
parent | 43e98717ad40a4ae64545b5ba047c7b86aa44f4f (diff) | |
parent | 3280f21d43ee541f97f8cda5792150d2dbec20d5 (diff) |
Merge branch 'wip-2.6.34' into old-private-masterarchived-private-master
Diffstat (limited to 'tools/perf/util/header.c')
-rw-r--r-- | tools/perf/util/header.c | 640 |
1 files changed, 534 insertions, 106 deletions
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index e306857b2c2b..6c9aa16ee51f 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -1,10 +1,20 @@ | |||
1 | #define _FILE_OFFSET_BITS 64 | ||
2 | |||
1 | #include <sys/types.h> | 3 | #include <sys/types.h> |
4 | #include <byteswap.h> | ||
2 | #include <unistd.h> | 5 | #include <unistd.h> |
3 | #include <stdio.h> | 6 | #include <stdio.h> |
4 | #include <stdlib.h> | 7 | #include <stdlib.h> |
8 | #include <linux/list.h> | ||
9 | #include <linux/kernel.h> | ||
5 | 10 | ||
6 | #include "util.h" | 11 | #include "util.h" |
7 | #include "header.h" | 12 | #include "header.h" |
13 | #include "../perf.h" | ||
14 | #include "trace-event.h" | ||
15 | #include "session.h" | ||
16 | #include "symbol.h" | ||
17 | #include "debug.h" | ||
8 | 18 | ||
9 | /* | 19 | /* |
10 | * Create new perf.data header attribute: | 20 | * Create new perf.data header attribute: |
@@ -13,75 +23,80 @@ struct perf_header_attr *perf_header_attr__new(struct perf_event_attr *attr) | |||
13 | { | 23 | { |
14 | struct perf_header_attr *self = malloc(sizeof(*self)); | 24 | struct perf_header_attr *self = malloc(sizeof(*self)); |
15 | 25 | ||
16 | if (!self) | 26 | if (self != NULL) { |
17 | die("nomem"); | 27 | self->attr = *attr; |
18 | 28 | self->ids = 0; | |
19 | self->attr = *attr; | 29 | self->size = 1; |
20 | self->ids = 0; | 30 | self->id = malloc(sizeof(u64)); |
21 | self->size = 1; | 31 | if (self->id == NULL) { |
22 | self->id = malloc(sizeof(u64)); | 32 | free(self); |
23 | 33 | self = NULL; | |
24 | if (!self->id) | 34 | } |
25 | die("nomem"); | 35 | } |
26 | 36 | ||
27 | return self; | 37 | return self; |
28 | } | 38 | } |
29 | 39 | ||
30 | void perf_header_attr__add_id(struct perf_header_attr *self, u64 id) | 40 | void perf_header_attr__delete(struct perf_header_attr *self) |
41 | { | ||
42 | free(self->id); | ||
43 | free(self); | ||
44 | } | ||
45 | |||
46 | int perf_header_attr__add_id(struct perf_header_attr *self, u64 id) | ||
31 | { | 47 | { |
32 | int pos = self->ids; | 48 | int pos = self->ids; |
33 | 49 | ||
34 | self->ids++; | 50 | self->ids++; |
35 | if (self->ids > self->size) { | 51 | if (self->ids > self->size) { |
36 | self->size *= 2; | 52 | int nsize = self->size * 2; |
37 | self->id = realloc(self->id, self->size * sizeof(u64)); | 53 | u64 *nid = realloc(self->id, nsize * sizeof(u64)); |
38 | if (!self->id) | 54 | |
39 | die("nomem"); | 55 | if (nid == NULL) |
56 | return -1; | ||
57 | |||
58 | self->size = nsize; | ||
59 | self->id = nid; | ||
40 | } | 60 | } |
41 | self->id[pos] = id; | 61 | self->id[pos] = id; |
62 | return 0; | ||
42 | } | 63 | } |
43 | 64 | ||
44 | /* | 65 | int perf_header__init(struct perf_header *self) |
45 | * Create new perf.data header: | ||
46 | */ | ||
47 | struct perf_header *perf_header__new(void) | ||
48 | { | 66 | { |
49 | struct perf_header *self = malloc(sizeof(*self)); | ||
50 | |||
51 | if (!self) | ||
52 | die("nomem"); | ||
53 | |||
54 | self->frozen = 0; | ||
55 | |||
56 | self->attrs = 0; | ||
57 | self->size = 1; | 67 | self->size = 1; |
58 | self->attr = malloc(sizeof(void *)); | 68 | self->attr = malloc(sizeof(void *)); |
59 | 69 | return self->attr == NULL ? -ENOMEM : 0; | |
60 | if (!self->attr) | ||
61 | die("nomem"); | ||
62 | |||
63 | self->data_offset = 0; | ||
64 | self->data_size = 0; | ||
65 | |||
66 | return self; | ||
67 | } | 70 | } |
68 | 71 | ||
69 | void perf_header__add_attr(struct perf_header *self, | 72 | void perf_header__exit(struct perf_header *self) |
70 | struct perf_header_attr *attr) | ||
71 | { | 73 | { |
72 | int pos = self->attrs; | 74 | int i; |
75 | for (i = 0; i < self->attrs; ++i) | ||
76 | perf_header_attr__delete(self->attr[i]); | ||
77 | free(self->attr); | ||
78 | } | ||
73 | 79 | ||
80 | int perf_header__add_attr(struct perf_header *self, | ||
81 | struct perf_header_attr *attr) | ||
82 | { | ||
74 | if (self->frozen) | 83 | if (self->frozen) |
75 | die("frozen"); | 84 | return -1; |
76 | 85 | ||
77 | self->attrs++; | 86 | if (self->attrs == self->size) { |
78 | if (self->attrs > self->size) { | 87 | int nsize = self->size * 2; |
79 | self->size *= 2; | 88 | struct perf_header_attr **nattr; |
80 | self->attr = realloc(self->attr, self->size * sizeof(void *)); | 89 | |
81 | if (!self->attr) | 90 | nattr = realloc(self->attr, nsize * sizeof(void *)); |
82 | die("nomem"); | 91 | if (nattr == NULL) |
92 | return -1; | ||
93 | |||
94 | self->size = nsize; | ||
95 | self->attr = nattr; | ||
83 | } | 96 | } |
84 | self->attr[pos] = attr; | 97 | |
98 | self->attr[self->attrs++] = attr; | ||
99 | return 0; | ||
85 | } | 100 | } |
86 | 101 | ||
87 | #define MAX_EVENT_NAME 64 | 102 | #define MAX_EVENT_NAME 64 |
@@ -94,24 +109,28 @@ struct perf_trace_event_type { | |||
94 | static int event_count; | 109 | static int event_count; |
95 | static struct perf_trace_event_type *events; | 110 | static struct perf_trace_event_type *events; |
96 | 111 | ||
97 | void perf_header__push_event(u64 id, const char *name) | 112 | int perf_header__push_event(u64 id, const char *name) |
98 | { | 113 | { |
99 | if (strlen(name) > MAX_EVENT_NAME) | 114 | if (strlen(name) > MAX_EVENT_NAME) |
100 | printf("Event %s will be truncated\n", name); | 115 | pr_warning("Event %s will be truncated\n", name); |
101 | 116 | ||
102 | if (!events) { | 117 | if (!events) { |
103 | events = malloc(sizeof(struct perf_trace_event_type)); | 118 | events = malloc(sizeof(struct perf_trace_event_type)); |
104 | if (!events) | 119 | if (events == NULL) |
105 | die("nomem"); | 120 | return -ENOMEM; |
106 | } else { | 121 | } else { |
107 | events = realloc(events, (event_count + 1) * sizeof(struct perf_trace_event_type)); | 122 | struct perf_trace_event_type *nevents; |
108 | if (!events) | 123 | |
109 | die("nomem"); | 124 | nevents = realloc(events, (event_count + 1) * sizeof(*events)); |
125 | if (nevents == NULL) | ||
126 | return -ENOMEM; | ||
127 | events = nevents; | ||
110 | } | 128 | } |
111 | memset(&events[event_count], 0, sizeof(struct perf_trace_event_type)); | 129 | memset(&events[event_count], 0, sizeof(struct perf_trace_event_type)); |
112 | events[event_count].event_id = id; | 130 | events[event_count].event_id = id; |
113 | strncpy(events[event_count].name, name, MAX_EVENT_NAME - 1); | 131 | strncpy(events[event_count].name, name, MAX_EVENT_NAME - 1); |
114 | event_count++; | 132 | event_count++; |
133 | return 0; | ||
115 | } | 134 | } |
116 | 135 | ||
117 | char *perf_header__find_event(u64 id) | 136 | char *perf_header__find_event(u64 id) |
@@ -128,44 +147,292 @@ static const char *__perf_magic = "PERFFILE"; | |||
128 | 147 | ||
129 | #define PERF_MAGIC (*(u64 *)__perf_magic) | 148 | #define PERF_MAGIC (*(u64 *)__perf_magic) |
130 | 149 | ||
131 | struct perf_file_section { | ||
132 | u64 offset; | ||
133 | u64 size; | ||
134 | }; | ||
135 | |||
136 | struct perf_file_attr { | 150 | struct perf_file_attr { |
137 | struct perf_event_attr attr; | 151 | struct perf_event_attr attr; |
138 | struct perf_file_section ids; | 152 | struct perf_file_section ids; |
139 | }; | 153 | }; |
140 | 154 | ||
141 | struct perf_file_header { | 155 | void perf_header__set_feat(struct perf_header *self, int feat) |
142 | u64 magic; | 156 | { |
143 | u64 size; | 157 | set_bit(feat, self->adds_features); |
144 | u64 attr_size; | 158 | } |
145 | struct perf_file_section attrs; | ||
146 | struct perf_file_section data; | ||
147 | struct perf_file_section event_types; | ||
148 | }; | ||
149 | 159 | ||
150 | static void do_write(int fd, void *buf, size_t size) | 160 | bool perf_header__has_feat(const struct perf_header *self, int feat) |
161 | { | ||
162 | return test_bit(feat, self->adds_features); | ||
163 | } | ||
164 | |||
165 | static int do_write(int fd, const void *buf, size_t size) | ||
151 | { | 166 | { |
152 | while (size) { | 167 | while (size) { |
153 | int ret = write(fd, buf, size); | 168 | int ret = write(fd, buf, size); |
154 | 169 | ||
155 | if (ret < 0) | 170 | if (ret < 0) |
156 | die("failed to write"); | 171 | return -errno; |
157 | 172 | ||
158 | size -= ret; | 173 | size -= ret; |
159 | buf += ret; | 174 | buf += ret; |
160 | } | 175 | } |
176 | |||
177 | return 0; | ||
178 | } | ||
179 | |||
180 | #define NAME_ALIGN 64 | ||
181 | |||
182 | static int write_padded(int fd, const void *bf, size_t count, | ||
183 | size_t count_aligned) | ||
184 | { | ||
185 | static const char zero_buf[NAME_ALIGN]; | ||
186 | int err = do_write(fd, bf, count); | ||
187 | |||
188 | if (!err) | ||
189 | err = do_write(fd, zero_buf, count_aligned - count); | ||
190 | |||
191 | return err; | ||
192 | } | ||
193 | |||
194 | #define dsos__for_each_with_build_id(pos, head) \ | ||
195 | list_for_each_entry(pos, head, node) \ | ||
196 | if (!pos->has_build_id) \ | ||
197 | continue; \ | ||
198 | else | ||
199 | |||
200 | static int __dsos__write_buildid_table(struct list_head *head, u16 misc, int fd) | ||
201 | { | ||
202 | struct dso *pos; | ||
203 | |||
204 | dsos__for_each_with_build_id(pos, head) { | ||
205 | int err; | ||
206 | struct build_id_event b; | ||
207 | size_t len; | ||
208 | |||
209 | if (!pos->hit) | ||
210 | continue; | ||
211 | len = pos->long_name_len + 1; | ||
212 | len = ALIGN(len, NAME_ALIGN); | ||
213 | memset(&b, 0, sizeof(b)); | ||
214 | memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id)); | ||
215 | b.header.misc = misc; | ||
216 | b.header.size = sizeof(b) + len; | ||
217 | err = do_write(fd, &b, sizeof(b)); | ||
218 | if (err < 0) | ||
219 | return err; | ||
220 | err = write_padded(fd, pos->long_name, | ||
221 | pos->long_name_len + 1, len); | ||
222 | if (err < 0) | ||
223 | return err; | ||
224 | } | ||
225 | |||
226 | return 0; | ||
227 | } | ||
228 | |||
229 | static int dsos__write_buildid_table(int fd) | ||
230 | { | ||
231 | int err = __dsos__write_buildid_table(&dsos__kernel, | ||
232 | PERF_RECORD_MISC_KERNEL, fd); | ||
233 | if (err == 0) | ||
234 | err = __dsos__write_buildid_table(&dsos__user, | ||
235 | PERF_RECORD_MISC_USER, fd); | ||
236 | return err; | ||
237 | } | ||
238 | |||
239 | int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | ||
240 | const char *name, bool is_kallsyms) | ||
241 | { | ||
242 | const size_t size = PATH_MAX; | ||
243 | char *filename = malloc(size), | ||
244 | *linkname = malloc(size), *targetname; | ||
245 | int len, err = -1; | ||
246 | |||
247 | if (filename == NULL || linkname == NULL) | ||
248 | goto out_free; | ||
249 | |||
250 | len = snprintf(filename, size, "%s%s%s", | ||
251 | debugdir, is_kallsyms ? "/" : "", name); | ||
252 | if (mkdir_p(filename, 0755)) | ||
253 | goto out_free; | ||
254 | |||
255 | snprintf(filename + len, sizeof(filename) - len, "/%s", sbuild_id); | ||
256 | |||
257 | if (access(filename, F_OK)) { | ||
258 | if (is_kallsyms) { | ||
259 | if (copyfile("/proc/kallsyms", filename)) | ||
260 | goto out_free; | ||
261 | } else if (link(name, filename) && copyfile(name, filename)) | ||
262 | goto out_free; | ||
263 | } | ||
264 | |||
265 | len = snprintf(linkname, size, "%s/.build-id/%.2s", | ||
266 | debugdir, sbuild_id); | ||
267 | |||
268 | if (access(linkname, X_OK) && mkdir_p(linkname, 0755)) | ||
269 | goto out_free; | ||
270 | |||
271 | snprintf(linkname + len, size - len, "/%s", sbuild_id + 2); | ||
272 | targetname = filename + strlen(debugdir) - 5; | ||
273 | memcpy(targetname, "../..", 5); | ||
274 | |||
275 | if (symlink(targetname, linkname) == 0) | ||
276 | err = 0; | ||
277 | out_free: | ||
278 | free(filename); | ||
279 | free(linkname); | ||
280 | return err; | ||
281 | } | ||
282 | |||
283 | static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size, | ||
284 | const char *name, const char *debugdir, | ||
285 | bool is_kallsyms) | ||
286 | { | ||
287 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
288 | |||
289 | build_id__sprintf(build_id, build_id_size, sbuild_id); | ||
290 | |||
291 | return build_id_cache__add_s(sbuild_id, debugdir, name, is_kallsyms); | ||
292 | } | ||
293 | |||
294 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir) | ||
295 | { | ||
296 | const size_t size = PATH_MAX; | ||
297 | char *filename = malloc(size), | ||
298 | *linkname = malloc(size); | ||
299 | int err = -1; | ||
300 | |||
301 | if (filename == NULL || linkname == NULL) | ||
302 | goto out_free; | ||
303 | |||
304 | snprintf(linkname, size, "%s/.build-id/%.2s/%s", | ||
305 | debugdir, sbuild_id, sbuild_id + 2); | ||
306 | |||
307 | if (access(linkname, F_OK)) | ||
308 | goto out_free; | ||
309 | |||
310 | if (readlink(linkname, filename, size) < 0) | ||
311 | goto out_free; | ||
312 | |||
313 | if (unlink(linkname)) | ||
314 | goto out_free; | ||
315 | |||
316 | /* | ||
317 | * Since the link is relative, we must make it absolute: | ||
318 | */ | ||
319 | snprintf(linkname, size, "%s/.build-id/%.2s/%s", | ||
320 | debugdir, sbuild_id, filename); | ||
321 | |||
322 | if (unlink(linkname)) | ||
323 | goto out_free; | ||
324 | |||
325 | err = 0; | ||
326 | out_free: | ||
327 | free(filename); | ||
328 | free(linkname); | ||
329 | return err; | ||
330 | } | ||
331 | |||
332 | static int dso__cache_build_id(struct dso *self, const char *debugdir) | ||
333 | { | ||
334 | bool is_kallsyms = self->kernel && self->long_name[0] != '/'; | ||
335 | |||
336 | return build_id_cache__add_b(self->build_id, sizeof(self->build_id), | ||
337 | self->long_name, debugdir, is_kallsyms); | ||
338 | } | ||
339 | |||
340 | static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir) | ||
341 | { | ||
342 | struct dso *pos; | ||
343 | int err = 0; | ||
344 | |||
345 | dsos__for_each_with_build_id(pos, head) | ||
346 | if (dso__cache_build_id(pos, debugdir)) | ||
347 | err = -1; | ||
348 | |||
349 | return err; | ||
350 | } | ||
351 | |||
352 | static int dsos__cache_build_ids(void) | ||
353 | { | ||
354 | int err_kernel, err_user; | ||
355 | char debugdir[PATH_MAX]; | ||
356 | |||
357 | snprintf(debugdir, sizeof(debugdir), "%s/%s", getenv("HOME"), | ||
358 | DEBUG_CACHE_DIR); | ||
359 | |||
360 | if (mkdir(debugdir, 0755) != 0 && errno != EEXIST) | ||
361 | return -1; | ||
362 | |||
363 | err_kernel = __dsos__cache_build_ids(&dsos__kernel, debugdir); | ||
364 | err_user = __dsos__cache_build_ids(&dsos__user, debugdir); | ||
365 | return err_kernel || err_user ? -1 : 0; | ||
366 | } | ||
367 | |||
368 | static int perf_header__adds_write(struct perf_header *self, int fd) | ||
369 | { | ||
370 | int nr_sections; | ||
371 | struct perf_file_section *feat_sec; | ||
372 | int sec_size; | ||
373 | u64 sec_start; | ||
374 | int idx = 0, err; | ||
375 | |||
376 | if (dsos__read_build_ids(true)) | ||
377 | perf_header__set_feat(self, HEADER_BUILD_ID); | ||
378 | |||
379 | nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS); | ||
380 | if (!nr_sections) | ||
381 | return 0; | ||
382 | |||
383 | feat_sec = calloc(sizeof(*feat_sec), nr_sections); | ||
384 | if (feat_sec == NULL) | ||
385 | return -ENOMEM; | ||
386 | |||
387 | sec_size = sizeof(*feat_sec) * nr_sections; | ||
388 | |||
389 | sec_start = self->data_offset + self->data_size; | ||
390 | lseek(fd, sec_start + sec_size, SEEK_SET); | ||
391 | |||
392 | if (perf_header__has_feat(self, HEADER_TRACE_INFO)) { | ||
393 | struct perf_file_section *trace_sec; | ||
394 | |||
395 | trace_sec = &feat_sec[idx++]; | ||
396 | |||
397 | /* Write trace info */ | ||
398 | trace_sec->offset = lseek(fd, 0, SEEK_CUR); | ||
399 | read_tracing_data(fd, attrs, nr_counters); | ||
400 | trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset; | ||
401 | } | ||
402 | |||
403 | |||
404 | if (perf_header__has_feat(self, HEADER_BUILD_ID)) { | ||
405 | struct perf_file_section *buildid_sec; | ||
406 | |||
407 | buildid_sec = &feat_sec[idx++]; | ||
408 | |||
409 | /* Write build-ids */ | ||
410 | buildid_sec->offset = lseek(fd, 0, SEEK_CUR); | ||
411 | err = dsos__write_buildid_table(fd); | ||
412 | if (err < 0) { | ||
413 | pr_debug("failed to write buildid table\n"); | ||
414 | goto out_free; | ||
415 | } | ||
416 | buildid_sec->size = lseek(fd, 0, SEEK_CUR) - | ||
417 | buildid_sec->offset; | ||
418 | dsos__cache_build_ids(); | ||
419 | } | ||
420 | |||
421 | lseek(fd, sec_start, SEEK_SET); | ||
422 | err = do_write(fd, feat_sec, sec_size); | ||
423 | if (err < 0) | ||
424 | pr_debug("failed to write feature section\n"); | ||
425 | out_free: | ||
426 | free(feat_sec); | ||
427 | return err; | ||
161 | } | 428 | } |
162 | 429 | ||
163 | void perf_header__write(struct perf_header *self, int fd) | 430 | int perf_header__write(struct perf_header *self, int fd, bool at_exit) |
164 | { | 431 | { |
165 | struct perf_file_header f_header; | 432 | struct perf_file_header f_header; |
166 | struct perf_file_attr f_attr; | 433 | struct perf_file_attr f_attr; |
167 | struct perf_header_attr *attr; | 434 | struct perf_header_attr *attr; |
168 | int i; | 435 | int i, err; |
169 | 436 | ||
170 | lseek(fd, sizeof(f_header), SEEK_SET); | 437 | lseek(fd, sizeof(f_header), SEEK_SET); |
171 | 438 | ||
@@ -174,7 +441,11 @@ void perf_header__write(struct perf_header *self, int fd) | |||
174 | attr = self->attr[i]; | 441 | attr = self->attr[i]; |
175 | 442 | ||
176 | attr->id_offset = lseek(fd, 0, SEEK_CUR); | 443 | attr->id_offset = lseek(fd, 0, SEEK_CUR); |
177 | do_write(fd, attr->id, attr->ids * sizeof(u64)); | 444 | err = do_write(fd, attr->id, attr->ids * sizeof(u64)); |
445 | if (err < 0) { | ||
446 | pr_debug("failed to write perf header\n"); | ||
447 | return err; | ||
448 | } | ||
178 | } | 449 | } |
179 | 450 | ||
180 | 451 | ||
@@ -190,17 +461,31 @@ void perf_header__write(struct perf_header *self, int fd) | |||
190 | .size = attr->ids * sizeof(u64), | 461 | .size = attr->ids * sizeof(u64), |
191 | } | 462 | } |
192 | }; | 463 | }; |
193 | do_write(fd, &f_attr, sizeof(f_attr)); | 464 | err = do_write(fd, &f_attr, sizeof(f_attr)); |
465 | if (err < 0) { | ||
466 | pr_debug("failed to write perf header attribute\n"); | ||
467 | return err; | ||
468 | } | ||
194 | } | 469 | } |
195 | 470 | ||
196 | self->event_offset = lseek(fd, 0, SEEK_CUR); | 471 | self->event_offset = lseek(fd, 0, SEEK_CUR); |
197 | self->event_size = event_count * sizeof(struct perf_trace_event_type); | 472 | self->event_size = event_count * sizeof(struct perf_trace_event_type); |
198 | if (events) | 473 | if (events) { |
199 | do_write(fd, events, self->event_size); | 474 | err = do_write(fd, events, self->event_size); |
200 | 475 | if (err < 0) { | |
476 | pr_debug("failed to write perf header events\n"); | ||
477 | return err; | ||
478 | } | ||
479 | } | ||
201 | 480 | ||
202 | self->data_offset = lseek(fd, 0, SEEK_CUR); | 481 | self->data_offset = lseek(fd, 0, SEEK_CUR); |
203 | 482 | ||
483 | if (at_exit) { | ||
484 | err = perf_header__adds_write(self, fd); | ||
485 | if (err < 0) | ||
486 | return err; | ||
487 | } | ||
488 | |||
204 | f_header = (struct perf_file_header){ | 489 | f_header = (struct perf_file_header){ |
205 | .magic = PERF_MAGIC, | 490 | .magic = PERF_MAGIC, |
206 | .size = sizeof(f_header), | 491 | .size = sizeof(f_header), |
@@ -219,44 +504,175 @@ void perf_header__write(struct perf_header *self, int fd) | |||
219 | }, | 504 | }, |
220 | }; | 505 | }; |
221 | 506 | ||
507 | memcpy(&f_header.adds_features, &self->adds_features, sizeof(self->adds_features)); | ||
508 | |||
222 | lseek(fd, 0, SEEK_SET); | 509 | lseek(fd, 0, SEEK_SET); |
223 | do_write(fd, &f_header, sizeof(f_header)); | 510 | err = do_write(fd, &f_header, sizeof(f_header)); |
511 | if (err < 0) { | ||
512 | pr_debug("failed to write perf header\n"); | ||
513 | return err; | ||
514 | } | ||
224 | lseek(fd, self->data_offset + self->data_size, SEEK_SET); | 515 | lseek(fd, self->data_offset + self->data_size, SEEK_SET); |
225 | 516 | ||
226 | self->frozen = 1; | 517 | self->frozen = 1; |
518 | return 0; | ||
227 | } | 519 | } |
228 | 520 | ||
229 | static void do_read(int fd, void *buf, size_t size) | 521 | static int do_read(int fd, void *buf, size_t size) |
230 | { | 522 | { |
231 | while (size) { | 523 | while (size) { |
232 | int ret = read(fd, buf, size); | 524 | int ret = read(fd, buf, size); |
233 | 525 | ||
234 | if (ret < 0) | 526 | if (ret <= 0) |
235 | die("failed to read"); | 527 | return -1; |
236 | if (ret == 0) | ||
237 | die("failed to read: missing data"); | ||
238 | 528 | ||
239 | size -= ret; | 529 | size -= ret; |
240 | buf += ret; | 530 | buf += ret; |
241 | } | 531 | } |
532 | |||
533 | return 0; | ||
242 | } | 534 | } |
243 | 535 | ||
244 | struct perf_header *perf_header__read(int fd) | 536 | static int perf_header__getbuffer64(struct perf_header *self, |
537 | int fd, void *buf, size_t size) | ||
245 | { | 538 | { |
246 | struct perf_header *self = perf_header__new(); | 539 | if (do_read(fd, buf, size)) |
247 | struct perf_file_header f_header; | 540 | return -1; |
248 | struct perf_file_attr f_attr; | ||
249 | u64 f_id; | ||
250 | 541 | ||
251 | int nr_attrs, nr_ids, i, j; | 542 | if (self->needs_swap) |
543 | mem_bswap_64(buf, size); | ||
544 | |||
545 | return 0; | ||
546 | } | ||
547 | |||
548 | int perf_header__process_sections(struct perf_header *self, int fd, | ||
549 | int (*process)(struct perf_file_section *self, | ||
550 | struct perf_header *ph, | ||
551 | int feat, int fd)) | ||
552 | { | ||
553 | struct perf_file_section *feat_sec; | ||
554 | int nr_sections; | ||
555 | int sec_size; | ||
556 | int idx = 0; | ||
557 | int err = -1, feat = 1; | ||
558 | |||
559 | nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS); | ||
560 | if (!nr_sections) | ||
561 | return 0; | ||
562 | |||
563 | feat_sec = calloc(sizeof(*feat_sec), nr_sections); | ||
564 | if (!feat_sec) | ||
565 | return -1; | ||
566 | |||
567 | sec_size = sizeof(*feat_sec) * nr_sections; | ||
568 | |||
569 | lseek(fd, self->data_offset + self->data_size, SEEK_SET); | ||
570 | |||
571 | if (perf_header__getbuffer64(self, fd, feat_sec, sec_size)) | ||
572 | goto out_free; | ||
573 | |||
574 | err = 0; | ||
575 | while (idx < nr_sections && feat < HEADER_LAST_FEATURE) { | ||
576 | if (perf_header__has_feat(self, feat)) { | ||
577 | struct perf_file_section *sec = &feat_sec[idx++]; | ||
578 | |||
579 | err = process(sec, self, feat, fd); | ||
580 | if (err < 0) | ||
581 | break; | ||
582 | } | ||
583 | ++feat; | ||
584 | } | ||
585 | out_free: | ||
586 | free(feat_sec); | ||
587 | return err; | ||
588 | } | ||
252 | 589 | ||
590 | int perf_file_header__read(struct perf_file_header *self, | ||
591 | struct perf_header *ph, int fd) | ||
592 | { | ||
253 | lseek(fd, 0, SEEK_SET); | 593 | lseek(fd, 0, SEEK_SET); |
254 | do_read(fd, &f_header, sizeof(f_header)); | ||
255 | 594 | ||
256 | if (f_header.magic != PERF_MAGIC || | 595 | if (do_read(fd, self, sizeof(*self)) || |
257 | f_header.size != sizeof(f_header) || | 596 | memcmp(&self->magic, __perf_magic, sizeof(self->magic))) |
258 | f_header.attr_size != sizeof(f_attr)) | 597 | return -1; |
259 | die("incompatible file format"); | 598 | |
599 | if (self->attr_size != sizeof(struct perf_file_attr)) { | ||
600 | u64 attr_size = bswap_64(self->attr_size); | ||
601 | |||
602 | if (attr_size != sizeof(struct perf_file_attr)) | ||
603 | return -1; | ||
604 | |||
605 | mem_bswap_64(self, offsetof(struct perf_file_header, | ||
606 | adds_features)); | ||
607 | ph->needs_swap = true; | ||
608 | } | ||
609 | |||
610 | if (self->size != sizeof(*self)) { | ||
611 | /* Support the previous format */ | ||
612 | if (self->size == offsetof(typeof(*self), adds_features)) | ||
613 | bitmap_zero(self->adds_features, HEADER_FEAT_BITS); | ||
614 | else | ||
615 | return -1; | ||
616 | } | ||
617 | |||
618 | memcpy(&ph->adds_features, &self->adds_features, | ||
619 | sizeof(ph->adds_features)); | ||
620 | /* | ||
621 | * FIXME: hack that assumes that if we need swap the perf.data file | ||
622 | * may be coming from an arch with a different word-size, ergo different | ||
623 | * DEFINE_BITMAP format, investigate more later, but for now its mostly | ||
624 | * safe to assume that we have a build-id section. Trace files probably | ||
625 | * have several other issues in this realm anyway... | ||
626 | */ | ||
627 | if (ph->needs_swap) { | ||
628 | memset(&ph->adds_features, 0, sizeof(ph->adds_features)); | ||
629 | perf_header__set_feat(ph, HEADER_BUILD_ID); | ||
630 | } | ||
631 | |||
632 | ph->event_offset = self->event_types.offset; | ||
633 | ph->event_size = self->event_types.size; | ||
634 | ph->data_offset = self->data.offset; | ||
635 | ph->data_size = self->data.size; | ||
636 | return 0; | ||
637 | } | ||
638 | |||
639 | static int perf_file_section__process(struct perf_file_section *self, | ||
640 | struct perf_header *ph, | ||
641 | int feat, int fd) | ||
642 | { | ||
643 | if (lseek(fd, self->offset, SEEK_SET) == (off_t)-1) { | ||
644 | pr_debug("Failed to lseek to %Ld offset for feature %d, " | ||
645 | "continuing...\n", self->offset, feat); | ||
646 | return 0; | ||
647 | } | ||
648 | |||
649 | switch (feat) { | ||
650 | case HEADER_TRACE_INFO: | ||
651 | trace_report(fd); | ||
652 | break; | ||
653 | |||
654 | case HEADER_BUILD_ID: | ||
655 | if (perf_header__read_build_ids(ph, fd, self->offset, self->size)) | ||
656 | pr_debug("Failed to read buildids, continuing...\n"); | ||
657 | break; | ||
658 | default: | ||
659 | pr_debug("unknown feature %d, continuing...\n", feat); | ||
660 | } | ||
661 | |||
662 | return 0; | ||
663 | } | ||
664 | |||
665 | int perf_header__read(struct perf_header *self, int fd) | ||
666 | { | ||
667 | struct perf_file_header f_header; | ||
668 | struct perf_file_attr f_attr; | ||
669 | u64 f_id; | ||
670 | int nr_attrs, nr_ids, i, j; | ||
671 | |||
672 | if (perf_file_header__read(&f_header, self, fd) < 0) { | ||
673 | pr_debug("incompatible file format\n"); | ||
674 | return -EINVAL; | ||
675 | } | ||
260 | 676 | ||
261 | nr_attrs = f_header.attrs.size / sizeof(f_attr); | 677 | nr_attrs = f_header.attrs.size / sizeof(f_attr); |
262 | lseek(fd, f_header.attrs.offset, SEEK_SET); | 678 | lseek(fd, f_header.attrs.offset, SEEK_SET); |
@@ -265,42 +681,54 @@ struct perf_header *perf_header__read(int fd) | |||
265 | struct perf_header_attr *attr; | 681 | struct perf_header_attr *attr; |
266 | off_t tmp; | 682 | off_t tmp; |
267 | 683 | ||
268 | do_read(fd, &f_attr, sizeof(f_attr)); | 684 | if (perf_header__getbuffer64(self, fd, &f_attr, sizeof(f_attr))) |
685 | goto out_errno; | ||
686 | |||
269 | tmp = lseek(fd, 0, SEEK_CUR); | 687 | tmp = lseek(fd, 0, SEEK_CUR); |
270 | 688 | ||
271 | attr = perf_header_attr__new(&f_attr.attr); | 689 | attr = perf_header_attr__new(&f_attr.attr); |
690 | if (attr == NULL) | ||
691 | return -ENOMEM; | ||
272 | 692 | ||
273 | nr_ids = f_attr.ids.size / sizeof(u64); | 693 | nr_ids = f_attr.ids.size / sizeof(u64); |
274 | lseek(fd, f_attr.ids.offset, SEEK_SET); | 694 | lseek(fd, f_attr.ids.offset, SEEK_SET); |
275 | 695 | ||
276 | for (j = 0; j < nr_ids; j++) { | 696 | for (j = 0; j < nr_ids; j++) { |
277 | do_read(fd, &f_id, sizeof(f_id)); | 697 | if (perf_header__getbuffer64(self, fd, &f_id, sizeof(f_id))) |
698 | goto out_errno; | ||
278 | 699 | ||
279 | perf_header_attr__add_id(attr, f_id); | 700 | if (perf_header_attr__add_id(attr, f_id) < 0) { |
701 | perf_header_attr__delete(attr); | ||
702 | return -ENOMEM; | ||
703 | } | ||
704 | } | ||
705 | if (perf_header__add_attr(self, attr) < 0) { | ||
706 | perf_header_attr__delete(attr); | ||
707 | return -ENOMEM; | ||
280 | } | 708 | } |
281 | perf_header__add_attr(self, attr); | 709 | |
282 | lseek(fd, tmp, SEEK_SET); | 710 | lseek(fd, tmp, SEEK_SET); |
283 | } | 711 | } |
284 | 712 | ||
285 | if (f_header.event_types.size) { | 713 | if (f_header.event_types.size) { |
286 | lseek(fd, f_header.event_types.offset, SEEK_SET); | 714 | lseek(fd, f_header.event_types.offset, SEEK_SET); |
287 | events = malloc(f_header.event_types.size); | 715 | events = malloc(f_header.event_types.size); |
288 | if (!events) | 716 | if (events == NULL) |
289 | die("nomem"); | 717 | return -ENOMEM; |
290 | do_read(fd, events, f_header.event_types.size); | 718 | if (perf_header__getbuffer64(self, fd, events, |
719 | f_header.event_types.size)) | ||
720 | goto out_errno; | ||
291 | event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type); | 721 | event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type); |
292 | } | 722 | } |
293 | self->event_offset = f_header.event_types.offset; | ||
294 | self->event_size = f_header.event_types.size; | ||
295 | 723 | ||
296 | self->data_offset = f_header.data.offset; | 724 | perf_header__process_sections(self, fd, perf_file_section__process); |
297 | self->data_size = f_header.data.size; | ||
298 | 725 | ||
299 | lseek(fd, self->data_offset, SEEK_SET); | 726 | lseek(fd, self->data_offset, SEEK_SET); |
300 | 727 | ||
301 | self->frozen = 1; | 728 | self->frozen = 1; |
302 | 729 | return 0; | |
303 | return self; | 730 | out_errno: |
731 | return -errno; | ||
304 | } | 732 | } |
305 | 733 | ||
306 | u64 perf_header__sample_type(struct perf_header *header) | 734 | u64 perf_header__sample_type(struct perf_header *header) |