Skip to content

Commit

Permalink
perf: Add pipe-specific header read/write and event processing code
Browse files Browse the repository at this point in the history
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: Tom Zanussi <tzanussi@gmail.com>
Acked-by: Thomas 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: Ingo Molnar <mingo@elte.hu>
  • Loading branch information
Tom Zanussi authored and Ingo Molnar committed Apr 14, 2010
1 parent c055564 commit 8dc5810
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 29 deletions.
2 changes: 1 addition & 1 deletion tools/perf/builtin-record.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
4 changes: 4 additions & 0 deletions tools/perf/util/event.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
78 changes: 60 additions & 18 deletions tools/perf/util/header.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down
8 changes: 7 additions & 1 deletion tools/perf/util/header.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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);
Expand Down
135 changes: 126 additions & 9 deletions tools/perf/util/session.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
}
Expand All @@ -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;
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -200,14 +215,17 @@ 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)
Expand Down Expand Up @@ -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,
Expand All @@ -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]);
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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,
self->header.data_size,
self->size, ops);
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;
}
Expand Down
4 changes: 4 additions & 0 deletions tools/perf/util/session.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ struct perf_session {
u64 sample_type;
struct ref_reloc_sym ref_reloc_sym;
int fd;
bool fd_pipe;
int cwdlen;
char *cwd;
char filename[0];
Expand Down Expand Up @@ -92,6 +93,9 @@ static inline struct map *
return map_groups__new_module(&self->kmaps, start, filename);
}

int do_read(int fd, void *buf, size_t size);
void perf_session__update_sample_type(struct perf_session *self);

#ifdef NO_NEWT_SUPPORT
static inline int perf_session__browse_hists(struct rb_root *hists __used,
u64 nr_hists __used,
Expand Down

0 comments on commit 8dc5810

Please sign in to comment.