diff options
Diffstat (limited to 'tools/perf/util/header.c')
| -rw-r--r-- | tools/perf/util/header.c | 489 | 
1 files changed, 447 insertions, 42 deletions
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 6c9aa16ee51..8847bec64c5 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -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; @@ -197,7 +190,8 @@ static int write_padded(int fd, const void *bf, size_t count,  			continue;		\  		else -static int __dsos__write_buildid_table(struct list_head *head, u16 misc, int fd) +static int __dsos__write_buildid_table(struct list_head *head, pid_t pid, +				u16 misc, int fd)  {  	struct dso *pos; @@ -212,6 +206,7 @@ static int __dsos__write_buildid_table(struct list_head *head, u16 misc, int fd)  		len = ALIGN(len, NAME_ALIGN);  		memset(&b, 0, sizeof(b));  		memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id)); +		b.pid = pid;  		b.header.misc = misc;  		b.header.size = sizeof(b) + len;  		err = do_write(fd, &b, sizeof(b)); @@ -226,13 +221,32 @@ static int __dsos__write_buildid_table(struct list_head *head, u16 misc, int fd)  	return 0;  } -static int dsos__write_buildid_table(int fd) +static int dsos__write_buildid_table(struct perf_header *header, int fd)  { -	int err = __dsos__write_buildid_table(&dsos__kernel, -					      PERF_RECORD_MISC_KERNEL, fd); -	if (err == 0) -		err = __dsos__write_buildid_table(&dsos__user, -						  PERF_RECORD_MISC_USER, fd); +	struct perf_session *session = container_of(header, +			struct perf_session, header); +	struct rb_node *nd; +	int err = 0; +	u16 kmisc, umisc; + +	for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) { +		struct machine *pos = rb_entry(nd, struct machine, rb_node); +		if (machine__is_host(pos)) { +			kmisc = PERF_RECORD_MISC_KERNEL; +			umisc = PERF_RECORD_MISC_USER; +		} else { +			kmisc = PERF_RECORD_MISC_GUEST_KERNEL; +			umisc = PERF_RECORD_MISC_GUEST_USER; +		} + +		err = __dsos__write_buildid_table(&pos->kernel_dsos, pos->pid, +						  kmisc, fd); +		if (err == 0) +			err = __dsos__write_buildid_table(&pos->user_dsos, +							  pos->pid, umisc, fd); +		if (err) +			break; +	}  	return err;  } @@ -349,9 +363,12 @@ static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir)  	return err;  } -static int dsos__cache_build_ids(void) +static int dsos__cache_build_ids(struct perf_header *self)  { -	int err_kernel, err_user; +	struct perf_session *session = container_of(self, +			struct perf_session, header); +	struct rb_node *nd; +	int ret = 0;  	char debugdir[PATH_MAX];  	snprintf(debugdir, sizeof(debugdir), "%s/%s", getenv("HOME"), @@ -360,9 +377,28 @@ static int dsos__cache_build_ids(void)  	if (mkdir(debugdir, 0755) != 0 && errno != EEXIST)  		return -1; -	err_kernel = __dsos__cache_build_ids(&dsos__kernel, debugdir); -	err_user   = __dsos__cache_build_ids(&dsos__user, debugdir); -	return err_kernel || err_user ? -1 : 0; +	for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) { +		struct machine *pos = rb_entry(nd, struct machine, rb_node); +		ret |= __dsos__cache_build_ids(&pos->kernel_dsos, debugdir); +		ret |= __dsos__cache_build_ids(&pos->user_dsos, debugdir); +	} +	return ret ? -1 : 0; +} + +static bool dsos__read_build_ids(struct perf_header *self, bool with_hits) +{ +	bool ret = false; +	struct perf_session *session = container_of(self, +			struct perf_session, header); +	struct rb_node *nd; + +	for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) { +		struct machine *pos = rb_entry(nd, struct machine, rb_node); +		ret |= __dsos__read_build_ids(&pos->kernel_dsos, with_hits); +		ret |= __dsos__read_build_ids(&pos->user_dsos, with_hits); +	} + +	return ret;  }  static int perf_header__adds_write(struct perf_header *self, int fd) @@ -373,7 +409,7 @@ static int perf_header__adds_write(struct perf_header *self, int fd)  	u64 sec_start;  	int idx = 0, err; -	if (dsos__read_build_ids(true)) +	if (dsos__read_build_ids(self, true))  		perf_header__set_feat(self, HEADER_BUILD_ID);  	nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS); @@ -400,7 +436,6 @@ static int perf_header__adds_write(struct perf_header *self, int fd)  		trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset;  	} -  	if (perf_header__has_feat(self, HEADER_BUILD_ID)) {  		struct perf_file_section *buildid_sec; @@ -408,14 +443,14 @@ static int perf_header__adds_write(struct perf_header *self, int fd)  		/* Write build-ids */  		buildid_sec->offset = lseek(fd, 0, SEEK_CUR); -		err = dsos__write_buildid_table(fd); +		err = dsos__write_buildid_table(self, fd);  		if (err < 0) {  			pr_debug("failed to write buildid table\n");  			goto out_free;  		}  		buildid_sec->size = lseek(fd, 0, SEEK_CUR) -  					  buildid_sec->offset; -		dsos__cache_build_ids(); +		dsos__cache_build_ids(self);  	}  	lseek(fd, sec_start, SEEK_SET); @@ -427,6 +462,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 +572,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 +631,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; @@ -636,6 +675,93 @@ int perf_file_header__read(struct perf_file_header *self,  	return 0;  } +static int __event_process_build_id(struct build_id_event *bev, +				    char *filename, +				    struct perf_session *session) +{ +	int err = -1; +	struct list_head *head; +	struct machine *machine; +	u16 misc; +	struct dso *dso; +	enum dso_kernel_type dso_type; + +	machine = perf_session__findnew_machine(session, bev->pid); +	if (!machine) +		goto out; + +	misc = bev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; + +	switch (misc) { +	case PERF_RECORD_MISC_KERNEL: +		dso_type = DSO_TYPE_KERNEL; +		head = &machine->kernel_dsos; +		break; +	case PERF_RECORD_MISC_GUEST_KERNEL: +		dso_type = DSO_TYPE_GUEST_KERNEL; +		head = &machine->kernel_dsos; +		break; +	case PERF_RECORD_MISC_USER: +	case PERF_RECORD_MISC_GUEST_USER: +		dso_type = DSO_TYPE_USER; +		head = &machine->user_dsos; +		break; +	default: +		goto out; +	} + +	dso = __dsos__findnew(head, filename); +	if (dso != NULL) { +		char sbuild_id[BUILD_ID_SIZE * 2 + 1]; + +		dso__set_build_id(dso, &bev->build_id); + +		if (filename[0] == '[') +			dso->kernel = dso_type; + +		build_id__sprintf(dso->build_id, sizeof(dso->build_id), +				  sbuild_id); +		pr_debug("build id event received for %s: %s\n", +			 dso->long_name, sbuild_id); +	} + +	err = 0; +out: +	return err; +} + +static int perf_header__read_build_ids(struct perf_header *self, +			int input, u64 offset, u64 size) +{ +	struct perf_session *session = container_of(self, +			struct perf_session, header); +	struct build_id_event bev; +	char filename[PATH_MAX]; +	u64 limit = offset + size; +	int err = -1; + +	while (offset < limit) { +		ssize_t len; + +		if (read(input, &bev, sizeof(bev)) != sizeof(bev)) +			goto out; + +		if (self->needs_swap) +			perf_event_header__bswap(&bev.header); + +		len = bev.header.size - sizeof(bev); +		if (read(input, filename, len) != len) +			goto out; + +		__event_process_build_id(&bev, filename, session); + +		offset += bev.header.size; +	} +	err = 0; +out: +	return err; +} +  static int perf_file_section__process(struct perf_file_section *self,  				      struct perf_header *ph,  				      int feat, int fd) @@ -648,7 +774,7 @@ static int perf_file_section__process(struct perf_file_section *self,  	switch (feat) {  	case HEADER_TRACE_INFO: -		trace_report(fd); +		trace_report(fd, false);  		break;  	case HEADER_BUILD_ID: @@ -662,13 +788,56 @@ 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, +				       bool repipe)  { +	if (do_read(fd, self, sizeof(*self)) <= 0 || +	    memcmp(&self->magic, __perf_magic, sizeof(self->magic))) +		return -1; + +	if (repipe && do_write(STDOUT_FILENO, self, sizeof(*self)) < 0) +		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, +					session->repipe) < 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; @@ -753,6 +922,14 @@ perf_header__find_attr(u64 id, struct perf_header *header)  {  	int i; +	/* +	 * We set id to -1 if the data file doesn't contain sample +	 * ids. Check for this and avoid walking through the entire +	 * list of ids which may be large. +	 */ +	if (id == -1ULL) +		return NULL; +  	for (i = 0; i < header->attrs; i++) {  		struct perf_header_attr *attr = header->attr[i];  		int j; @@ -765,3 +942,231 @@ 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, session->repipe); + +	padding = ALIGN(size_read, sizeof(u64)) - size_read; + +	if (read(session->fd, buf, padding) < 0) +		die("reading input file"); +	if (session->repipe) { +		int retw = write(STDOUT_FILENO, buf, padding); +		if (retw <= 0 || retw != padding) +			die("repiping tracing data padding"); +	} + +	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 machine *machine, +			       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.pid = machine->pid; +	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; +} + +int event__process_build_id(event_t *self, +			    struct perf_session *session) +{ +	__event_process_build_id(&self->build_id, +				 self->build_id.filename, +				 session); +	return 0; +}  |