|
@@ -99,13 +99,6 @@ int perf_header__add_attr(struct perf_header *self,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-#define MAX_EVENT_NAME 64
|
|
|
-
|
|
|
-struct perf_trace_event_type {
|
|
|
- u64 event_id;
|
|
|
- char name[MAX_EVENT_NAME];
|
|
|
-};
|
|
|
-
|
|
|
static int event_count;
|
|
|
static struct perf_trace_event_type *events;
|
|
|
|
|
@@ -427,6 +420,25 @@ out_free:
|
|
|
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 +530,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 +589,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 +659,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;
|
|
@@ -765,3 +800,279 @@ perf_header__find_attr(u64 id, struct perf_header *header)
|
|
|
|
|
|
return NULL;
|
|
|
}
|
|
|
+
|
|
|
+int event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id,
|
|
|
+ event__handler_t process,
|
|
|
+ struct perf_session *session)
|
|
|
+{
|
|
|
+ event_t *ev;
|
|
|
+ size_t size;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ size = sizeof(struct perf_event_attr);
|
|
|
+ size = ALIGN(size, sizeof(u64));
|
|
|
+ size += sizeof(struct perf_event_header);
|
|
|
+ size += ids * sizeof(u64);
|
|
|
+
|
|
|
+ ev = malloc(size);
|
|
|
+
|
|
|
+ ev->attr.attr = *attr;
|
|
|
+ memcpy(ev->attr.id, id, ids * sizeof(u64));
|
|
|
+
|
|
|
+ ev->attr.header.type = PERF_RECORD_HEADER_ATTR;
|
|
|
+ ev->attr.header.size = size;
|
|
|
+
|
|
|
+ err = process(ev, session);
|
|
|
+
|
|
|
+ free(ev);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+int event__synthesize_attrs(struct perf_header *self,
|
|
|
+ event__handler_t process,
|
|
|
+ struct perf_session *session)
|
|
|
+{
|
|
|
+ struct perf_header_attr *attr;
|
|
|
+ int i, err = 0;
|
|
|
+
|
|
|
+ for (i = 0; i < self->attrs; i++) {
|
|
|
+ attr = self->attr[i];
|
|
|
+
|
|
|
+ err = event__synthesize_attr(&attr->attr, attr->ids, attr->id,
|
|
|
+ process, session);
|
|
|
+ if (err) {
|
|
|
+ pr_debug("failed to create perf header attribute\n");
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+int event__process_attr(event_t *self, struct perf_session *session)
|
|
|
+{
|
|
|
+ struct perf_header_attr *attr;
|
|
|
+ unsigned int i, ids, n_ids;
|
|
|
+
|
|
|
+ attr = perf_header_attr__new(&self->attr.attr);
|
|
|
+ if (attr == NULL)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ ids = self->header.size;
|
|
|
+ ids -= (void *)&self->attr.id - (void *)self;
|
|
|
+ n_ids = ids / sizeof(u64);
|
|
|
+
|
|
|
+ for (i = 0; i < n_ids; i++) {
|
|
|
+ if (perf_header_attr__add_id(attr, self->attr.id[i]) < 0) {
|
|
|
+ perf_header_attr__delete(attr);
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (perf_header__add_attr(&session->header, attr) < 0) {
|
|
|
+ perf_header_attr__delete(attr);
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ perf_session__update_sample_type(session);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int event__synthesize_event_type(u64 event_id, char *name,
|
|
|
+ event__handler_t process,
|
|
|
+ struct perf_session *session)
|
|
|
+{
|
|
|
+ event_t ev;
|
|
|
+ size_t size = 0;
|
|
|
+ int err = 0;
|
|
|
+
|
|
|
+ memset(&ev, 0, sizeof(ev));
|
|
|
+
|
|
|
+ ev.event_type.event_type.event_id = event_id;
|
|
|
+ memset(ev.event_type.event_type.name, 0, MAX_EVENT_NAME);
|
|
|
+ strncpy(ev.event_type.event_type.name, name, MAX_EVENT_NAME - 1);
|
|
|
+
|
|
|
+ ev.event_type.header.type = PERF_RECORD_HEADER_EVENT_TYPE;
|
|
|
+ size = strlen(name);
|
|
|
+ size = ALIGN(size, sizeof(u64));
|
|
|
+ ev.event_type.header.size = sizeof(ev.event_type) -
|
|
|
+ (sizeof(ev.event_type.event_type.name) - size);
|
|
|
+
|
|
|
+ err = process(&ev, session);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+int event__synthesize_event_types(event__handler_t process,
|
|
|
+ struct perf_session *session)
|
|
|
+{
|
|
|
+ struct perf_trace_event_type *type;
|
|
|
+ int i, err = 0;
|
|
|
+
|
|
|
+ for (i = 0; i < event_count; i++) {
|
|
|
+ type = &events[i];
|
|
|
+
|
|
|
+ err = event__synthesize_event_type(type->event_id, type->name,
|
|
|
+ process, session);
|
|
|
+ if (err) {
|
|
|
+ pr_debug("failed to create perf header event type\n");
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+int event__process_event_type(event_t *self,
|
|
|
+ struct perf_session *session __unused)
|
|
|
+{
|
|
|
+ if (perf_header__push_event(self->event_type.event_type.event_id,
|
|
|
+ self->event_type.event_type.name) < 0)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int event__synthesize_tracing_data(int fd, struct perf_event_attr *pattrs,
|
|
|
+ int nb_events,
|
|
|
+ event__handler_t process,
|
|
|
+ struct perf_session *session __unused)
|
|
|
+{
|
|
|
+ event_t ev;
|
|
|
+ ssize_t size = 0, aligned_size = 0, padding;
|
|
|
+ int err = 0;
|
|
|
+
|
|
|
+ memset(&ev, 0, sizeof(ev));
|
|
|
+
|
|
|
+ ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA;
|
|
|
+ size = read_tracing_data_size(fd, pattrs, nb_events);
|
|
|
+ if (size <= 0)
|
|
|
+ return size;
|
|
|
+ aligned_size = ALIGN(size, sizeof(u64));
|
|
|
+ padding = aligned_size - size;
|
|
|
+ ev.tracing_data.header.size = sizeof(ev.tracing_data);
|
|
|
+ ev.tracing_data.size = aligned_size;
|
|
|
+
|
|
|
+ process(&ev, session);
|
|
|
+
|
|
|
+ err = read_tracing_data(fd, pattrs, nb_events);
|
|
|
+ write_padded(fd, NULL, 0, padding);
|
|
|
+
|
|
|
+ return aligned_size;
|
|
|
+}
|
|
|
+
|
|
|
+int event__process_tracing_data(event_t *self,
|
|
|
+ struct perf_session *session)
|
|
|
+{
|
|
|
+ ssize_t size_read, padding, size = self->tracing_data.size;
|
|
|
+ off_t offset = lseek(session->fd, 0, SEEK_CUR);
|
|
|
+ char buf[BUFSIZ];
|
|
|
+
|
|
|
+ /* setup for reading amidst mmap */
|
|
|
+ lseek(session->fd, offset + sizeof(struct tracing_data_event),
|
|
|
+ SEEK_SET);
|
|
|
+
|
|
|
+ size_read = trace_report(session->fd);
|
|
|
+
|
|
|
+ padding = ALIGN(size_read, sizeof(u64)) - size_read;
|
|
|
+
|
|
|
+ if (read(session->fd, buf, padding) < 0)
|
|
|
+ die("reading input file");
|
|
|
+
|
|
|
+ if (size_read + padding != size)
|
|
|
+ die("tracing data size mismatch");
|
|
|
+
|
|
|
+ return size_read + padding;
|
|
|
+}
|
|
|
+
|
|
|
+int event__synthesize_build_id(struct dso *pos, u16 misc,
|
|
|
+ event__handler_t process,
|
|
|
+ struct perf_session *session)
|
|
|
+{
|
|
|
+ event_t ev;
|
|
|
+ size_t len;
|
|
|
+ int err = 0;
|
|
|
+
|
|
|
+ if (!pos->hit)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ memset(&ev, 0, sizeof(ev));
|
|
|
+
|
|
|
+ len = pos->long_name_len + 1;
|
|
|
+ len = ALIGN(len, NAME_ALIGN);
|
|
|
+ memcpy(&ev.build_id.build_id, pos->build_id, sizeof(pos->build_id));
|
|
|
+ ev.build_id.header.type = PERF_RECORD_HEADER_BUILD_ID;
|
|
|
+ ev.build_id.header.misc = misc;
|
|
|
+ ev.build_id.header.size = sizeof(ev.build_id) + len;
|
|
|
+ memcpy(&ev.build_id.filename, pos->long_name, pos->long_name_len);
|
|
|
+
|
|
|
+ err = process(&ev, session);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static int __event_synthesize_build_ids(struct list_head *head, u16 misc,
|
|
|
+ event__handler_t process,
|
|
|
+ struct perf_session *session)
|
|
|
+{
|
|
|
+ struct dso *pos;
|
|
|
+
|
|
|
+ dsos__for_each_with_build_id(pos, head) {
|
|
|
+ int err;
|
|
|
+ if (!pos->hit)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ err = event__synthesize_build_id(pos, misc, process, session);
|
|
|
+ if (err < 0)
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int event__synthesize_build_ids(event__handler_t process,
|
|
|
+ struct perf_session *session)
|
|
|
+{
|
|
|
+ int err;
|
|
|
+
|
|
|
+ if (!dsos__read_build_ids(true))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ err = __event_synthesize_build_ids(&dsos__kernel,
|
|
|
+ PERF_RECORD_MISC_KERNEL,
|
|
|
+ process, session);
|
|
|
+ if (err == 0)
|
|
|
+ err = __event_synthesize_build_ids(&dsos__user,
|
|
|
+ PERF_RECORD_MISC_USER,
|
|
|
+ process, session);
|
|
|
+
|
|
|
+ if (err < 0) {
|
|
|
+ pr_debug("failed to synthesize build ids\n");
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ dsos__cache_build_ids();
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int event__process_build_id(event_t *self,
|
|
|
+ struct perf_session *session __unused)
|
|
|
+{
|
|
|
+ struct list_head *head = &dsos__user;
|
|
|
+ struct dso *dso;
|
|
|
+
|
|
|
+ if (self->build_id.header.misc & PERF_RECORD_MISC_KERNEL)
|
|
|
+ head = &dsos__kernel;
|
|
|
+
|
|
|
+ dso = __dsos__findnew(head, self->build_id.filename);
|
|
|
+ if (dso != NULL) {
|
|
|
+ dso__set_build_id(dso, &self->build_id.build_id);
|
|
|
+ if (head == &dsos__kernel && self->build_id.filename[0] == '[')
|
|
|
+ dso->kernel = 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|