Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7c6a1c65 authored by Peter Zijlstra's avatar Peter Zijlstra Committed by Ingo Molnar
Browse files

perf_counter tools: Rework the file format



Create a structured file format that includes the full
perf_counter_attr and all its relevant counter IDs so that
the reporting program has full information.

Signed-off-by: default avatarPeter Zijlstra <a.p.zijlstra@chello.nl>
LKML-Reference: <new-submission>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent e5c59547
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -290,7 +290,7 @@ LIB_FILE=libperf.a

LIB_H += ../../include/linux/perf_counter.h
LIB_H += perf.h
LIB_H += types.h
LIB_H += util/types.h
LIB_H += util/list.h
LIB_H += util/rbtree.h
LIB_H += util/levenshtein.h
@@ -328,6 +328,7 @@ LIB_OBJS += util/sigchain.o
LIB_OBJS += util/symbol.o
LIB_OBJS += util/color.o
LIB_OBJS += util/pager.o
LIB_OBJS += util/header.o

BUILTIN_OBJS += builtin-annotate.o
BUILTIN_OBJS += builtin-help.o
+65 −35
Original line number Diff line number Diff line
@@ -14,6 +14,8 @@
#include "util/parse-events.h"
#include "util/string.h"

#include "util/header.h"

#include <unistd.h>
#include <sched.h>

@@ -52,7 +54,8 @@ static int nr_poll;
static int			nr_cpu;

static int			file_new = 1;
static struct perf_file_header	file_header;

struct perf_header		*header;

struct mmap_event {
	struct perf_event_header	header;
@@ -328,7 +331,7 @@ static void pid_synthesize_mmap_samples(pid_t pid)
	fclose(fp);
}

static void synthesize_samples(void)
static void synthesize_all(void)
{
	DIR *proc;
	struct dirent dirent, *next;
@@ -352,10 +355,35 @@ static void synthesize_samples(void)

static int group_fd;

static struct perf_header_attr *get_header_attr(struct perf_counter_attr *a, int nr)
{
	struct perf_header_attr *h_attr;

	if (nr < header->attrs) {
		h_attr = header->attr[nr];
	} else {
		h_attr = perf_header_attr__new(a);
		perf_header__add_attr(header, h_attr);
	}

	return h_attr;
}

static void create_counter(int counter, int cpu, pid_t pid)
{
	struct perf_counter_attr *attr = attrs + counter;
	int track = 1;
	struct perf_header_attr *h_attr;
	int track = !counter; /* only the first counter needs these */
	struct {
		u64 count;
		u64 time_enabled;
		u64 time_running;
		u64 id;
	} read_data;

	attr->read_format	= PERF_FORMAT_TOTAL_TIME_ENABLED |
				  PERF_FORMAT_TOTAL_TIME_RUNNING |
				  PERF_FORMAT_ID;

	attr->sample_type	= PERF_SAMPLE_IP | PERF_SAMPLE_TID;

@@ -368,22 +396,11 @@ static void create_counter(int counter, int cpu, pid_t pid)
	if (call_graph)
		attr->sample_type	|= PERF_SAMPLE_CALLCHAIN;

	if (file_new) {
		file_header.sample_type = attr->sample_type;
	} else {
		if (file_header.sample_type != attr->sample_type) {
			fprintf(stderr, "incompatible append\n");
			exit(-1);
		}
	}

	attr->mmap		= track;
	attr->comm		= track;
	attr->inherit		= (cpu < 0) && inherit;
	attr->disabled		= 1;

	track = 0; /* only the first counter needs these */

try_again:
	fd[nr_cpu][counter] = sys_perf_counter_open(attr, pid, cpu, group_fd, 0);

@@ -414,6 +431,19 @@ static void create_counter(int counter, int cpu, pid_t pid)
		exit(-1);
	}

	h_attr = get_header_attr(attr, counter);

	if (!file_new) {
		if (memcmp(&h_attr->attr, attr, sizeof(*attr))) {
			fprintf(stderr, "incompatible append\n");
			exit(-1);
		}
	}

	read(fd[nr_cpu][counter], &read_data, sizeof(read_data));

	perf_header_attr__add_id(h_attr, read_data.id);

	assert(fd[nr_cpu][counter] >= 0);
	fcntl(fd[nr_cpu][counter], F_SETFL, O_NONBLOCK);

@@ -444,11 +474,6 @@ static void open_counters(int cpu, pid_t pid)
{
	int counter;

	if (pid > 0) {
		pid_synthesize_comm_event(pid, 0);
		pid_synthesize_mmap_samples(pid);
	}

	group_fd = -1;
	for (counter = 0; counter < nr_counters; counter++)
		create_counter(counter, cpu, pid);
@@ -458,17 +483,16 @@ static void open_counters(int cpu, pid_t pid)

static void atexit_header(void)
{
	file_header.data_size += bytes_written;
	header->data_size += bytes_written;

	if (pwrite(output, &file_header, sizeof(file_header), 0) == -1)
		perror("failed to write on file headers");
	perf_header__write(header, output);
}

static int __cmd_record(int argc, const char **argv)
{
	int i, counter;
	struct stat st;
	pid_t pid;
	pid_t pid = 0;
	int flags;
	int ret;

@@ -499,22 +523,31 @@ static int __cmd_record(int argc, const char **argv)
		exit(-1);
	}

	if (!file_new) {
		if (read(output, &file_header, sizeof(file_header)) == -1) {
			perror("failed to read file headers");
			exit(-1);
		}

		lseek(output, file_header.data_size, SEEK_CUR);
	}
	if (!file_new)
		header = perf_header__read(output);
	else
		header = perf_header__new();

	atexit(atexit_header);

	if (!system_wide) {
		open_counters(-1, target_pid != -1 ? target_pid : getpid());
		pid = target_pid;
		if (pid == -1)
			pid = getpid();

		open_counters(-1, pid);
	} else for (i = 0; i < nr_cpus; i++)
		open_counters(i, target_pid);

	if (file_new)
		perf_header__write(header, output);

	if (!system_wide) {
		pid_synthesize_comm_event(pid, 0);
		pid_synthesize_mmap_samples(pid);
	} else
		synthesize_all();

	if (target_pid == -1 && argc) {
		pid = fork();
		if (pid < 0)
@@ -538,9 +571,6 @@ static int __cmd_record(int argc, const char **argv)
		}
	}

	if (system_wide)
		synthesize_samples();

	while (!done) {
		int hits = samples;

+28 −9
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#include "util/string.h"

#include "perf.h"
#include "util/header.h"

#include "util/parse-options.h"
#include "util/parse-events.h"
@@ -1385,13 +1386,27 @@ process_event(event_t *event, unsigned long offset, unsigned long head)
	return 0;
}

static struct perf_file_header		file_header;
static struct perf_header	*header;

static int perf_header__has_sample(u64 sample_mask)
{
	int i;

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

		if (!(attr->attr.sample_type & sample_mask))
			return 0;
	}

	return 1;
}

static int __cmd_report(void)
{
	int ret, rc = EXIT_FAILURE;
	unsigned long offset = 0;
	unsigned long head = sizeof(file_header);
	unsigned long head, shift;
	struct stat stat;
	event_t *event;
	uint32_t size;
@@ -1419,13 +1434,11 @@ static int __cmd_report(void)
		exit(0);
	}

	if (read(input, &file_header, sizeof(file_header)) == -1) {
		perror("failed to read file headers");
		exit(-1);
	}
	header = perf_header__read(input);
	head = header->data_offset;

	if (sort__has_parent &&
	    !(file_header.sample_type & PERF_SAMPLE_CALLCHAIN)) {
	    !perf_header__has_sample(PERF_SAMPLE_CALLCHAIN)) {
		fprintf(stderr, "selected --sort parent, but no callchain data\n");
		exit(-1);
	}
@@ -1445,6 +1458,11 @@ static int __cmd_report(void)
		cwd = NULL;
		cwdlen = 0;
	}

	shift = page_size * (head / page_size);
	offset += shift;
	head -= shift;

remap:
	buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ,
			   MAP_SHARED, input, offset);
@@ -1461,9 +1479,10 @@ static int __cmd_report(void)
		size = 8;

	if (head + event->header.size >= page_size * mmap_window) {
		unsigned long shift = page_size * (head / page_size);
		int ret;

		shift = page_size * (head / page_size);

		ret = munmap(buf, page_size * mmap_window);
		assert(ret == 0);

@@ -1501,7 +1520,7 @@ static int __cmd_report(void)

	head += size;

	if (offset + head >= sizeof(file_header) + file_header.data_size)
	if (offset + head >= header->data_offset + header->data_size)
		goto done;

	if (offset + head < stat.st_size)
+1 −7
Original line number Diff line number Diff line
@@ -19,7 +19,7 @@
#include <sys/syscall.h>

#include "../../include/linux/perf_counter.h"
#include "types.h"
#include "util/types.h"

/*
 * prctl(PR_TASK_PERF_COUNTERS_DISABLE) will (cheaply) disable all
@@ -66,10 +66,4 @@ sys_perf_counter_open(struct perf_counter_attr *attr,
#define MAX_COUNTERS			256
#define MAX_NR_CPUS			256

struct perf_file_header {
	u64	version;
	u64	sample_type;
	u64	data_size;
};

#endif
+242 −0
Original line number Diff line number Diff line
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

#include "util.h"
#include "header.h"

/*
 *
 */

struct perf_header_attr *perf_header_attr__new(struct perf_counter_attr *attr)
{
	struct perf_header_attr *self = malloc(sizeof(*self));

	if (!self)
		die("nomem");

	self->attr = *attr;
	self->ids = 0;
	self->size = 1;
	self->id = malloc(sizeof(u64));

	if (!self->id)
		die("nomem");

	return self;
}

void perf_header_attr__add_id(struct perf_header_attr *self, u64 id)
{
	int pos = self->ids;

	self->ids++;
	if (self->ids > self->size) {
		self->size *= 2;
		self->id = realloc(self->id, self->size * sizeof(u64));
		if (!self->id)
			die("nomem");
	}
	self->id[pos] = id;
}

/*
 *
 */

struct perf_header *perf_header__new(void)
{
	struct perf_header *self = malloc(sizeof(*self));

	if (!self)
		die("nomem");

	self->frozen = 0;

	self->attrs = 0;
	self->size = 1;
	self->attr = malloc(sizeof(void *));

	if (!self->attr)
		die("nomem");

	self->data_offset = 0;
	self->data_size = 0;

	return self;
}

void perf_header__add_attr(struct perf_header *self,
			   struct perf_header_attr *attr)
{
	int pos = self->attrs;

	if (self->frozen)
		die("frozen");

	self->attrs++;
	if (self->attrs > self->size) {
		self->size *= 2;
		self->attr = realloc(self->attr, self->size * sizeof(void *));
		if (!self->attr)
			die("nomem");
	}
	self->attr[pos] = attr;
}

static const char *__perf_magic = "PERFFILE";

#define PERF_MAGIC	(*(u64 *)__perf_magic)

struct perf_file_section {
	u64 offset;
	u64 size;
};

struct perf_file_attr {
	struct perf_counter_attr	attr;
	struct perf_file_section	ids;
};

struct perf_file_header {
	u64				magic;
	u64				size;
	u64				attr_size;
	struct perf_file_section	attrs;
	struct perf_file_section	data;
};

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

		if (ret < 0)
			die("failed to write");

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

void perf_header__write(struct perf_header *self, int fd)
{
	struct perf_file_header f_header;
	struct perf_file_attr   f_attr;
	struct perf_header_attr	*attr;
	int i;

	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);
		do_write(fd, attr->id, attr->ids * sizeof(u64));
	}


	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),
			}
		};
		do_write(fd, &f_attr, sizeof(f_attr));
	}


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

	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,
		},
	};

	lseek(fd, 0, SEEK_SET);
	do_write(fd, &f_header, sizeof(f_header));
	lseek(fd, self->data_offset + self->data_size, SEEK_SET);

	self->frozen = 1;
}

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");

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

struct perf_header *perf_header__read(int fd)
{
	struct perf_header	*self = perf_header__new();
	struct perf_file_header f_header;
	struct perf_file_attr	f_attr;
	u64			f_id;

	int nr_attrs, nr_ids, i, j;

	lseek(fd, 0, SEEK_SET);
	do_read(fd, &f_header, sizeof(f_header));

	if (f_header.magic	!= PERF_MAGIC		||
	    f_header.size	!= sizeof(f_header)	||
	    f_header.attr_size	!= sizeof(f_attr))
		die("incompatible file format");

	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;
		off_t tmp = lseek(fd, 0, SEEK_CUR);

		do_read(fd, &f_attr, sizeof(f_attr));

		attr = perf_header_attr__new(&f_attr.attr);

		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));

			perf_header_attr__add_id(attr, f_id);
		}
		perf_header__add_attr(self, attr);
		lseek(fd, tmp, SEEK_SET);
	}

	self->data_offset = f_header.data.offset;
	self->data_size   = f_header.data.size;

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

	self->frozen = 1;

	return self;
}
Loading