header.c 12.0 KB
Newer Older
1 2 3 4
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
5
#include <linux/list.h>
6 7 8

#include "util.h"
#include "header.h"
9 10
#include "../perf.h"
#include "trace-event.h"
11
#include "symbol.h"
12 13
#include "data_map.h"
#include "debug.h"
14 15

/*
16
 * Create new perf.data header attribute:
17
 */
18
struct perf_header_attr *perf_header_attr__new(struct perf_event_attr *attr)
19 20 21
{
	struct perf_header_attr *self = malloc(sizeof(*self));

22 23 24 25 26 27 28 29 30 31
	if (self != NULL) {
		self->attr = *attr;
		self->ids  = 0;
		self->size = 1;
		self->id   = malloc(sizeof(u64));
		if (self->id == NULL) {
			free(self);
			self = NULL;
		}
	}
32 33 34 35

	return self;
}

36 37 38 39 40 41
void perf_header_attr__delete(struct perf_header_attr *self)
{
	free(self->id);
	free(self);
}

42
int perf_header_attr__add_id(struct perf_header_attr *self, u64 id)
43 44 45 46 47
{
	int pos = self->ids;

	self->ids++;
	if (self->ids > self->size) {
48 49 50 51 52 53 54 55
		int nsize = self->size * 2;
		u64 *nid = realloc(self->id, nsize * sizeof(u64));

		if (nid == NULL)
			return -1;

		self->size = nsize;
		self->id = nid;
56 57
	}
	self->id[pos] = id;
58
	return 0;
59 60
}

61
int perf_header__init(struct perf_header *self)
62
{
63 64 65
	self->size = 1;
	self->attr = malloc(sizeof(void *));
	return self->attr == NULL ? -ENOMEM : 0;
66 67
}

68
void perf_header__exit(struct perf_header *self)
69 70 71
{
	int i;
	for (i = 0; i < self->attrs; ++i)
72
                perf_header_attr__delete(self->attr[i]);
73 74 75
	free(self->attr);
}

76 77
int perf_header__add_attr(struct perf_header *self,
			  struct perf_header_attr *attr)
78 79
{
	if (self->frozen)
80
		return -1;
81

82
	if (self->attrs == self->size) {
83 84 85 86 87 88 89 90 91
		int nsize = self->size * 2;
		struct perf_header_attr **nattr;

		nattr = realloc(self->attr, nsize * sizeof(void *));
		if (nattr == NULL)
			return -1;

		self->size = nsize;
		self->attr = nattr;
92
	}
93 94

	self->attr[self->attrs++] = attr;
95
	return 0;
96 97
}

98 99
#define MAX_EVENT_NAME 64

100 101
struct perf_trace_event_type {
	u64	event_id;
102
	char	name[MAX_EVENT_NAME];
103 104 105 106 107 108 109
};

static int event_count;
static struct perf_trace_event_type *events;

void perf_header__push_event(u64 id, const char *name)
{
110
	if (strlen(name) > MAX_EVENT_NAME)
111
		pr_warning("Event %s will be truncated\n", name);
112 113 114 115 116 117 118 119 120 121 122 123

	if (!events) {
		events = malloc(sizeof(struct perf_trace_event_type));
		if (!events)
			die("nomem");
	} else {
		events = realloc(events, (event_count + 1) * sizeof(struct perf_trace_event_type));
		if (!events)
			die("nomem");
	}
	memset(&events[event_count], 0, sizeof(struct perf_trace_event_type));
	events[event_count].event_id = id;
124
	strncpy(events[event_count].name, name, MAX_EVENT_NAME - 1);
125 126 127 128 129 130 131 132 133 134 135 136 137
	event_count++;
}

char *perf_header__find_event(u64 id)
{
	int i;
	for (i = 0 ; i < event_count; i++) {
		if (events[i].event_id == id)
			return events[i].name;
	}
	return NULL;
}

138 139 140 141 142
static const char *__perf_magic = "PERFFILE";

#define PERF_MAGIC	(*(u64 *)__perf_magic)

struct perf_file_attr {
143
	struct perf_event_attr	attr;
144 145 146
	struct perf_file_section	ids;
};

147 148 149 150 151 152 153 154 155 156
void perf_header__set_feat(struct perf_header *self, int feat)
{
	set_bit(feat, self->adds_features);
}

bool perf_header__has_feat(const struct perf_header *self, int feat)
{
	return test_bit(feat, self->adds_features);
}

157
static int do_write(int fd, const void *buf, size_t size)
158 159 160 161 162
{
	while (size) {
		int ret = write(fd, buf, size);

		if (ret < 0)
163
			return -errno;
164 165 166 167

		size -= ret;
		buf += ret;
	}
168 169

	return 0;
170 171
}

172
static int __dsos__write_buildid_table(struct list_head *head, int fd)
173
{
O
OGAWA Hirofumi 已提交
174
#define NAME_ALIGN	64
175
	struct dso *pos;
O
OGAWA Hirofumi 已提交
176
	static const char zero_buf[NAME_ALIGN];
177

178
	list_for_each_entry(pos, head, node) {
179
		int err;
180 181 182 183 184 185
		struct build_id_event b;
		size_t len;

		if (!pos->has_build_id)
			continue;
		len = pos->long_name_len + 1;
O
OGAWA Hirofumi 已提交
186
		len = ALIGN(len, NAME_ALIGN);
187 188 189
		memset(&b, 0, sizeof(b));
		memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id));
		b.header.size = sizeof(b) + len;
190 191 192
		err = do_write(fd, &b, sizeof(b));
		if (err < 0)
			return err;
O
OGAWA Hirofumi 已提交
193 194 195
		err = do_write(fd, pos->long_name, pos->long_name_len + 1);
		if (err < 0)
			return err;
196
		err = do_write(fd, zero_buf, len - pos->long_name_len - 1);
197 198
		if (err < 0)
			return err;
199
	}
200 201

	return 0;
202 203
}

204 205 206 207 208 209 210 211
static int dsos__write_buildid_table(int fd)
{
	int err = __dsos__write_buildid_table(&dsos__kernel, fd);
	if (err == 0)
		err = __dsos__write_buildid_table(&dsos__user, fd);
	return err;
}

212
static int perf_header__adds_write(struct perf_header *self, int fd)
213
{
214 215 216 217
	int nr_sections;
	struct perf_file_section *feat_sec;
	int sec_size;
	u64 sec_start;
218
	int idx = 0, err;
219

220
	if (dsos__read_build_ids())
221 222 223 224
		perf_header__set_feat(self, HEADER_BUILD_ID);

	nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS);
	if (!nr_sections)
225
		return 0;
226 227

	feat_sec = calloc(sizeof(*feat_sec), nr_sections);
228 229
	if (feat_sec == NULL)
		return -ENOMEM;
230 231 232 233 234

	sec_size = sizeof(*feat_sec) * nr_sections;

	sec_start = self->data_offset + self->data_size;
	lseek(fd, sec_start + sec_size, SEEK_SET);
235

236
	if (perf_header__has_feat(self, HEADER_TRACE_INFO)) {
237 238 239 240
		struct perf_file_section *trace_sec;

		trace_sec = &feat_sec[idx++];

241
		/* Write trace info */
242
		trace_sec->offset = lseek(fd, 0, SEEK_CUR);
243
		read_tracing_data(fd, attrs, nr_counters);
244
		trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset;
245
	}
246

247

248 249 250 251 252 253 254
	if (perf_header__has_feat(self, HEADER_BUILD_ID)) {
		struct perf_file_section *buildid_sec;

		buildid_sec = &feat_sec[idx++];

		/* Write build-ids */
		buildid_sec->offset = lseek(fd, 0, SEEK_CUR);
255 256 257 258 259
		err = dsos__write_buildid_table(fd);
		if (err < 0) {
			pr_debug("failed to write buildid table\n");
			goto out_free;
		}
260
		buildid_sec->size = lseek(fd, 0, SEEK_CUR) - buildid_sec->offset;
261
	}
262 263

	lseek(fd, sec_start, SEEK_SET);
264 265 266 267
	err = do_write(fd, feat_sec, sec_size);
	if (err < 0)
		pr_debug("failed to write feature section\n");
out_free:
268
	free(feat_sec);
269
	return err;
270
}
271

272
int perf_header__write(struct perf_header *self, int fd, bool at_exit)
273 274 275 276
{
	struct perf_file_header f_header;
	struct perf_file_attr   f_attr;
	struct perf_header_attr	*attr;
277
	int i, err;
278 279 280 281 282 283 284 285

	lseek(fd, sizeof(f_header), SEEK_SET);


	for (i = 0; i < self->attrs; i++) {
		attr = self->attr[i];

		attr->id_offset = lseek(fd, 0, SEEK_CUR);
286 287 288 289 290
		err = do_write(fd, attr->id, attr->ids * sizeof(u64));
		if (err < 0) {
			pr_debug("failed to write perf header\n");
			return err;
		}
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
	}


	self->attr_offset = lseek(fd, 0, SEEK_CUR);

	for (i = 0; i < self->attrs; i++) {
		attr = self->attr[i];

		f_attr = (struct perf_file_attr){
			.attr = attr->attr,
			.ids  = {
				.offset = attr->id_offset,
				.size   = attr->ids * sizeof(u64),
			}
		};
306 307 308 309 310
		err = do_write(fd, &f_attr, sizeof(f_attr));
		if (err < 0) {
			pr_debug("failed to write perf header attribute\n");
			return err;
		}
311 312
	}

313 314
	self->event_offset = lseek(fd, 0, SEEK_CUR);
	self->event_size = event_count * sizeof(struct perf_trace_event_type);
315 316 317 318 319 320 321
	if (events) {
		err = do_write(fd, events, self->event_size);
		if (err < 0) {
			pr_debug("failed to write perf header events\n");
			return err;
		}
	}
322

323 324
	self->data_offset = lseek(fd, 0, SEEK_CUR);

325 326 327 328 329
	if (at_exit) {
		err = perf_header__adds_write(self, fd);
		if (err < 0)
			return err;
	}
330

331 332 333 334 335 336 337 338 339 340 341 342
	f_header = (struct perf_file_header){
		.magic	   = PERF_MAGIC,
		.size	   = sizeof(f_header),
		.attr_size = sizeof(f_attr),
		.attrs = {
			.offset = self->attr_offset,
			.size   = self->attrs * sizeof(f_attr),
		},
		.data = {
			.offset = self->data_offset,
			.size	= self->data_size,
		},
343 344 345 346
		.event_types = {
			.offset = self->event_offset,
			.size	= self->event_size,
		},
347 348
	};

349
	memcpy(&f_header.adds_features, &self->adds_features, sizeof(self->adds_features));
350

351
	lseek(fd, 0, SEEK_SET);
352 353 354 355 356
	err = do_write(fd, &f_header, sizeof(f_header));
	if (err < 0) {
		pr_debug("failed to write perf header\n");
		return err;
	}
357 358 359
	lseek(fd, self->data_offset + self->data_size, SEEK_SET);

	self->frozen = 1;
360
	return 0;
361 362 363 364 365 366 367 368 369
}

static void do_read(int fd, void *buf, size_t size)
{
	while (size) {
		int ret = read(fd, buf, size);

		if (ret < 0)
			die("failed to read");
370 371
		if (ret == 0)
			die("failed to read: missing data");
372 373 374 375 376 377

		size -= ret;
		buf += ret;
	}
}

378 379 380
int perf_header__process_sections(struct perf_header *self, int fd,
				  int (*process)(struct perf_file_section *self,
						 int feat, int fd))
381
{
382 383 384 385
	struct perf_file_section *feat_sec;
	int nr_sections;
	int sec_size;
	int idx = 0;
386
	int err = 0, feat = 1;
387 388 389

	nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS);
	if (!nr_sections)
390
		return 0;
391 392 393

	feat_sec = calloc(sizeof(*feat_sec), nr_sections);
	if (!feat_sec)
394
		return -1;
395 396 397 398 399 400 401

	sec_size = sizeof(*feat_sec) * nr_sections;

	lseek(fd, self->data_offset + self->data_size, SEEK_SET);

	do_read(fd, feat_sec, sec_size);

402 403 404
	while (idx < nr_sections && feat < HEADER_LAST_FEATURE) {
		if (perf_header__has_feat(self, feat)) {
			struct perf_file_section *sec = &feat_sec[idx++];
405

406 407 408 409 410
			err = process(sec, feat, fd);
			if (err < 0)
				break;
		}
		++feat;
411
	}
412

413 414 415
	free(feat_sec);
	return err;
};
416

417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432
int perf_file_header__read(struct perf_file_header *self,
			   struct perf_header *ph, int fd)
{
	lseek(fd, 0, SEEK_SET);
	do_read(fd, self, sizeof(*self));

	if (self->magic     != PERF_MAGIC ||
	    self->attr_size != sizeof(struct perf_file_attr))
		return -1;

	if (self->size != sizeof(*self)) {
		/* Support the previous format */
		if (self->size == offsetof(typeof(*self), adds_features))
			bitmap_zero(self->adds_features, HEADER_FEAT_BITS);
		else
			return -1;
433
	}
434

435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468
	memcpy(&ph->adds_features, &self->adds_features,
	       sizeof(self->adds_features));

	ph->event_offset = self->event_types.offset;
	ph->event_size	 = self->event_types.size;
	ph->data_offset	 = self->data.offset;
	ph->data_size	 = self->data.size;
	return 0;
}

static int perf_file_section__process(struct perf_file_section *self,
				      int feat, int fd)
{
	if (lseek(fd, self->offset, SEEK_SET) < 0) {
		pr_debug("Failed to lseek to %Ld offset for feature %d, "
			 "continuing...\n", self->offset, feat);
		return 0;
	}

	switch (feat) {
	case HEADER_TRACE_INFO:
		trace_report(fd);
		break;

	case HEADER_BUILD_ID:
		if (perf_header__read_build_ids(fd, self->offset, self->size))
			pr_debug("Failed to read buildids, continuing...\n");
		break;
	default:
		pr_debug("unknown feature %d, continuing...\n", feat);
	}

	return 0;
}
469

470
int perf_header__read(struct perf_header *self, int fd)
471 472 473 474 475 476
{
	struct perf_file_header f_header;
	struct perf_file_attr	f_attr;
	u64			f_id;
	int nr_attrs, nr_ids, i, j;

477 478 479 480
	if (perf_file_header__read(&f_header, self, fd) < 0) {
		pr_debug("incompatible file format\n");
		return -EINVAL;
	}
481 482 483 484 485 486

	nr_attrs = f_header.attrs.size / sizeof(f_attr);
	lseek(fd, f_header.attrs.offset, SEEK_SET);

	for (i = 0; i < nr_attrs; i++) {
		struct perf_header_attr *attr;
487
		off_t tmp;
488 489

		do_read(fd, &f_attr, sizeof(f_attr));
490
		tmp = lseek(fd, 0, SEEK_CUR);
491 492

		attr = perf_header_attr__new(&f_attr.attr);
493
		if (attr == NULL)
494
			 return -ENOMEM;
495 496 497 498 499 500 501

		nr_ids = f_attr.ids.size / sizeof(u64);
		lseek(fd, f_attr.ids.offset, SEEK_SET);

		for (j = 0; j < nr_ids; j++) {
			do_read(fd, &f_id, sizeof(f_id));

502 503 504 505 506 507 508 509
			if (perf_header_attr__add_id(attr, f_id) < 0) {
				perf_header_attr__delete(attr);
				return -ENOMEM;
			}
		}
		if (perf_header__add_attr(self, attr) < 0) {
			perf_header_attr__delete(attr);
			return -ENOMEM;
510
		}
511

512 513 514
		lseek(fd, tmp, SEEK_SET);
	}

515 516 517
	if (f_header.event_types.size) {
		lseek(fd, f_header.event_types.offset, SEEK_SET);
		events = malloc(f_header.event_types.size);
518 519
		if (events == NULL)
			return -ENOMEM;
520 521 522
		do_read(fd, events, f_header.event_types.size);
		event_count =  f_header.event_types.size / sizeof(struct perf_trace_event_type);
	}
523

524
	perf_header__process_sections(self, fd, perf_file_section__process);
525

526
	lseek(fd, self->data_offset, SEEK_SET);
527 528

	self->frozen = 1;
529
	return 0;
530
}
531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548

u64 perf_header__sample_type(struct perf_header *header)
{
	u64 type = 0;
	int i;

	for (i = 0; i < header->attrs; i++) {
		struct perf_header_attr *attr = header->attr[i];

		if (!type)
			type = attr->attr.sample_type;
		else if (type != attr->attr.sample_type)
			die("non matching sample_type");
	}

	return type;
}

549
struct perf_event_attr *
550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565
perf_header__find_attr(u64 id, struct perf_header *header)
{
	int i;

	for (i = 0; i < header->attrs; i++) {
		struct perf_header_attr *attr = header->attr[i];
		int j;

		for (j = 0; j < attr->ids; j++) {
			if (attr->id[j] == id)
				return &attr->attr;
		}
	}

	return NULL;
}