perf record: Introduce a symtab cache
[deliverable/linux.git] / tools / perf / util / header.c
CommitLineData
7c6a1c65
PZ
1#include <sys/types.h>
2#include <unistd.h>
3#include <stdio.h>
4#include <stdlib.h>
8671dab9 5#include <linux/list.h>
7c6a1c65
PZ
6
7#include "util.h"
8#include "header.h"
03456a15
FW
9#include "../perf.h"
10#include "trace-event.h"
301a0b02 11#include "session.h"
8671dab9 12#include "symbol.h"
4778d2e4 13#include "debug.h"
7c6a1c65
PZ
14
15/*
ec60a3fe 16 * Create new perf.data header attribute:
7c6a1c65 17 */
cdd6c482 18struct perf_header_attr *perf_header_attr__new(struct perf_event_attr *attr)
7c6a1c65
PZ
19{
20 struct perf_header_attr *self = malloc(sizeof(*self));
21
dc79c0fc
ACM
22 if (self != NULL) {
23 self->attr = *attr;
24 self->ids = 0;
25 self->size = 1;
26 self->id = malloc(sizeof(u64));
27 if (self->id == NULL) {
28 free(self);
29 self = NULL;
30 }
31 }
7c6a1c65
PZ
32
33 return self;
34}
35
11deb1f9
ACM
36void perf_header_attr__delete(struct perf_header_attr *self)
37{
38 free(self->id);
39 free(self);
40}
41
58754121 42int perf_header_attr__add_id(struct perf_header_attr *self, u64 id)
7c6a1c65
PZ
43{
44 int pos = self->ids;
45
46 self->ids++;
47 if (self->ids > self->size) {
58754121
ACM
48 int nsize = self->size * 2;
49 u64 *nid = realloc(self->id, nsize * sizeof(u64));
50
51 if (nid == NULL)
52 return -1;
53
54 self->size = nsize;
55 self->id = nid;
7c6a1c65
PZ
56 }
57 self->id[pos] = id;
58754121 58 return 0;
7c6a1c65
PZ
59}
60
94c744b6 61int perf_header__init(struct perf_header *self)
7c6a1c65 62{
94c744b6
ACM
63 self->size = 1;
64 self->attr = malloc(sizeof(void *));
65 return self->attr == NULL ? -ENOMEM : 0;
7c6a1c65
PZ
66}
67
94c744b6 68void perf_header__exit(struct perf_header *self)
4dc0a04b
ACM
69{
70 int i;
4dc0a04b 71 for (i = 0; i < self->attrs; ++i)
94c744b6 72 perf_header_attr__delete(self->attr[i]);
4dc0a04b 73 free(self->attr);
4dc0a04b
ACM
74}
75
11deb1f9
ACM
76int perf_header__add_attr(struct perf_header *self,
77 struct perf_header_attr *attr)
7c6a1c65 78{
7c6a1c65 79 if (self->frozen)
11deb1f9 80 return -1;
7c6a1c65 81
4dc0a04b 82 if (self->attrs == self->size) {
11deb1f9
ACM
83 int nsize = self->size * 2;
84 struct perf_header_attr **nattr;
85
86 nattr = realloc(self->attr, nsize * sizeof(void *));
87 if (nattr == NULL)
88 return -1;
89
90 self->size = nsize;
91 self->attr = nattr;
7c6a1c65 92 }
4dc0a04b
ACM
93
94 self->attr[self->attrs++] = attr;
11deb1f9 95 return 0;
7c6a1c65
PZ
96}
97
ec60a3fe
AV
98#define MAX_EVENT_NAME 64
99
8755a8f2
AV
100struct perf_trace_event_type {
101 u64 event_id;
ec60a3fe 102 char name[MAX_EVENT_NAME];
8755a8f2
AV
103};
104
105static int event_count;
106static struct perf_trace_event_type *events;
107
108void perf_header__push_event(u64 id, const char *name)
109{
ec60a3fe 110 if (strlen(name) > MAX_EVENT_NAME)
6beba7ad 111 pr_warning("Event %s will be truncated\n", name);
8755a8f2
AV
112
113 if (!events) {
114 events = malloc(sizeof(struct perf_trace_event_type));
115 if (!events)
116 die("nomem");
117 } else {
118 events = realloc(events, (event_count + 1) * sizeof(struct perf_trace_event_type));
119 if (!events)
120 die("nomem");
121 }
122 memset(&events[event_count], 0, sizeof(struct perf_trace_event_type));
123 events[event_count].event_id = id;
ec60a3fe 124 strncpy(events[event_count].name, name, MAX_EVENT_NAME - 1);
8755a8f2
AV
125 event_count++;
126}
127
128char *perf_header__find_event(u64 id)
129{
130 int i;
131 for (i = 0 ; i < event_count; i++) {
132 if (events[i].event_id == id)
133 return events[i].name;
134 }
135 return NULL;
136}
137
7c6a1c65
PZ
138static const char *__perf_magic = "PERFFILE";
139
140#define PERF_MAGIC (*(u64 *)__perf_magic)
141
7c6a1c65 142struct perf_file_attr {
cdd6c482 143 struct perf_event_attr attr;
7c6a1c65
PZ
144 struct perf_file_section ids;
145};
146
8d06367f
ACM
147void perf_header__set_feat(struct perf_header *self, int feat)
148{
149 set_bit(feat, self->adds_features);
150}
151
152bool perf_header__has_feat(const struct perf_header *self, int feat)
153{
154 return test_bit(feat, self->adds_features);
155}
156
3726cc75 157static int do_write(int fd, const void *buf, size_t size)
7c6a1c65
PZ
158{
159 while (size) {
160 int ret = write(fd, buf, size);
161
162 if (ret < 0)
d5eed904 163 return -errno;
7c6a1c65
PZ
164
165 size -= ret;
166 buf += ret;
167 }
3726cc75
ACM
168
169 return 0;
7c6a1c65
PZ
170}
171
4cf40131
ACM
172#define dsos__for_each_with_build_id(pos, head) \
173 list_for_each_entry(pos, head, node) \
174 if (!pos->has_build_id) \
175 continue; \
176 else
177
b0da954a 178static int __dsos__write_buildid_table(struct list_head *head, int fd)
8671dab9 179{
7691b1ec 180#define NAME_ALIGN 64
e30a3d12 181 struct dso *pos;
7691b1ec 182 static const char zero_buf[NAME_ALIGN];
e30a3d12 183
4cf40131 184 dsos__for_each_with_build_id(pos, head) {
d5eed904 185 int err;
e30a3d12 186 struct build_id_event b;
4cf40131 187 size_t len = pos->long_name_len + 1;
e30a3d12 188
7691b1ec 189 len = ALIGN(len, NAME_ALIGN);
e30a3d12
ACM
190 memset(&b, 0, sizeof(b));
191 memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id));
192 b.header.size = sizeof(b) + len;
d5eed904
ACM
193 err = do_write(fd, &b, sizeof(b));
194 if (err < 0)
195 return err;
7691b1ec
OH
196 err = do_write(fd, pos->long_name, pos->long_name_len + 1);
197 if (err < 0)
198 return err;
d9541ed3 199 err = do_write(fd, zero_buf, len - pos->long_name_len - 1);
d5eed904
ACM
200 if (err < 0)
201 return err;
57f395a7 202 }
3726cc75
ACM
203
204 return 0;
8671dab9
FW
205}
206
b0da954a
ACM
207static int dsos__write_buildid_table(int fd)
208{
209 int err = __dsos__write_buildid_table(&dsos__kernel, fd);
210 if (err == 0)
211 err = __dsos__write_buildid_table(&dsos__user, fd);
212 return err;
213}
214
4cf40131
ACM
215static int dso__cache_build_id(struct dso *self, const char *debugdir)
216{
217 const size_t size = PATH_MAX;
218 char *filename = malloc(size),
219 *linkname = malloc(size), *targetname, *sbuild_id;
220 int len, err = -1;
221
222 if (filename == NULL || linkname == NULL)
223 goto out_free;
224
225 len = snprintf(filename, size, "%s%s", debugdir, self->long_name);
226 if (mkdir_p(filename, 0755))
227 goto out_free;
228
229 len += snprintf(filename + len, sizeof(filename) - len, "/");
230 sbuild_id = filename + len;
231 build_id__sprintf(self->build_id, sizeof(self->build_id), sbuild_id);
232
233 if (access(filename, F_OK) && link(self->long_name, filename) &&
234 copyfile(self->long_name, filename))
235 goto out_free;
236
237 len = snprintf(linkname, size, "%s/.build-id/%.2s",
238 debugdir, sbuild_id);
239
240 if (access(linkname, X_OK) && mkdir_p(linkname, 0755))
241 goto out_free;
242
243 snprintf(linkname + len, size - len, "/%s", sbuild_id + 2);
244 targetname = filename + strlen(debugdir) - 5;
245 memcpy(targetname, "../..", 5);
246
247 if (symlink(targetname, linkname) == 0)
248 err = 0;
249out_free:
250 free(filename);
251 free(linkname);
252 return err;
253}
254
255static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir)
256{
257 struct dso *pos;
258 int err = 0;
259
260 dsos__for_each_with_build_id(pos, head)
261 if (dso__cache_build_id(pos, debugdir))
262 err = -1;
263
264 return err;
265}
266
267static int dsos__cache_build_ids(void)
268{
269 int err_kernel, err_user;
270 char debugdir[PATH_MAX];
271
272 snprintf(debugdir, sizeof(debugdir), "%s/%s", getenv("HOME"),
273 DEBUG_CACHE_DIR);
274
275 if (mkdir(debugdir, 0755) != 0 && errno != EEXIST)
276 return -1;
277
278 err_kernel = __dsos__cache_build_ids(&dsos__kernel, debugdir);
279 err_user = __dsos__cache_build_ids(&dsos__user, debugdir);
280 return err_kernel || err_user ? -1 : 0;
281}
282
d5eed904 283static int perf_header__adds_write(struct perf_header *self, int fd)
2ba08250 284{
9e827dd0
FW
285 int nr_sections;
286 struct perf_file_section *feat_sec;
287 int sec_size;
288 u64 sec_start;
d5eed904 289 int idx = 0, err;
9e827dd0 290
e30a3d12 291 if (dsos__read_build_ids())
9e827dd0
FW
292 perf_header__set_feat(self, HEADER_BUILD_ID);
293
294 nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS);
295 if (!nr_sections)
d5eed904 296 return 0;
9e827dd0
FW
297
298 feat_sec = calloc(sizeof(*feat_sec), nr_sections);
d5eed904
ACM
299 if (feat_sec == NULL)
300 return -ENOMEM;
9e827dd0
FW
301
302 sec_size = sizeof(*feat_sec) * nr_sections;
303
304 sec_start = self->data_offset + self->data_size;
305 lseek(fd, sec_start + sec_size, SEEK_SET);
2ba08250 306
3e13ab2d 307 if (perf_header__has_feat(self, HEADER_TRACE_INFO)) {
9e827dd0
FW
308 struct perf_file_section *trace_sec;
309
310 trace_sec = &feat_sec[idx++];
311
2ba08250 312 /* Write trace info */
9e827dd0 313 trace_sec->offset = lseek(fd, 0, SEEK_CUR);
2ba08250 314 read_tracing_data(fd, attrs, nr_counters);
9e827dd0 315 trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset;
2ba08250 316 }
8671dab9 317
57f395a7 318
9e827dd0
FW
319 if (perf_header__has_feat(self, HEADER_BUILD_ID)) {
320 struct perf_file_section *buildid_sec;
321
322 buildid_sec = &feat_sec[idx++];
323
324 /* Write build-ids */
325 buildid_sec->offset = lseek(fd, 0, SEEK_CUR);
d5eed904
ACM
326 err = dsos__write_buildid_table(fd);
327 if (err < 0) {
328 pr_debug("failed to write buildid table\n");
329 goto out_free;
330 }
9e827dd0 331 buildid_sec->size = lseek(fd, 0, SEEK_CUR) - buildid_sec->offset;
4cf40131 332 dsos__cache_build_ids();
8671dab9 333 }
9e827dd0
FW
334
335 lseek(fd, sec_start, SEEK_SET);
d5eed904
ACM
336 err = do_write(fd, feat_sec, sec_size);
337 if (err < 0)
338 pr_debug("failed to write feature section\n");
339out_free:
9e827dd0 340 free(feat_sec);
d5eed904 341 return err;
9e827dd0 342}
2ba08250 343
d5eed904 344int perf_header__write(struct perf_header *self, int fd, bool at_exit)
7c6a1c65
PZ
345{
346 struct perf_file_header f_header;
347 struct perf_file_attr f_attr;
348 struct perf_header_attr *attr;
d5eed904 349 int i, err;
7c6a1c65
PZ
350
351 lseek(fd, sizeof(f_header), SEEK_SET);
352
353
354 for (i = 0; i < self->attrs; i++) {
355 attr = self->attr[i];
356
357 attr->id_offset = lseek(fd, 0, SEEK_CUR);
d5eed904
ACM
358 err = do_write(fd, attr->id, attr->ids * sizeof(u64));
359 if (err < 0) {
360 pr_debug("failed to write perf header\n");
361 return err;
362 }
7c6a1c65
PZ
363 }
364
365
366 self->attr_offset = lseek(fd, 0, SEEK_CUR);
367
368 for (i = 0; i < self->attrs; i++) {
369 attr = self->attr[i];
370
371 f_attr = (struct perf_file_attr){
372 .attr = attr->attr,
373 .ids = {
374 .offset = attr->id_offset,
375 .size = attr->ids * sizeof(u64),
376 }
377 };
d5eed904
ACM
378 err = do_write(fd, &f_attr, sizeof(f_attr));
379 if (err < 0) {
380 pr_debug("failed to write perf header attribute\n");
381 return err;
382 }
7c6a1c65
PZ
383 }
384
8755a8f2
AV
385 self->event_offset = lseek(fd, 0, SEEK_CUR);
386 self->event_size = event_count * sizeof(struct perf_trace_event_type);
d5eed904
ACM
387 if (events) {
388 err = do_write(fd, events, self->event_size);
389 if (err < 0) {
390 pr_debug("failed to write perf header events\n");
391 return err;
392 }
393 }
8755a8f2 394
7c6a1c65
PZ
395 self->data_offset = lseek(fd, 0, SEEK_CUR);
396
d5eed904
ACM
397 if (at_exit) {
398 err = perf_header__adds_write(self, fd);
399 if (err < 0)
400 return err;
401 }
9e827dd0 402
7c6a1c65
PZ
403 f_header = (struct perf_file_header){
404 .magic = PERF_MAGIC,
405 .size = sizeof(f_header),
406 .attr_size = sizeof(f_attr),
407 .attrs = {
408 .offset = self->attr_offset,
409 .size = self->attrs * sizeof(f_attr),
410 },
411 .data = {
412 .offset = self->data_offset,
413 .size = self->data_size,
414 },
8755a8f2
AV
415 .event_types = {
416 .offset = self->event_offset,
417 .size = self->event_size,
418 },
7c6a1c65
PZ
419 };
420
db9f11e3 421 memcpy(&f_header.adds_features, &self->adds_features, sizeof(self->adds_features));
2ba08250 422
7c6a1c65 423 lseek(fd, 0, SEEK_SET);
d5eed904
ACM
424 err = do_write(fd, &f_header, sizeof(f_header));
425 if (err < 0) {
426 pr_debug("failed to write perf header\n");
427 return err;
428 }
7c6a1c65
PZ
429 lseek(fd, self->data_offset + self->data_size, SEEK_SET);
430
431 self->frozen = 1;
d5eed904 432 return 0;
7c6a1c65
PZ
433}
434
435static void do_read(int fd, void *buf, size_t size)
436{
437 while (size) {
438 int ret = read(fd, buf, size);
439
440 if (ret < 0)
441 die("failed to read");
7eac7e9e
PH
442 if (ret == 0)
443 die("failed to read: missing data");
7c6a1c65
PZ
444
445 size -= ret;
446 buf += ret;
447 }
448}
449
37562eac
ACM
450int perf_header__process_sections(struct perf_header *self, int fd,
451 int (*process)(struct perf_file_section *self,
452 int feat, int fd))
2ba08250 453{
9e827dd0
FW
454 struct perf_file_section *feat_sec;
455 int nr_sections;
456 int sec_size;
457 int idx = 0;
37562eac 458 int err = 0, feat = 1;
9e827dd0
FW
459
460 nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS);
461 if (!nr_sections)
37562eac 462 return 0;
9e827dd0
FW
463
464 feat_sec = calloc(sizeof(*feat_sec), nr_sections);
465 if (!feat_sec)
37562eac 466 return -1;
9e827dd0
FW
467
468 sec_size = sizeof(*feat_sec) * nr_sections;
469
470 lseek(fd, self->data_offset + self->data_size, SEEK_SET);
471
472 do_read(fd, feat_sec, sec_size);
473
37562eac
ACM
474 while (idx < nr_sections && feat < HEADER_LAST_FEATURE) {
475 if (perf_header__has_feat(self, feat)) {
476 struct perf_file_section *sec = &feat_sec[idx++];
2ba08250 477
37562eac
ACM
478 err = process(sec, feat, fd);
479 if (err < 0)
480 break;
481 }
482 ++feat;
2ba08250 483 }
4778d2e4 484
37562eac
ACM
485 free(feat_sec);
486 return err;
487};
4778d2e4 488
37562eac
ACM
489int perf_file_header__read(struct perf_file_header *self,
490 struct perf_header *ph, int fd)
491{
492 lseek(fd, 0, SEEK_SET);
493 do_read(fd, self, sizeof(*self));
494
495 if (self->magic != PERF_MAGIC ||
496 self->attr_size != sizeof(struct perf_file_attr))
497 return -1;
498
499 if (self->size != sizeof(*self)) {
500 /* Support the previous format */
501 if (self->size == offsetof(typeof(*self), adds_features))
502 bitmap_zero(self->adds_features, HEADER_FEAT_BITS);
503 else
504 return -1;
4778d2e4 505 }
9e827dd0 506
37562eac
ACM
507 memcpy(&ph->adds_features, &self->adds_features,
508 sizeof(self->adds_features));
509
510 ph->event_offset = self->event_types.offset;
511 ph->event_size = self->event_types.size;
512 ph->data_offset = self->data.offset;
513 ph->data_size = self->data.size;
514 return 0;
515}
516
517static int perf_file_section__process(struct perf_file_section *self,
518 int feat, int fd)
519{
520 if (lseek(fd, self->offset, SEEK_SET) < 0) {
521 pr_debug("Failed to lseek to %Ld offset for feature %d, "
522 "continuing...\n", self->offset, feat);
523 return 0;
524 }
525
526 switch (feat) {
527 case HEADER_TRACE_INFO:
528 trace_report(fd);
529 break;
530
531 case HEADER_BUILD_ID:
532 if (perf_header__read_build_ids(fd, self->offset, self->size))
533 pr_debug("Failed to read buildids, continuing...\n");
534 break;
535 default:
536 pr_debug("unknown feature %d, continuing...\n", feat);
537 }
538
539 return 0;
540}
2ba08250 541
4dc0a04b 542int perf_header__read(struct perf_header *self, int fd)
7c6a1c65 543{
7c6a1c65
PZ
544 struct perf_file_header f_header;
545 struct perf_file_attr f_attr;
546 u64 f_id;
7c6a1c65
PZ
547 int nr_attrs, nr_ids, i, j;
548
4dc0a04b
ACM
549 if (perf_file_header__read(&f_header, self, fd) < 0) {
550 pr_debug("incompatible file format\n");
551 return -EINVAL;
552 }
7c6a1c65
PZ
553
554 nr_attrs = f_header.attrs.size / sizeof(f_attr);
555 lseek(fd, f_header.attrs.offset, SEEK_SET);
556
557 for (i = 0; i < nr_attrs; i++) {
558 struct perf_header_attr *attr;
1c222bce 559 off_t tmp;
7c6a1c65
PZ
560
561 do_read(fd, &f_attr, sizeof(f_attr));
1c222bce 562 tmp = lseek(fd, 0, SEEK_CUR);
7c6a1c65
PZ
563
564 attr = perf_header_attr__new(&f_attr.attr);
dc79c0fc 565 if (attr == NULL)
4dc0a04b 566 return -ENOMEM;
7c6a1c65
PZ
567
568 nr_ids = f_attr.ids.size / sizeof(u64);
569 lseek(fd, f_attr.ids.offset, SEEK_SET);
570
571 for (j = 0; j < nr_ids; j++) {
572 do_read(fd, &f_id, sizeof(f_id));
573
4dc0a04b
ACM
574 if (perf_header_attr__add_id(attr, f_id) < 0) {
575 perf_header_attr__delete(attr);
576 return -ENOMEM;
577 }
578 }
579 if (perf_header__add_attr(self, attr) < 0) {
580 perf_header_attr__delete(attr);
581 return -ENOMEM;
7c6a1c65 582 }
11deb1f9 583
7c6a1c65
PZ
584 lseek(fd, tmp, SEEK_SET);
585 }
586
8755a8f2
AV
587 if (f_header.event_types.size) {
588 lseek(fd, f_header.event_types.offset, SEEK_SET);
589 events = malloc(f_header.event_types.size);
4dc0a04b
ACM
590 if (events == NULL)
591 return -ENOMEM;
8755a8f2
AV
592 do_read(fd, events, f_header.event_types.size);
593 event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type);
594 }
03456a15 595
37562eac 596 perf_header__process_sections(self, fd, perf_file_section__process);
4778d2e4 597
2e01d179 598 lseek(fd, self->data_offset, SEEK_SET);
7c6a1c65
PZ
599
600 self->frozen = 1;
4dc0a04b 601 return 0;
7c6a1c65 602}
0d3a5c88
FW
603
604u64 perf_header__sample_type(struct perf_header *header)
605{
606 u64 type = 0;
607 int i;
608
609 for (i = 0; i < header->attrs; i++) {
610 struct perf_header_attr *attr = header->attr[i];
611
612 if (!type)
613 type = attr->attr.sample_type;
614 else if (type != attr->attr.sample_type)
615 die("non matching sample_type");
616 }
617
618 return type;
619}
620
cdd6c482 621struct perf_event_attr *
0d3a5c88
FW
622perf_header__find_attr(u64 id, struct perf_header *header)
623{
624 int i;
625
626 for (i = 0; i < header->attrs; i++) {
627 struct perf_header_attr *attr = header->attr[i];
628 int j;
629
630 for (j = 0; j < attr->ids; j++) {
631 if (attr->id[j] == id)
632 return &attr->attr;
633 }
634 }
635
636 return NULL;
637}
This page took 0.104679 seconds and 5 git commands to generate.