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

Commit 8dc58101 authored by Tom Zanussi's avatar Tom Zanussi Committed by Ingo Molnar
Browse files

perf: Add pipe-specific header read/write and event processing code



This patch makes several changes to allow the perf event stream
to be sent and received over a pipe:

- adds pipe-specific versions of the header read/write code

- adds pipe-specific version of the event processing code

- adds a range of event types to be used for header or other
  pseudo events, above the range used by the kernel

- checks the return value of event handlers, which they can use
  to skip over large events during event processing rather than actually
  reading them into event objects.

- unifies the multiple do_read() functions and updates its
  users.

Note that none of these changes affect the existing perf data
file format or processing - this code only comes into play if
perf output is sent to stdout (or is read from stdin).

Signed-off-by: default avatarTom Zanussi <tzanussi@gmail.com>
Acked-by: default avatarThomas Gleixner <tglx@linutronix.de>
Cc: fweisbec@gmail.com
Cc: rostedt@goodmis.org
Cc: k-keiichi@bx.jp.nec.com
Cc: acme@ghostprotocols.net
LKML-Reference: <1270184365-8281-2-git-send-email-tzanussi@gmail.com>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent c0555642
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -487,7 +487,7 @@ static int __cmd_record(int argc, const char **argv)
	}

	if (!file_new) {
		err = perf_header__read(&session->header, output);
		err = perf_header__read(session, output);
		if (err < 0)
			return err;
	}
+4 −0
Original line number Diff line number Diff line
@@ -83,6 +83,10 @@ struct build_id_event {
	char			 filename[];
};

enum perf_header_event_type { /* above any possible kernel type */
	PERF_RECORD_HEADER_MAX			= 64,
};

typedef union event_union {
	struct perf_event_header	header;
	struct ip_event			ip;
+60 −18
Original line number Diff line number Diff line
@@ -427,6 +427,25 @@ static int perf_header__adds_write(struct perf_header *self, int fd)
	return err;
}

int perf_header__write_pipe(int fd)
{
	struct perf_pipe_file_header f_header;
	int err;

	f_header = (struct perf_pipe_file_header){
		.magic	   = PERF_MAGIC,
		.size	   = sizeof(f_header),
	};

	err = do_write(fd, &f_header, sizeof(f_header));
	if (err < 0) {
		pr_debug("failed to write perf pipe header\n");
		return err;
	}

	return 0;
}

int perf_header__write(struct perf_header *self, int fd, bool at_exit)
{
	struct perf_file_header f_header;
@@ -518,25 +537,10 @@ int perf_header__write(struct perf_header *self, int fd, bool at_exit)
	return 0;
}

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

		if (ret <= 0)
			return -1;

		size -= ret;
		buf += ret;
	}

	return 0;
}

static int perf_header__getbuffer64(struct perf_header *self,
				    int fd, void *buf, size_t size)
{
	if (do_read(fd, buf, size))
	if (do_read(fd, buf, size) <= 0)
		return -1;

	if (self->needs_swap)
@@ -592,7 +596,7 @@ int perf_file_header__read(struct perf_file_header *self,
{
	lseek(fd, 0, SEEK_SET);

	if (do_read(fd, self, sizeof(*self)) ||
	if (do_read(fd, self, sizeof(*self)) <= 0 ||
	    memcmp(&self->magic, __perf_magic, sizeof(self->magic)))
		return -1;

@@ -662,13 +666,51 @@ static int perf_file_section__process(struct perf_file_section *self,
	return 0;
}

int perf_header__read(struct perf_header *self, int fd)
static int perf_file_header__read_pipe(struct perf_pipe_file_header *self,
				       struct perf_header *ph, int fd)
{
	if (do_read(fd, self, sizeof(*self)) <= 0 ||
	    memcmp(&self->magic, __perf_magic, sizeof(self->magic)))
		return -1;

	if (self->size != sizeof(*self)) {
		u64 size = bswap_64(self->size);

		if (size != sizeof(*self))
			return -1;

		ph->needs_swap = true;
	}

	return 0;
}

static int perf_header__read_pipe(struct perf_session *session, int fd)
{
	struct perf_header *self = &session->header;
	struct perf_pipe_file_header f_header;

	if (perf_file_header__read_pipe(&f_header, self, fd) < 0) {
		pr_debug("incompatible file format\n");
		return -EINVAL;
	}

	session->fd = fd;

	return 0;
}

int perf_header__read(struct perf_session *session, int fd)
{
	struct perf_header *self = &session->header;
	struct perf_file_header	f_header;
	struct perf_file_attr	f_attr;
	u64			f_id;
	int nr_attrs, nr_ids, i, j;

	if (session->fd_pipe)
		return perf_header__read_pipe(session, fd);

	if (perf_file_header__read(&f_header, self, fd) < 0) {
		pr_debug("incompatible file format\n");
		return -EINVAL;
+7 −1
Original line number Diff line number Diff line
@@ -39,6 +39,11 @@ struct perf_file_header {
	DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS);
};

struct perf_pipe_file_header {
	u64				magic;
	u64				size;
};

struct perf_header;

int perf_file_header__read(struct perf_file_header *self,
@@ -60,8 +65,9 @@ struct perf_header {
int perf_header__init(struct perf_header *self);
void perf_header__exit(struct perf_header *self);

int perf_header__read(struct perf_header *self, int fd);
int perf_header__read(struct perf_session *session, int fd);
int perf_header__write(struct perf_header *self, int fd, bool at_exit);
int perf_header__write_pipe(int fd);

int perf_header__add_attr(struct perf_header *self,
			  struct perf_header_attr *attr);
+126 −9
Original line number Diff line number Diff line
@@ -14,6 +14,16 @@ static int perf_session__open(struct perf_session *self, bool force)
{
	struct stat input_stat;

	if (!strcmp(self->filename, "-")) {
		self->fd_pipe = true;
		self->fd = STDIN_FILENO;

		if (perf_header__read(self, self->fd) < 0)
			pr_err("incompatible file format");

		return 0;
	}

	self->fd = open(self->filename, O_RDONLY);
	if (self->fd < 0) {
		pr_err("failed to open file: %s", self->filename);
@@ -38,7 +48,7 @@ static int perf_session__open(struct perf_session *self, bool force)
		goto out_close;
	}

	if (perf_header__read(&self->header, self->fd) < 0) {
	if (perf_header__read(self, self->fd) < 0) {
		pr_err("incompatible file format");
		goto out_close;
	}
@@ -52,6 +62,11 @@ static int perf_session__open(struct perf_session *self, bool force)
	return -1;
}

void perf_session__update_sample_type(struct perf_session *self)
{
	self->sample_type = perf_header__sample_type(&self->header);
}

struct perf_session *perf_session__new(const char *filename, int mode, bool force)
{
	size_t len = filename ? strlen(filename) + 1 : 0;
@@ -85,7 +100,7 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc
			goto out_delete;
	}

	self->sample_type = perf_header__sample_type(&self->header);
	perf_session__update_sample_type(self);
out:
	return self;
out_free:
@@ -200,15 +215,18 @@ static const char *event__name[] = {
	[PERF_RECORD_SAMPLE]	 = "SAMPLE",
};

unsigned long event__total[PERF_RECORD_MAX];
unsigned long event__total[PERF_RECORD_HEADER_MAX];

void event__print_totals(void)
{
	int i;
	for (i = 0; i < PERF_RECORD_MAX; ++i)
	for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) {
		if (!event__name[i])
			continue;
		pr_info("%10s events: %10ld\n",
			event__name[i], event__total[i]);
	}
}

void mem_bswap_64(void *src, int byte_size)
{
@@ -271,7 +289,7 @@ static event__swap_op event__swap_ops[] = {
	[PERF_RECORD_LOST]   = event__all64_swap,
	[PERF_RECORD_READ]   = event__read_swap,
	[PERF_RECORD_SAMPLE] = event__all64_swap,
	[PERF_RECORD_MAX]    = NULL,
	[PERF_RECORD_HEADER_MAX]    = NULL,
};

static int perf_session__process_event(struct perf_session *self,
@@ -281,7 +299,7 @@ static int perf_session__process_event(struct perf_session *self,
{
	trace_event(event);

	if (event->header.type < PERF_RECORD_MAX) {
	if (event->header.type < PERF_RECORD_HEADER_MAX) {
		dump_printf("%#Lx [%#x]: PERF_RECORD_%s",
			    offset + head, event->header.size,
			    event__name[event->header.type]);
@@ -376,6 +394,101 @@ static struct thread *perf_session__register_idle_thread(struct perf_session *se
	return thread;
}

int do_read(int fd, void *buf, size_t size)
{
	void *buf_start = buf;

	while (size) {
		int ret = read(fd, buf, size);

		if (ret <= 0)
			return ret;

		size -= ret;
		buf += ret;
	}

	return buf - buf_start;
}

#define session_done()	(*(volatile int *)(&session_done))
volatile int session_done;

static int __perf_session__process_pipe_events(struct perf_session *self,
					       struct perf_event_ops *ops)
{
	event_t event;
	uint32_t size;
	int skip = 0;
	u64 head;
	int err;
	void *p;

	perf_event_ops__fill_defaults(ops);

	head = 0;
more:
	err = do_read(self->fd, &event, sizeof(struct perf_event_header));
	if (err <= 0) {
		if (err == 0)
			goto done;

		pr_err("failed to read event header\n");
		goto out_err;
	}

	if (self->header.needs_swap)
		perf_event_header__bswap(&event.header);

	size = event.header.size;
	if (size == 0)
		size = 8;

	p = &event;
	p += sizeof(struct perf_event_header);

	err = do_read(self->fd, p, size - sizeof(struct perf_event_header));
	if (err <= 0) {
		if (err == 0) {
			pr_err("unexpected end of event stream\n");
			goto done;
		}

		pr_err("failed to read event data\n");
		goto out_err;
	}

	if (size == 0 ||
	    (skip = perf_session__process_event(self, &event, ops,
						0, head)) < 0) {
		dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n",
			    head, event.header.size, event.header.type);
		/*
		 * assume we lost track of the stream, check alignment, and
		 * increment a single u64 in the hope to catch on again 'soon'.
		 */
		if (unlikely(head & 7))
			head &= ~7ULL;

		size = 8;
	}

	head += size;

	dump_printf("\n%#Lx [%#x]: event: %d\n",
		    head, event.header.size, event.header.type);

	if (skip > 0)
		head += skip;

	if (!session_done())
		goto more;
done:
	err = 0;
out_err:
	return err;
}

int __perf_session__process_events(struct perf_session *self,
				   u64 data_offset, u64 data_size,
				   u64 file_size, struct perf_event_ops *ops)
@@ -499,9 +612,13 @@ int perf_session__process_events(struct perf_session *self,
		self->cwdlen = strlen(self->cwd);
	}

	err = __perf_session__process_events(self, self->header.data_offset,
	if (!self->fd_pipe)
		err = __perf_session__process_events(self,
						     self->header.data_offset,
						     self->header.data_size,
						     self->size, ops);
	else
		err = __perf_session__process_pipe_events(self, ops);
out_err:
	return err;
}
Loading