diff options
Diffstat (limited to 'tools/perf/util/header.c')
| -rw-r--r-- | tools/perf/util/header.c | 3092 | 
1 files changed, 2482 insertions, 610 deletions
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index d7e67b167ea..893f8e2df92 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -1,5 +1,4 @@ -#define _FILE_OFFSET_BITS 64 - +#include "util.h"  #include <sys/types.h>  #include <byteswap.h>  #include <unistd.h> @@ -7,183 +6,169 @@  #include <stdlib.h>  #include <linux/list.h>  #include <linux/kernel.h> +#include <linux/bitops.h> +#include <sys/utsname.h> -#include "util.h" +#include "evlist.h" +#include "evsel.h"  #include "header.h"  #include "../perf.h"  #include "trace-event.h"  #include "session.h"  #include "symbol.h"  #include "debug.h" +#include "cpumap.h" +#include "pmu.h" +#include "vdso.h" +#include "strbuf.h" +#include "build-id.h" +#include "data.h"  static bool no_buildid_cache = false; +static u32 header_argc; +static const char **header_argv; +  /* - * Create new perf.data header attribute: + * magic2 = "PERFILE2" + * must be a numerical value to let the endianness + * determine the memory layout. That way we are able + * to detect endianness when reading the perf.data file + * back. + * + * we check for legacy (PERFFILE) format.   */ -struct perf_header_attr *perf_header_attr__new(struct perf_event_attr *attr) -{ -	struct perf_header_attr *self = malloc(sizeof(*self)); - -	if (self != NULL) { -		self->attr = *attr; -		self->ids  = 0; -		self->size = 1; -		self->id   = malloc(sizeof(u64)); -		if (self->id == NULL) { -			free(self); -			self = NULL; -		} -	} +static const char *__perf_magic1 = "PERFFILE"; +static const u64 __perf_magic2    = 0x32454c4946524550ULL; +static const u64 __perf_magic2_sw = 0x50455246494c4532ULL; -	return self; -} +#define PERF_MAGIC	__perf_magic2 -void perf_header_attr__delete(struct perf_header_attr *self) -{ -	free(self->id); -	free(self); -} +struct perf_file_attr { +	struct perf_event_attr	attr; +	struct perf_file_section	ids; +}; -int perf_header_attr__add_id(struct perf_header_attr *self, u64 id) +void perf_header__set_feat(struct perf_header *header, int feat)  { -	int pos = self->ids; - -	self->ids++; -	if (self->ids > self->size) { -		int nsize = self->size * 2; -		u64 *nid = realloc(self->id, nsize * sizeof(u64)); - -		if (nid == NULL) -			return -1; - -		self->size = nsize; -		self->id = nid; -	} -	self->id[pos] = id; -	return 0; +	set_bit(feat, header->adds_features);  } -int perf_header__init(struct perf_header *self) +void perf_header__clear_feat(struct perf_header *header, int feat)  { -	self->size = 1; -	self->attr = malloc(sizeof(void *)); -	return self->attr == NULL ? -ENOMEM : 0; +	clear_bit(feat, header->adds_features);  } -void perf_header__exit(struct perf_header *self) +bool perf_header__has_feat(const struct perf_header *header, int feat)  { -	int i; -	for (i = 0; i < self->attrs; ++i) -                perf_header_attr__delete(self->attr[i]); -	free(self->attr); +	return test_bit(feat, header->adds_features);  } -int perf_header__add_attr(struct perf_header *self, -			  struct perf_header_attr *attr) +static int do_write(int fd, const void *buf, size_t size)  { -	if (self->frozen) -		return -1; - -	if (self->attrs == self->size) { -		int nsize = self->size * 2; -		struct perf_header_attr **nattr; +	while (size) { +		int ret = write(fd, buf, size); -		nattr = realloc(self->attr, nsize * sizeof(void *)); -		if (nattr == NULL) -			return -1; +		if (ret < 0) +			return -errno; -		self->size = nsize; -		self->attr = nattr; +		size -= ret; +		buf += ret;  	} -	self->attr[self->attrs++] = attr;  	return 0;  } -static int event_count; -static struct perf_trace_event_type *events; +#define NAME_ALIGN 64 -int perf_header__push_event(u64 id, const char *name) +static int write_padded(int fd, const void *bf, size_t count, +			size_t count_aligned)  { -	if (strlen(name) > MAX_EVENT_NAME) -		pr_warning("Event %s will be truncated\n", name); +	static const char zero_buf[NAME_ALIGN]; +	int err = do_write(fd, bf, count); -	if (!events) { -		events = malloc(sizeof(struct perf_trace_event_type)); -		if (events == NULL) -			return -ENOMEM; -	} else { -		struct perf_trace_event_type *nevents; +	if (!err) +		err = do_write(fd, zero_buf, count_aligned - count); -		nevents = realloc(events, (event_count + 1) * sizeof(*events)); -		if (nevents == NULL) -			return -ENOMEM; -		events = nevents; -	} -	memset(&events[event_count], 0, sizeof(struct perf_trace_event_type)); -	events[event_count].event_id = id; -	strncpy(events[event_count].name, name, MAX_EVENT_NAME - 1); -	event_count++; -	return 0; +	return err;  } -char *perf_header__find_event(u64 id) +static int do_write_string(int fd, const char *str)  { -	int i; -	for (i = 0 ; i < event_count; i++) { -		if (events[i].event_id == id) -			return events[i].name; -	} -	return NULL; -} - -static const char *__perf_magic = "PERFFILE"; +	u32 len, olen; +	int ret; -#define PERF_MAGIC	(*(u64 *)__perf_magic) +	olen = strlen(str) + 1; +	len = PERF_ALIGN(olen, NAME_ALIGN); -struct perf_file_attr { -	struct perf_event_attr	attr; -	struct perf_file_section	ids; -}; +	/* write len, incl. \0 */ +	ret = do_write(fd, &len, sizeof(len)); +	if (ret < 0) +		return ret; -void perf_header__set_feat(struct perf_header *self, int feat) -{ -	set_bit(feat, self->adds_features); +	return write_padded(fd, str, olen, len);  } -bool perf_header__has_feat(const struct perf_header *self, int feat) +static char *do_read_string(int fd, struct perf_header *ph)  { -	return test_bit(feat, self->adds_features); -} +	ssize_t sz, ret; +	u32 len; +	char *buf; -static int do_write(int fd, const void *buf, size_t size) -{ -	while (size) { -		int ret = write(fd, buf, size); +	sz = readn(fd, &len, sizeof(len)); +	if (sz < (ssize_t)sizeof(len)) +		return NULL; -		if (ret < 0) -			return -errno; +	if (ph->needs_swap) +		len = bswap_32(len); -		size -= ret; -		buf += ret; +	buf = malloc(len); +	if (!buf) +		return NULL; + +	ret = readn(fd, buf, len); +	if (ret == (ssize_t)len) { +		/* +		 * strings are padded by zeroes +		 * thus the actual strlen of buf +		 * may be less than len +		 */ +		return buf;  	} -	return 0; +	free(buf); +	return NULL;  } -#define NAME_ALIGN 64 - -static int write_padded(int fd, const void *bf, size_t count, -			size_t count_aligned) +int +perf_header__set_cmdline(int argc, const char **argv)  { -	static const char zero_buf[NAME_ALIGN]; -	int err = do_write(fd, bf, count); +	int i; -	if (!err) -		err = do_write(fd, zero_buf, count_aligned - count); +	/* +	 * If header_argv has already been set, do not override it. +	 * This allows a command to set the cmdline, parse args and +	 * then call another builtin function that implements a +	 * command -- e.g, cmd_kvm calling cmd_record. +	 */ +	if (header_argv) +		return 0; -	return err; +	header_argc = (u32)argc; + +	/* do not include NULL termination */ +	header_argv = calloc(argc, sizeof(char *)); +	if (!header_argv) +		return -ENOMEM; + +	/* +	 * must copy argv contents because it gets moved +	 * around during option parsing +	 */ +	for (i = 0; i < argc ; i++) +		header_argv[i] = argv[i]; + +	return 0;  }  #define dsos__for_each_with_build_id(pos, head)	\ @@ -192,53 +177,81 @@ static int write_padded(int fd, const void *bf, size_t count,  			continue;		\  		else -static int __dsos__write_buildid_table(struct list_head *head, pid_t pid, -				u16 misc, int fd) +static int write_buildid(const char *name, size_t name_len, u8 *build_id, +			 pid_t pid, u16 misc, int fd)  { +	int err; +	struct build_id_event b; +	size_t len; + +	len = name_len + 1; +	len = PERF_ALIGN(len, NAME_ALIGN); + +	memset(&b, 0, sizeof(b)); +	memcpy(&b.build_id, build_id, BUILD_ID_SIZE); +	b.pid = pid; +	b.header.misc = misc; +	b.header.size = sizeof(b) + len; + +	err = do_write(fd, &b, sizeof(b)); +	if (err < 0) +		return err; + +	return write_padded(fd, name, name_len + 1, len); +} + +static int __dsos__write_buildid_table(struct list_head *head, +				       struct machine *machine, +				       pid_t pid, u16 misc, int fd) +{ +	char nm[PATH_MAX];  	struct dso *pos;  	dsos__for_each_with_build_id(pos, head) {  		int err; -		struct build_id_event b; -		size_t len; +		const char *name; +		size_t name_len;  		if (!pos->hit)  			continue; -		len = pos->long_name_len + 1; -		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)); -		if (err < 0) -			return err; -		err = write_padded(fd, pos->long_name, -				   pos->long_name_len + 1, len); -		if (err < 0) + +		if (is_vdso_map(pos->short_name)) { +			name = (char *) VDSO__MAP_NAME; +			name_len = sizeof(VDSO__MAP_NAME) + 1; +		} else if (dso__is_kcore(pos)) { +			machine__mmap_name(machine, nm, sizeof(nm)); +			name = nm; +			name_len = strlen(nm) + 1; +		} else { +			name = pos->long_name; +			name_len = pos->long_name_len + 1; +		} + +		err = write_buildid(name, name_len, pos->build_id, +				    pid, misc, fd); +		if (err)  			return err;  	}  	return 0;  } -static int machine__write_buildid_table(struct machine *self, int fd) +static int machine__write_buildid_table(struct machine *machine, int fd)  {  	int err;  	u16 kmisc = PERF_RECORD_MISC_KERNEL,  	    umisc = PERF_RECORD_MISC_USER; -	if (!machine__is_host(self)) { +	if (!machine__is_host(machine)) {  		kmisc = PERF_RECORD_MISC_GUEST_KERNEL;  		umisc = PERF_RECORD_MISC_GUEST_USER;  	} -	err = __dsos__write_buildid_table(&self->kernel_dsos, self->pid, -					  kmisc, fd); +	err = __dsos__write_buildid_table(&machine->kernel_dsos, machine, +					  machine->pid, kmisc, fd);  	if (err == 0) -		err = __dsos__write_buildid_table(&self->user_dsos, -						  self->pid, umisc, fd); +		err = __dsos__write_buildid_table(&machine->user_dsos, machine, +						  machine->pid, umisc, fd);  	return err;  } @@ -247,12 +260,12 @@ static int dsos__write_buildid_table(struct perf_header *header, int fd)  	struct perf_session *session = container_of(header,  			struct perf_session, header);  	struct rb_node *nd; -	int err = machine__write_buildid_table(&session->host_machine, fd); +	int err = machine__write_buildid_table(&session->machines.host, fd);  	if (err)  		return err; -	for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) { +	for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {  		struct machine *pos = rb_entry(nd, struct machine, rb_node);  		err = machine__write_buildid_table(pos, fd);  		if (err) @@ -262,32 +275,44 @@ static int dsos__write_buildid_table(struct perf_header *header, int fd)  }  int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, -			  const char *name, bool is_kallsyms) +			  const char *name, bool is_kallsyms, bool is_vdso)  {  	const size_t size = PATH_MAX; -	char *filename = malloc(size), -	     *linkname = malloc(size), *targetname; +	char *realname, *filename = zalloc(size), +	     *linkname = zalloc(size), *targetname;  	int len, err = -1; +	bool slash = is_kallsyms || is_vdso; -	if (filename == NULL || linkname == NULL) +	if (is_kallsyms) { +		if (symbol_conf.kptr_restrict) { +			pr_debug("Not caching a kptr_restrict'ed /proc/kallsyms\n"); +			err = 0; +			goto out_free; +		} +		realname = (char *) name; +	} else +		realname = realpath(name, NULL); + +	if (realname == NULL || filename == NULL || linkname == NULL)  		goto out_free; -	len = snprintf(filename, size, "%s%s%s", -		       debugdir, is_kallsyms ? "/" : "", name); +	len = scnprintf(filename, size, "%s%s%s", +		       debugdir, slash ? "/" : "", +		       is_vdso ? VDSO__MAP_NAME : realname);  	if (mkdir_p(filename, 0755))  		goto out_free; -	snprintf(filename + len, sizeof(filename) - len, "/%s", sbuild_id); +	snprintf(filename + len, size - len, "/%s", sbuild_id);  	if (access(filename, F_OK)) {  		if (is_kallsyms) {  			 if (copyfile("/proc/kallsyms", filename))  				goto out_free; -		} else if (link(name, filename) && copyfile(name, filename)) +		} else if (link(realname, filename) && copyfile(name, filename))  			goto out_free;  	} -	len = snprintf(linkname, size, "%s/.build-id/%.2s", +	len = scnprintf(linkname, size, "%s/.build-id/%.2s",  		       debugdir, sbuild_id);  	if (access(linkname, X_OK) && mkdir_p(linkname, 0755)) @@ -300,6 +325,8 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,  	if (symlink(targetname, linkname) == 0)  		err = 0;  out_free: +	if (!is_kallsyms) +		free(realname);  	free(filename);  	free(linkname);  	return err; @@ -307,20 +334,21 @@ out_free:  static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size,  				 const char *name, const char *debugdir, -				 bool is_kallsyms) +				 bool is_kallsyms, bool is_vdso)  {  	char sbuild_id[BUILD_ID_SIZE * 2 + 1];  	build_id__sprintf(build_id, build_id_size, sbuild_id); -	return build_id_cache__add_s(sbuild_id, debugdir, name, is_kallsyms); +	return build_id_cache__add_s(sbuild_id, debugdir, name, +				     is_kallsyms, is_vdso);  }  int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir)  {  	const size_t size = PATH_MAX; -	char *filename = malloc(size), -	     *linkname = malloc(size); +	char *filename = zalloc(size), +	     *linkname = zalloc(size);  	int err = -1;  	if (filename == NULL || linkname == NULL) @@ -332,7 +360,7 @@ int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir)  	if (access(linkname, F_OK))  		goto out_free; -	if (readlink(linkname, filename, size) < 0) +	if (readlink(linkname, filename, size - 1) < 0)  		goto out_free;  	if (unlink(linkname)) @@ -354,34 +382,45 @@ out_free:  	return err;  } -static int dso__cache_build_id(struct dso *self, const char *debugdir) +static int dso__cache_build_id(struct dso *dso, struct machine *machine, +			       const char *debugdir)  { -	bool is_kallsyms = self->kernel && self->long_name[0] != '/'; - -	return build_id_cache__add_b(self->build_id, sizeof(self->build_id), -				     self->long_name, debugdir, is_kallsyms); +	bool is_kallsyms = dso->kernel && dso->long_name[0] != '/'; +	bool is_vdso = is_vdso_map(dso->short_name); +	const char *name = dso->long_name; +	char nm[PATH_MAX]; + +	if (dso__is_kcore(dso)) { +		is_kallsyms = true; +		machine__mmap_name(machine, nm, sizeof(nm)); +		name = nm; +	} +	return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id), name, +				     debugdir, is_kallsyms, is_vdso);  } -static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir) +static int __dsos__cache_build_ids(struct list_head *head, +				   struct machine *machine, const char *debugdir)  {  	struct dso *pos;  	int err = 0;  	dsos__for_each_with_build_id(pos, head) -		if (dso__cache_build_id(pos, debugdir)) +		if (dso__cache_build_id(pos, machine, debugdir))  			err = -1;  	return err;  } -static int machine__cache_build_ids(struct machine *self, const char *debugdir) +static int machine__cache_build_ids(struct machine *machine, const char *debugdir)  { -	int ret = __dsos__cache_build_ids(&self->kernel_dsos, debugdir); -	ret |= __dsos__cache_build_ids(&self->user_dsos, debugdir); +	int ret = __dsos__cache_build_ids(&machine->kernel_dsos, machine, +					  debugdir); +	ret |= __dsos__cache_build_ids(&machine->user_dsos, machine, debugdir);  	return ret;  } -static int perf_session__cache_build_ids(struct perf_session *self) +static int perf_session__cache_build_ids(struct perf_session *session)  {  	struct rb_node *nd;  	int ret; @@ -392,28 +431,28 @@ static int perf_session__cache_build_ids(struct perf_session *self)  	if (mkdir(debugdir, 0755) != 0 && errno != EEXIST)  		return -1; -	ret = machine__cache_build_ids(&self->host_machine, debugdir); +	ret = machine__cache_build_ids(&session->machines.host, debugdir); -	for (nd = rb_first(&self->machines); nd; nd = rb_next(nd)) { +	for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {  		struct machine *pos = rb_entry(nd, struct machine, rb_node);  		ret |= machine__cache_build_ids(pos, debugdir);  	}  	return ret ? -1 : 0;  } -static bool machine__read_build_ids(struct machine *self, bool with_hits) +static bool machine__read_build_ids(struct machine *machine, bool with_hits)  { -	bool ret = __dsos__read_build_ids(&self->kernel_dsos, with_hits); -	ret |= __dsos__read_build_ids(&self->user_dsos, with_hits); +	bool ret = __dsos__read_build_ids(&machine->kernel_dsos, with_hits); +	ret |= __dsos__read_build_ids(&machine->user_dsos, with_hits);  	return ret;  } -static bool perf_session__read_build_ids(struct perf_session *self, bool with_hits) +static bool perf_session__read_build_ids(struct perf_session *session, bool with_hits)  {  	struct rb_node *nd; -	bool ret = machine__read_build_ids(&self->host_machine, with_hits); +	bool ret = machine__read_build_ids(&session->machines.host, with_hits); -	for (nd = rb_first(&self->machines); nd; nd = rb_next(nd)) { +	for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {  		struct machine *pos = rb_entry(nd, struct machine, rb_node);  		ret |= machine__read_build_ids(pos, with_hits);  	} @@ -421,280 +460,1046 @@ static bool perf_session__read_build_ids(struct perf_session *self, bool with_hi  	return ret;  } -static int perf_header__adds_write(struct perf_header *self, int fd) +static int write_tracing_data(int fd, struct perf_header *h __maybe_unused, +			    struct perf_evlist *evlist) +{ +	return read_tracing_data(fd, &evlist->entries); +} + + +static int write_build_id(int fd, struct perf_header *h, +			  struct perf_evlist *evlist __maybe_unused)  { -	int nr_sections;  	struct perf_session *session; -	struct perf_file_section *feat_sec; -	int sec_size; -	u64 sec_start; -	int idx = 0, err; +	int err; -	session = container_of(self, struct perf_session, header); -	if (perf_session__read_build_ids(session, true)) -		perf_header__set_feat(self, HEADER_BUILD_ID); +	session = container_of(h, struct perf_session, header); -	nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS); -	if (!nr_sections) -		return 0; +	if (!perf_session__read_build_ids(session, true)) +		return -1; -	feat_sec = calloc(sizeof(*feat_sec), nr_sections); -	if (feat_sec == NULL) -		return -ENOMEM; +	err = dsos__write_buildid_table(h, fd); +	if (err < 0) { +		pr_debug("failed to write buildid table\n"); +		return err; +	} +	if (!no_buildid_cache) +		perf_session__cache_build_ids(session); -	sec_size = sizeof(*feat_sec) * nr_sections; +	return 0; +} -	sec_start = self->data_offset + self->data_size; -	lseek(fd, sec_start + sec_size, SEEK_SET); +static int write_hostname(int fd, struct perf_header *h __maybe_unused, +			  struct perf_evlist *evlist __maybe_unused) +{ +	struct utsname uts; +	int ret; -	if (perf_header__has_feat(self, HEADER_TRACE_INFO)) { -		struct perf_file_section *trace_sec; +	ret = uname(&uts); +	if (ret < 0) +		return -1; -		trace_sec = &feat_sec[idx++]; +	return do_write_string(fd, uts.nodename); +} -		/* Write trace info */ -		trace_sec->offset = lseek(fd, 0, SEEK_CUR); -		read_tracing_data(fd, attrs, nr_counters); -		trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset; -	} +static int write_osrelease(int fd, struct perf_header *h __maybe_unused, +			   struct perf_evlist *evlist __maybe_unused) +{ +	struct utsname uts; +	int ret; -	if (perf_header__has_feat(self, HEADER_BUILD_ID)) { -		struct perf_file_section *buildid_sec; +	ret = uname(&uts); +	if (ret < 0) +		return -1; -		buildid_sec = &feat_sec[idx++]; +	return do_write_string(fd, uts.release); +} -		/* Write build-ids */ -		buildid_sec->offset = lseek(fd, 0, SEEK_CUR); -		err = dsos__write_buildid_table(self, fd); -		if (err < 0) { -			pr_debug("failed to write buildid table\n"); -			goto out_free; +static int write_arch(int fd, struct perf_header *h __maybe_unused, +		      struct perf_evlist *evlist __maybe_unused) +{ +	struct utsname uts; +	int ret; + +	ret = uname(&uts); +	if (ret < 0) +		return -1; + +	return do_write_string(fd, uts.machine); +} + +static int write_version(int fd, struct perf_header *h __maybe_unused, +			 struct perf_evlist *evlist __maybe_unused) +{ +	return do_write_string(fd, perf_version_string); +} + +static int write_cpudesc(int fd, struct perf_header *h __maybe_unused, +		       struct perf_evlist *evlist __maybe_unused) +{ +#ifndef CPUINFO_PROC +#define CPUINFO_PROC NULL +#endif +	FILE *file; +	char *buf = NULL; +	char *s, *p; +	const char *search = CPUINFO_PROC; +	size_t len = 0; +	int ret = -1; + +	if (!search) +		return -1; + +	file = fopen("/proc/cpuinfo", "r"); +	if (!file) +		return -1; + +	while (getline(&buf, &len, file) > 0) { +		ret = strncmp(buf, search, strlen(search)); +		if (!ret) +			break; +	} + +	if (ret) +		goto done; + +	s = buf; + +	p = strchr(buf, ':'); +	if (p && *(p+1) == ' ' && *(p+2)) +		s = p + 2; +	p = strchr(s, '\n'); +	if (p) +		*p = '\0'; + +	/* squash extra space characters (branding string) */ +	p = s; +	while (*p) { +		if (isspace(*p)) { +			char *r = p + 1; +			char *q = r; +			*p = ' '; +			while (*q && isspace(*q)) +				q++; +			if (q != (p+1)) +				while ((*r++ = *q++));  		} -		buildid_sec->size = lseek(fd, 0, SEEK_CUR) - -					  buildid_sec->offset; -		if (!no_buildid_cache) -			perf_session__cache_build_ids(session); +		p++;  	} +	ret = do_write_string(fd, s); +done: +	free(buf); +	fclose(file); +	return ret; +} -	lseek(fd, sec_start, SEEK_SET); -	err = do_write(fd, feat_sec, sec_size); -	if (err < 0) -		pr_debug("failed to write feature section\n"); -out_free: -	free(feat_sec); -	return err; +static int write_nrcpus(int fd, struct perf_header *h __maybe_unused, +			struct perf_evlist *evlist __maybe_unused) +{ +	long nr; +	u32 nrc, nra; +	int ret; + +	nr = sysconf(_SC_NPROCESSORS_CONF); +	if (nr < 0) +		return -1; + +	nrc = (u32)(nr & UINT_MAX); + +	nr = sysconf(_SC_NPROCESSORS_ONLN); +	if (nr < 0) +		return -1; + +	nra = (u32)(nr & UINT_MAX); + +	ret = do_write(fd, &nrc, sizeof(nrc)); +	if (ret < 0) +		return ret; + +	return do_write(fd, &nra, sizeof(nra));  } -int perf_header__write_pipe(int fd) +static int write_event_desc(int fd, struct perf_header *h __maybe_unused, +			    struct perf_evlist *evlist)  { -	struct perf_pipe_file_header f_header; -	int err; +	struct perf_evsel *evsel; +	u32 nre, nri, sz; +	int ret; -	f_header = (struct perf_pipe_file_header){ -		.magic	   = PERF_MAGIC, -		.size	   = sizeof(f_header), -	}; +	nre = evlist->nr_entries; -	err = do_write(fd, &f_header, sizeof(f_header)); -	if (err < 0) { -		pr_debug("failed to write perf pipe header\n"); -		return err; +	/* +	 * write number of events +	 */ +	ret = do_write(fd, &nre, sizeof(nre)); +	if (ret < 0) +		return ret; + +	/* +	 * size of perf_event_attr struct +	 */ +	sz = (u32)sizeof(evsel->attr); +	ret = do_write(fd, &sz, sizeof(sz)); +	if (ret < 0) +		return ret; + +	evlist__for_each(evlist, evsel) { +		ret = do_write(fd, &evsel->attr, sz); +		if (ret < 0) +			return ret; +		/* +		 * write number of unique id per event +		 * there is one id per instance of an event +		 * +		 * copy into an nri to be independent of the +		 * type of ids, +		 */ +		nri = evsel->ids; +		ret = do_write(fd, &nri, sizeof(nri)); +		if (ret < 0) +			return ret; + +		/* +		 * write event string as passed on cmdline +		 */ +		ret = do_write_string(fd, perf_evsel__name(evsel)); +		if (ret < 0) +			return ret; +		/* +		 * write unique ids for this event +		 */ +		ret = do_write(fd, evsel->id, evsel->ids * sizeof(u64)); +		if (ret < 0) +			return ret;  	} +	return 0; +} + +static int write_cmdline(int fd, struct perf_header *h __maybe_unused, +			 struct perf_evlist *evlist __maybe_unused) +{ +	char buf[MAXPATHLEN]; +	char proc[32]; +	u32 i, n; +	int ret; + +	/* +	 * actual atual path to perf binary +	 */ +	sprintf(proc, "/proc/%d/exe", getpid()); +	ret = readlink(proc, buf, sizeof(buf)); +	if (ret <= 0) +		return -1; +	/* readlink() does not add null termination */ +	buf[ret] = '\0'; + +	/* account for binary path */ +	n = header_argc + 1; + +	ret = do_write(fd, &n, sizeof(n)); +	if (ret < 0) +		return ret; + +	ret = do_write_string(fd, buf); +	if (ret < 0) +		return ret; + +	for (i = 0 ; i < header_argc; i++) { +		ret = do_write_string(fd, header_argv[i]); +		if (ret < 0) +			return ret; +	}  	return 0;  } -int perf_header__write(struct perf_header *self, int fd, bool at_exit) +#define CORE_SIB_FMT \ +	"/sys/devices/system/cpu/cpu%d/topology/core_siblings_list" +#define THRD_SIB_FMT \ +	"/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list" + +struct cpu_topo { +	u32 core_sib; +	u32 thread_sib; +	char **core_siblings; +	char **thread_siblings; +}; + +static int build_cpu_topo(struct cpu_topo *tp, int cpu)  { -	struct perf_file_header f_header; -	struct perf_file_attr   f_attr; -	struct perf_header_attr	*attr; -	int i, err; +	FILE *fp; +	char filename[MAXPATHLEN]; +	char *buf = NULL, *p; +	size_t len = 0; +	ssize_t sret; +	u32 i = 0; +	int ret = -1; + +	sprintf(filename, CORE_SIB_FMT, cpu); +	fp = fopen(filename, "r"); +	if (!fp) +		goto try_threads; + +	sret = getline(&buf, &len, fp); +	fclose(fp); +	if (sret <= 0) +		goto try_threads; + +	p = strchr(buf, '\n'); +	if (p) +		*p = '\0'; + +	for (i = 0; i < tp->core_sib; i++) { +		if (!strcmp(buf, tp->core_siblings[i])) +			break; +	} +	if (i == tp->core_sib) { +		tp->core_siblings[i] = buf; +		tp->core_sib++; +		buf = NULL; +		len = 0; +	} +	ret = 0; -	lseek(fd, sizeof(f_header), SEEK_SET); +try_threads: +	sprintf(filename, THRD_SIB_FMT, cpu); +	fp = fopen(filename, "r"); +	if (!fp) +		goto done; -	for (i = 0; i < self->attrs; i++) { -		attr = self->attr[i]; +	if (getline(&buf, &len, fp) <= 0) +		goto done; -		attr->id_offset = lseek(fd, 0, SEEK_CUR); -		err = do_write(fd, attr->id, attr->ids * sizeof(u64)); -		if (err < 0) { -			pr_debug("failed to write perf header\n"); -			return err; -		} +	p = strchr(buf, '\n'); +	if (p) +		*p = '\0'; + +	for (i = 0; i < tp->thread_sib; i++) { +		if (!strcmp(buf, tp->thread_siblings[i])) +			break; +	} +	if (i == tp->thread_sib) { +		tp->thread_siblings[i] = buf; +		tp->thread_sib++; +		buf = NULL;  	} +	ret = 0; +done: +	if(fp) +		fclose(fp); +	free(buf); +	return ret; +} +static void free_cpu_topo(struct cpu_topo *tp) +{ +	u32 i; -	self->attr_offset = lseek(fd, 0, SEEK_CUR); +	if (!tp) +		return; -	for (i = 0; i < self->attrs; i++) { -		attr = self->attr[i]; +	for (i = 0 ; i < tp->core_sib; i++) +		zfree(&tp->core_siblings[i]); -		f_attr = (struct perf_file_attr){ -			.attr = attr->attr, -			.ids  = { -				.offset = attr->id_offset, -				.size   = attr->ids * sizeof(u64), -			} -		}; -		err = do_write(fd, &f_attr, sizeof(f_attr)); -		if (err < 0) { -			pr_debug("failed to write perf header attribute\n"); -			return err; -		} +	for (i = 0 ; i < tp->thread_sib; i++) +		zfree(&tp->thread_siblings[i]); + +	free(tp); +} + +static struct cpu_topo *build_cpu_topology(void) +{ +	struct cpu_topo *tp; +	void *addr; +	u32 nr, i; +	size_t sz; +	long ncpus; +	int ret = -1; + +	ncpus = sysconf(_SC_NPROCESSORS_CONF); +	if (ncpus < 0) +		return NULL; + +	nr = (u32)(ncpus & UINT_MAX); + +	sz = nr * sizeof(char *); + +	addr = calloc(1, sizeof(*tp) + 2 * sz); +	if (!addr) +		return NULL; + +	tp = addr; + +	addr += sizeof(*tp); +	tp->core_siblings = addr; +	addr += sz; +	tp->thread_siblings = addr; + +	for (i = 0; i < nr; i++) { +		ret = build_cpu_topo(tp, i); +		if (ret < 0) +			break;  	} +	if (ret) { +		free_cpu_topo(tp); +		tp = NULL; +	} +	return tp; +} -	self->event_offset = lseek(fd, 0, SEEK_CUR); -	self->event_size = event_count * sizeof(struct perf_trace_event_type); -	if (events) { -		err = do_write(fd, events, self->event_size); -		if (err < 0) { -			pr_debug("failed to write perf header events\n"); -			return err; -		} +static int write_cpu_topology(int fd, struct perf_header *h __maybe_unused, +			  struct perf_evlist *evlist __maybe_unused) +{ +	struct cpu_topo *tp; +	u32 i; +	int ret; + +	tp = build_cpu_topology(); +	if (!tp) +		return -1; + +	ret = do_write(fd, &tp->core_sib, sizeof(tp->core_sib)); +	if (ret < 0) +		goto done; + +	for (i = 0; i < tp->core_sib; i++) { +		ret = do_write_string(fd, tp->core_siblings[i]); +		if (ret < 0) +			goto done;  	} +	ret = do_write(fd, &tp->thread_sib, sizeof(tp->thread_sib)); +	if (ret < 0) +		goto done; -	self->data_offset = lseek(fd, 0, SEEK_CUR); +	for (i = 0; i < tp->thread_sib; i++) { +		ret = do_write_string(fd, tp->thread_siblings[i]); +		if (ret < 0) +			break; +	} +done: +	free_cpu_topo(tp); +	return ret; +} -	if (at_exit) { -		err = perf_header__adds_write(self, fd); -		if (err < 0) -			return err; + + +static int write_total_mem(int fd, struct perf_header *h __maybe_unused, +			  struct perf_evlist *evlist __maybe_unused) +{ +	char *buf = NULL; +	FILE *fp; +	size_t len = 0; +	int ret = -1, n; +	uint64_t mem; + +	fp = fopen("/proc/meminfo", "r"); +	if (!fp) +		return -1; + +	while (getline(&buf, &len, fp) > 0) { +		ret = strncmp(buf, "MemTotal:", 9); +		if (!ret) +			break; +	} +	if (!ret) { +		n = sscanf(buf, "%*s %"PRIu64, &mem); +		if (n == 1) +			ret = do_write(fd, &mem, sizeof(mem));  	} +	free(buf); +	fclose(fp); +	return ret; +} -	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, -		}, -		.event_types = { -			.offset = self->event_offset, -			.size	= self->event_size, -		}, -	}; +static int write_topo_node(int fd, int node) +{ +	char str[MAXPATHLEN]; +	char field[32]; +	char *buf = NULL, *p; +	size_t len = 0; +	FILE *fp; +	u64 mem_total, mem_free, mem; +	int ret = -1; + +	sprintf(str, "/sys/devices/system/node/node%d/meminfo", node); +	fp = fopen(str, "r"); +	if (!fp) +		return -1; -	memcpy(&f_header.adds_features, &self->adds_features, sizeof(self->adds_features)); +	while (getline(&buf, &len, fp) > 0) { +		/* skip over invalid lines */ +		if (!strchr(buf, ':')) +			continue; +		if (sscanf(buf, "%*s %*d %31s %"PRIu64, field, &mem) != 2) +			goto done; +		if (!strcmp(field, "MemTotal:")) +			mem_total = mem; +		if (!strcmp(field, "MemFree:")) +			mem_free = mem; +	} -	lseek(fd, 0, SEEK_SET); -	err = do_write(fd, &f_header, sizeof(f_header)); -	if (err < 0) { -		pr_debug("failed to write perf header\n"); -		return err; +	fclose(fp); +	fp = NULL; + +	ret = do_write(fd, &mem_total, sizeof(u64)); +	if (ret) +		goto done; + +	ret = do_write(fd, &mem_free, sizeof(u64)); +	if (ret) +		goto done; + +	ret = -1; +	sprintf(str, "/sys/devices/system/node/node%d/cpulist", node); + +	fp = fopen(str, "r"); +	if (!fp) +		goto done; + +	if (getline(&buf, &len, fp) <= 0) +		goto done; + +	p = strchr(buf, '\n'); +	if (p) +		*p = '\0'; + +	ret = do_write_string(fd, buf); +done: +	free(buf); +	if (fp) +		fclose(fp); +	return ret; +} + +static int write_numa_topology(int fd, struct perf_header *h __maybe_unused, +			  struct perf_evlist *evlist __maybe_unused) +{ +	char *buf = NULL; +	size_t len = 0; +	FILE *fp; +	struct cpu_map *node_map = NULL; +	char *c; +	u32 nr, i, j; +	int ret = -1; + +	fp = fopen("/sys/devices/system/node/online", "r"); +	if (!fp) +		return -1; + +	if (getline(&buf, &len, fp) <= 0) +		goto done; + +	c = strchr(buf, '\n'); +	if (c) +		*c = '\0'; + +	node_map = cpu_map__new(buf); +	if (!node_map) +		goto done; + +	nr = (u32)node_map->nr; + +	ret = do_write(fd, &nr, sizeof(nr)); +	if (ret < 0) +		goto done; + +	for (i = 0; i < nr; i++) { +		j = (u32)node_map->map[i]; +		ret = do_write(fd, &j, sizeof(j)); +		if (ret < 0) +			break; + +		ret = write_topo_node(fd, i); +		if (ret < 0) +			break; +	} +done: +	free(buf); +	fclose(fp); +	free(node_map); +	return ret; +} + +/* + * File format: + * + * struct pmu_mappings { + *	u32	pmu_num; + *	struct pmu_map { + *		u32	type; + *		char	name[]; + *	}[pmu_num]; + * }; + */ + +static int write_pmu_mappings(int fd, struct perf_header *h __maybe_unused, +			      struct perf_evlist *evlist __maybe_unused) +{ +	struct perf_pmu *pmu = NULL; +	off_t offset = lseek(fd, 0, SEEK_CUR); +	__u32 pmu_num = 0; +	int ret; + +	/* write real pmu_num later */ +	ret = do_write(fd, &pmu_num, sizeof(pmu_num)); +	if (ret < 0) +		return ret; + +	while ((pmu = perf_pmu__scan(pmu))) { +		if (!pmu->name) +			continue; +		pmu_num++; + +		ret = do_write(fd, &pmu->type, sizeof(pmu->type)); +		if (ret < 0) +			return ret; + +		ret = do_write_string(fd, pmu->name); +		if (ret < 0) +			return ret; +	} + +	if (pwrite(fd, &pmu_num, sizeof(pmu_num), offset) != sizeof(pmu_num)) { +		/* discard all */ +		lseek(fd, offset, SEEK_SET); +		return -1;  	} -	lseek(fd, self->data_offset + self->data_size, SEEK_SET); -	self->frozen = 1;  	return 0;  } -static int perf_header__getbuffer64(struct perf_header *self, -				    int fd, void *buf, size_t size) +/* + * File format: + * + * struct group_descs { + *	u32	nr_groups; + *	struct group_desc { + *		char	name[]; + *		u32	leader_idx; + *		u32	nr_members; + *	}[nr_groups]; + * }; + */ +static int write_group_desc(int fd, struct perf_header *h __maybe_unused, +			    struct perf_evlist *evlist)  { -	if (do_read(fd, buf, size) <= 0) -		return -1; +	u32 nr_groups = evlist->nr_groups; +	struct perf_evsel *evsel; +	int ret; -	if (self->needs_swap) -		mem_bswap_64(buf, size); +	ret = do_write(fd, &nr_groups, sizeof(nr_groups)); +	if (ret < 0) +		return ret; + +	evlist__for_each(evlist, evsel) { +		if (perf_evsel__is_group_leader(evsel) && +		    evsel->nr_members > 1) { +			const char *name = evsel->group_name ?: "{anon_group}"; +			u32 leader_idx = evsel->idx; +			u32 nr_members = evsel->nr_members; + +			ret = do_write_string(fd, name); +			if (ret < 0) +				return ret; +			ret = do_write(fd, &leader_idx, sizeof(leader_idx)); +			if (ret < 0) +				return ret; + +			ret = do_write(fd, &nr_members, sizeof(nr_members)); +			if (ret < 0) +				return ret; +		} +	}  	return 0;  } -int perf_header__process_sections(struct perf_header *self, int fd, -				  int (*process)(struct perf_file_section *self, -						 struct perf_header *ph, -						 int feat, int fd)) +/* + * default get_cpuid(): nothing gets recorded + * actual implementation must be in arch/$(ARCH)/util/header.c + */ +int __attribute__ ((weak)) get_cpuid(char *buffer __maybe_unused, +				     size_t sz __maybe_unused)  { -	struct perf_file_section *feat_sec; -	int nr_sections; -	int sec_size; -	int idx = 0; -	int err = -1, feat = 1; +	return -1; +} -	nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS); -	if (!nr_sections) -		return 0; +static int write_cpuid(int fd, struct perf_header *h __maybe_unused, +		       struct perf_evlist *evlist __maybe_unused) +{ +	char buffer[64]; +	int ret; -	feat_sec = calloc(sizeof(*feat_sec), nr_sections); -	if (!feat_sec) -		return -1; +	ret = get_cpuid(buffer, sizeof(buffer)); +	if (!ret) +		goto write_it; -	sec_size = sizeof(*feat_sec) * nr_sections; +	return -1; +write_it: +	return do_write_string(fd, buffer); +} -	lseek(fd, self->data_offset + self->data_size, SEEK_SET); +static int write_branch_stack(int fd __maybe_unused, +			      struct perf_header *h __maybe_unused, +		       struct perf_evlist *evlist __maybe_unused) +{ +	return 0; +} -	if (perf_header__getbuffer64(self, fd, feat_sec, sec_size)) -		goto out_free; +static void print_hostname(struct perf_header *ph, int fd __maybe_unused, +			   FILE *fp) +{ +	fprintf(fp, "# hostname : %s\n", ph->env.hostname); +} -	err = 0; -	while (idx < nr_sections && feat < HEADER_LAST_FEATURE) { -		if (perf_header__has_feat(self, feat)) { -			struct perf_file_section *sec = &feat_sec[idx++]; +static void print_osrelease(struct perf_header *ph, int fd __maybe_unused, +			    FILE *fp) +{ +	fprintf(fp, "# os release : %s\n", ph->env.os_release); +} + +static void print_arch(struct perf_header *ph, int fd __maybe_unused, FILE *fp) +{ +	fprintf(fp, "# arch : %s\n", ph->env.arch); +} + +static void print_cpudesc(struct perf_header *ph, int fd __maybe_unused, +			  FILE *fp) +{ +	fprintf(fp, "# cpudesc : %s\n", ph->env.cpu_desc); +} -			err = process(sec, self, feat, fd); -			if (err < 0) -				break; +static void print_nrcpus(struct perf_header *ph, int fd __maybe_unused, +			 FILE *fp) +{ +	fprintf(fp, "# nrcpus online : %u\n", ph->env.nr_cpus_online); +	fprintf(fp, "# nrcpus avail : %u\n", ph->env.nr_cpus_avail); +} + +static void print_version(struct perf_header *ph, int fd __maybe_unused, +			  FILE *fp) +{ +	fprintf(fp, "# perf version : %s\n", ph->env.version); +} + +static void print_cmdline(struct perf_header *ph, int fd __maybe_unused, +			  FILE *fp) +{ +	int nr, i; +	char *str; + +	nr = ph->env.nr_cmdline; +	str = ph->env.cmdline; + +	fprintf(fp, "# cmdline : "); + +	for (i = 0; i < nr; i++) { +		fprintf(fp, "%s ", str); +		str += strlen(str) + 1; +	} +	fputc('\n', fp); +} + +static void print_cpu_topology(struct perf_header *ph, int fd __maybe_unused, +			       FILE *fp) +{ +	int nr, i; +	char *str; + +	nr = ph->env.nr_sibling_cores; +	str = ph->env.sibling_cores; + +	for (i = 0; i < nr; i++) { +		fprintf(fp, "# sibling cores   : %s\n", str); +		str += strlen(str) + 1; +	} + +	nr = ph->env.nr_sibling_threads; +	str = ph->env.sibling_threads; + +	for (i = 0; i < nr; i++) { +		fprintf(fp, "# sibling threads : %s\n", str); +		str += strlen(str) + 1; +	} +} + +static void free_event_desc(struct perf_evsel *events) +{ +	struct perf_evsel *evsel; + +	if (!events) +		return; + +	for (evsel = events; evsel->attr.size; evsel++) { +		zfree(&evsel->name); +		zfree(&evsel->id); +	} + +	free(events); +} + +static struct perf_evsel * +read_event_desc(struct perf_header *ph, int fd) +{ +	struct perf_evsel *evsel, *events = NULL; +	u64 *id; +	void *buf = NULL; +	u32 nre, sz, nr, i, j; +	ssize_t ret; +	size_t msz; + +	/* number of events */ +	ret = readn(fd, &nre, sizeof(nre)); +	if (ret != (ssize_t)sizeof(nre)) +		goto error; + +	if (ph->needs_swap) +		nre = bswap_32(nre); + +	ret = readn(fd, &sz, sizeof(sz)); +	if (ret != (ssize_t)sizeof(sz)) +		goto error; + +	if (ph->needs_swap) +		sz = bswap_32(sz); + +	/* buffer to hold on file attr struct */ +	buf = malloc(sz); +	if (!buf) +		goto error; + +	/* the last event terminates with evsel->attr.size == 0: */ +	events = calloc(nre + 1, sizeof(*events)); +	if (!events) +		goto error; + +	msz = sizeof(evsel->attr); +	if (sz < msz) +		msz = sz; + +	for (i = 0, evsel = events; i < nre; evsel++, i++) { +		evsel->idx = i; + +		/* +		 * must read entire on-file attr struct to +		 * sync up with layout. +		 */ +		ret = readn(fd, buf, sz); +		if (ret != (ssize_t)sz) +			goto error; + +		if (ph->needs_swap) +			perf_event__attr_swap(buf); + +		memcpy(&evsel->attr, buf, msz); + +		ret = readn(fd, &nr, sizeof(nr)); +		if (ret != (ssize_t)sizeof(nr)) +			goto error; + +		if (ph->needs_swap) { +			nr = bswap_32(nr); +			evsel->needs_swap = true; +		} + +		evsel->name = do_read_string(fd, ph); + +		if (!nr) +			continue; + +		id = calloc(nr, sizeof(*id)); +		if (!id) +			goto error; +		evsel->ids = nr; +		evsel->id = id; + +		for (j = 0 ; j < nr; j++) { +			ret = readn(fd, id, sizeof(*id)); +			if (ret != (ssize_t)sizeof(*id)) +				goto error; +			if (ph->needs_swap) +				*id = bswap_64(*id); +			id++;  		} -		++feat;  	} -out_free: -	free(feat_sec); -	return err; +out: +	free(buf); +	return events; +error: +	if (events) +		free_event_desc(events); +	events = NULL; +	goto out;  } -int perf_file_header__read(struct perf_file_header *self, -			   struct perf_header *ph, int fd) +static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)  { -	lseek(fd, 0, SEEK_SET); +	struct perf_evsel *evsel, *events = read_event_desc(ph, fd); +	u32 j; +	u64 *id; -	if (do_read(fd, self, sizeof(*self)) <= 0 || -	    memcmp(&self->magic, __perf_magic, sizeof(self->magic))) -		return -1; +	if (!events) { +		fprintf(fp, "# event desc: not available or unable to read\n"); +		return; +	} + +	for (evsel = events; evsel->attr.size; evsel++) { +		fprintf(fp, "# event : name = %s, ", evsel->name); + +		fprintf(fp, "type = %d, config = 0x%"PRIx64 +			    ", config1 = 0x%"PRIx64", config2 = 0x%"PRIx64, +				evsel->attr.type, +				(u64)evsel->attr.config, +				(u64)evsel->attr.config1, +				(u64)evsel->attr.config2); + +		fprintf(fp, ", excl_usr = %d, excl_kern = %d", +				evsel->attr.exclude_user, +				evsel->attr.exclude_kernel); + +		fprintf(fp, ", excl_host = %d, excl_guest = %d", +				evsel->attr.exclude_host, +				evsel->attr.exclude_guest); + +		fprintf(fp, ", precise_ip = %d", evsel->attr.precise_ip); + +		fprintf(fp, ", attr_mmap2 = %d", evsel->attr.mmap2); +		fprintf(fp, ", attr_mmap  = %d", evsel->attr.mmap); +		fprintf(fp, ", attr_mmap_data = %d", evsel->attr.mmap_data); +		if (evsel->ids) { +			fprintf(fp, ", id = {"); +			for (j = 0, id = evsel->id; j < evsel->ids; j++, id++) { +				if (j) +					fputc(',', fp); +				fprintf(fp, " %"PRIu64, *id); +			} +			fprintf(fp, " }"); +		} -	if (self->attr_size != sizeof(struct perf_file_attr)) { -		u64 attr_size = bswap_64(self->attr_size); +		fputc('\n', fp); +	} -		if (attr_size != sizeof(struct perf_file_attr)) -			return -1; +	free_event_desc(events); +} + +static void print_total_mem(struct perf_header *ph, int fd __maybe_unused, +			    FILE *fp) +{ +	fprintf(fp, "# total memory : %Lu kB\n", ph->env.total_mem); +} -		mem_bswap_64(self, offsetof(struct perf_file_header, -					    adds_features)); -		ph->needs_swap = true; +static void print_numa_topology(struct perf_header *ph, int fd __maybe_unused, +				FILE *fp) +{ +	u32 nr, c, i; +	char *str, *tmp; +	uint64_t mem_total, mem_free; + +	/* nr nodes */ +	nr = ph->env.nr_numa_nodes; +	str = ph->env.numa_nodes; + +	for (i = 0; i < nr; i++) { +		/* node number */ +		c = strtoul(str, &tmp, 0); +		if (*tmp != ':') +			goto error; + +		str = tmp + 1; +		mem_total = strtoull(str, &tmp, 0); +		if (*tmp != ':') +			goto error; + +		str = tmp + 1; +		mem_free = strtoull(str, &tmp, 0); +		if (*tmp != ':') +			goto error; + +		fprintf(fp, "# node%u meminfo  : total = %"PRIu64" kB," +			    " free = %"PRIu64" kB\n", +			c, mem_total, mem_free); + +		str = tmp + 1; +		fprintf(fp, "# node%u cpu list : %s\n", c, str); + +		str += strlen(str) + 1;  	} +	return; +error: +	fprintf(fp, "# numa topology : not available\n"); +} -	if (self->size != sizeof(*self)) { -		/* Support the previous format */ -		if (self->size == offsetof(typeof(*self), adds_features)) -			bitmap_zero(self->adds_features, HEADER_FEAT_BITS); -		else -			return -1; +static void print_cpuid(struct perf_header *ph, int fd __maybe_unused, FILE *fp) +{ +	fprintf(fp, "# cpuid : %s\n", ph->env.cpuid); +} + +static void print_branch_stack(struct perf_header *ph __maybe_unused, +			       int fd __maybe_unused, FILE *fp) +{ +	fprintf(fp, "# contains samples with branch stack\n"); +} + +static void print_pmu_mappings(struct perf_header *ph, int fd __maybe_unused, +			       FILE *fp) +{ +	const char *delimiter = "# pmu mappings: "; +	char *str, *tmp; +	u32 pmu_num; +	u32 type; + +	pmu_num = ph->env.nr_pmu_mappings; +	if (!pmu_num) { +		fprintf(fp, "# pmu mappings: not available\n"); +		return;  	} -	memcpy(&ph->adds_features, &self->adds_features, -	       sizeof(ph->adds_features)); -	/* -	 * FIXME: hack that assumes that if we need swap the perf.data file -	 * may be coming from an arch with a different word-size, ergo different -	 * DEFINE_BITMAP format, investigate more later, but for now its mostly -	 * safe to assume that we have a build-id section. Trace files probably -	 * have several other issues in this realm anyway... -	 */ -	if (ph->needs_swap) { -		memset(&ph->adds_features, 0, sizeof(ph->adds_features)); -		perf_header__set_feat(ph, HEADER_BUILD_ID); +	str = ph->env.pmu_mappings; + +	while (pmu_num) { +		type = strtoul(str, &tmp, 0); +		if (*tmp != ':') +			goto error; + +		str = tmp + 1; +		fprintf(fp, "%s%s = %" PRIu32, delimiter, str, type); + +		delimiter = ", "; +		str += strlen(str) + 1; +		pmu_num--;  	} -	ph->event_offset = self->event_types.offset; -	ph->event_size   = self->event_types.size; -	ph->data_offset  = self->data.offset; -	ph->data_size	 = self->data.size; -	return 0; +	fprintf(fp, "\n"); + +	if (!pmu_num) +		return; +error: +	fprintf(fp, "# pmu mappings: unable to read\n"); +} + +static void print_group_desc(struct perf_header *ph, int fd __maybe_unused, +			     FILE *fp) +{ +	struct perf_session *session; +	struct perf_evsel *evsel; +	u32 nr = 0; + +	session = container_of(ph, struct perf_session, header); + +	evlist__for_each(session->evlist, evsel) { +		if (perf_evsel__is_group_leader(evsel) && +		    evsel->nr_members > 1) { +			fprintf(fp, "# group: %s{%s", evsel->group_name ?: "", +				perf_evsel__name(evsel)); + +			nr = evsel->nr_members - 1; +		} else if (nr) { +			fprintf(fp, ",%s", perf_evsel__name(evsel)); + +			if (--nr == 0) +				fprintf(fp, "}\n"); +		} +	}  }  static int __event_process_build_id(struct build_id_event *bev, @@ -752,28 +1557,91 @@ out:  	return err;  } -static int perf_header__read_build_ids(struct perf_header *self, -			int input, u64 offset, u64 size) +static int perf_header__read_build_ids_abi_quirk(struct perf_header *header, +						 int input, u64 offset, u64 size)  { -	struct perf_session *session = container_of(self, -			struct perf_session, header); +	struct perf_session *session = container_of(header, struct perf_session, header); +	struct { +		struct perf_event_header   header; +		u8			   build_id[PERF_ALIGN(BUILD_ID_SIZE, sizeof(u64))]; +		char			   filename[0]; +	} old_bev;  	struct build_id_event bev;  	char filename[PATH_MAX];  	u64 limit = offset + size; + +	while (offset < limit) { +		ssize_t len; + +		if (readn(input, &old_bev, sizeof(old_bev)) != sizeof(old_bev)) +			return -1; + +		if (header->needs_swap) +			perf_event_header__bswap(&old_bev.header); + +		len = old_bev.header.size - sizeof(old_bev); +		if (readn(input, filename, len) != len) +			return -1; + +		bev.header = old_bev.header; + +		/* +		 * As the pid is the missing value, we need to fill +		 * it properly. The header.misc value give us nice hint. +		 */ +		bev.pid	= HOST_KERNEL_ID; +		if (bev.header.misc == PERF_RECORD_MISC_GUEST_USER || +		    bev.header.misc == PERF_RECORD_MISC_GUEST_KERNEL) +			bev.pid	= DEFAULT_GUEST_KERNEL_ID; + +		memcpy(bev.build_id, old_bev.build_id, sizeof(bev.build_id)); +		__event_process_build_id(&bev, filename, session); + +		offset += bev.header.size; +	} + +	return 0; +} + +static int perf_header__read_build_ids(struct perf_header *header, +				       int input, u64 offset, u64 size) +{ +	struct perf_session *session = container_of(header, struct perf_session, header); +	struct build_id_event bev; +	char filename[PATH_MAX]; +	u64 limit = offset + size, orig_offset = offset;  	int err = -1;  	while (offset < limit) {  		ssize_t len; -		if (read(input, &bev, sizeof(bev)) != sizeof(bev)) +		if (readn(input, &bev, sizeof(bev)) != sizeof(bev))  			goto out; -		if (self->needs_swap) +		if (header->needs_swap)  			perf_event_header__bswap(&bev.header);  		len = bev.header.size - sizeof(bev); -		if (read(input, filename, len) != len) +		if (readn(input, filename, len) != len)  			goto out; +		/* +		 * The a1645ce1 changeset: +		 * +		 * "perf: 'perf kvm' tool for monitoring guest performance from host" +		 * +		 * Added a field to struct build_id_event that broke the file +		 * format. +		 * +		 * Since the kernel build-id is the first entry, process the +		 * table using the old format if the well known +		 * '[kernel.kallsyms]' string for the kernel build-id has the +		 * first 4 characters chopped off (where the pid_t sits). +		 */ +		if (memcmp(filename, "nel.kallsyms]", 13) == 0) { +			if (lseek(input, orig_offset, SEEK_SET) == (off_t)-1) +				return -1; +			return perf_header__read_build_ids_abi_quirk(header, input, offset, size); +		}  		__event_process_build_id(&bev, filename, session); @@ -784,304 +1652,1248 @@ out:  	return err;  } -static int perf_file_section__process(struct perf_file_section *self, -				      struct perf_header *ph, -				      int feat, int fd) +static int process_tracing_data(struct perf_file_section *section __maybe_unused, +				struct perf_header *ph __maybe_unused, +				int fd, void *data) +{ +	ssize_t ret = trace_report(fd, data, false); +	return ret < 0 ? -1 : 0; +} + +static int process_build_id(struct perf_file_section *section, +			    struct perf_header *ph, int fd, +			    void *data __maybe_unused) +{ +	if (perf_header__read_build_ids(ph, fd, section->offset, section->size)) +		pr_debug("Failed to read buildids, continuing...\n"); +	return 0; +} + +static int process_hostname(struct perf_file_section *section __maybe_unused, +			    struct perf_header *ph, int fd, +			    void *data __maybe_unused) +{ +	ph->env.hostname = do_read_string(fd, ph); +	return ph->env.hostname ? 0 : -ENOMEM; +} + +static int process_osrelease(struct perf_file_section *section __maybe_unused, +			     struct perf_header *ph, int fd, +			     void *data __maybe_unused) +{ +	ph->env.os_release = do_read_string(fd, ph); +	return ph->env.os_release ? 0 : -ENOMEM; +} + +static int process_version(struct perf_file_section *section __maybe_unused, +			   struct perf_header *ph, int fd, +			   void *data __maybe_unused) +{ +	ph->env.version = do_read_string(fd, ph); +	return ph->env.version ? 0 : -ENOMEM; +} + +static int process_arch(struct perf_file_section *section __maybe_unused, +			struct perf_header *ph,	int fd, +			void *data __maybe_unused) +{ +	ph->env.arch = do_read_string(fd, ph); +	return ph->env.arch ? 0 : -ENOMEM; +} + +static int process_nrcpus(struct perf_file_section *section __maybe_unused, +			  struct perf_header *ph, int fd, +			  void *data __maybe_unused) +{ +	ssize_t ret; +	u32 nr; + +	ret = readn(fd, &nr, sizeof(nr)); +	if (ret != sizeof(nr)) +		return -1; + +	if (ph->needs_swap) +		nr = bswap_32(nr); + +	ph->env.nr_cpus_online = nr; + +	ret = readn(fd, &nr, sizeof(nr)); +	if (ret != sizeof(nr)) +		return -1; + +	if (ph->needs_swap) +		nr = bswap_32(nr); + +	ph->env.nr_cpus_avail = nr; +	return 0; +} + +static int process_cpudesc(struct perf_file_section *section __maybe_unused, +			   struct perf_header *ph, int fd, +			   void *data __maybe_unused) +{ +	ph->env.cpu_desc = do_read_string(fd, ph); +	return ph->env.cpu_desc ? 0 : -ENOMEM; +} + +static int process_cpuid(struct perf_file_section *section __maybe_unused, +			 struct perf_header *ph,  int fd, +			 void *data __maybe_unused) +{ +	ph->env.cpuid = do_read_string(fd, ph); +	return ph->env.cpuid ? 0 : -ENOMEM; +} + +static int process_total_mem(struct perf_file_section *section __maybe_unused, +			     struct perf_header *ph, int fd, +			     void *data __maybe_unused)  { -	if (lseek(fd, self->offset, SEEK_SET) == (off_t)-1) { -		pr_debug("Failed to lseek to %Ld offset for feature %d, " -			 "continuing...\n", self->offset, feat); +	uint64_t mem; +	ssize_t ret; + +	ret = readn(fd, &mem, sizeof(mem)); +	if (ret != sizeof(mem)) +		return -1; + +	if (ph->needs_swap) +		mem = bswap_64(mem); + +	ph->env.total_mem = mem; +	return 0; +} + +static struct perf_evsel * +perf_evlist__find_by_index(struct perf_evlist *evlist, int idx) +{ +	struct perf_evsel *evsel; + +	evlist__for_each(evlist, evsel) { +		if (evsel->idx == idx) +			return evsel; +	} + +	return NULL; +} + +static void +perf_evlist__set_event_name(struct perf_evlist *evlist, +			    struct perf_evsel *event) +{ +	struct perf_evsel *evsel; + +	if (!event->name) +		return; + +	evsel = perf_evlist__find_by_index(evlist, event->idx); +	if (!evsel) +		return; + +	if (evsel->name) +		return; + +	evsel->name = strdup(event->name); +} + +static int +process_event_desc(struct perf_file_section *section __maybe_unused, +		   struct perf_header *header, int fd, +		   void *data __maybe_unused) +{ +	struct perf_session *session; +	struct perf_evsel *evsel, *events = read_event_desc(header, fd); + +	if (!events)  		return 0; + +	session = container_of(header, struct perf_session, header); +	for (evsel = events; evsel->attr.size; evsel++) +		perf_evlist__set_event_name(session->evlist, evsel); + +	free_event_desc(events); + +	return 0; +} + +static int process_cmdline(struct perf_file_section *section __maybe_unused, +			   struct perf_header *ph, int fd, +			   void *data __maybe_unused) +{ +	ssize_t ret; +	char *str; +	u32 nr, i; +	struct strbuf sb; + +	ret = readn(fd, &nr, sizeof(nr)); +	if (ret != sizeof(nr)) +		return -1; + +	if (ph->needs_swap) +		nr = bswap_32(nr); + +	ph->env.nr_cmdline = nr; +	strbuf_init(&sb, 128); + +	for (i = 0; i < nr; i++) { +		str = do_read_string(fd, ph); +		if (!str) +			goto error; + +		/* include a NULL character at the end */ +		strbuf_add(&sb, str, strlen(str) + 1); +		free(str);  	} +	ph->env.cmdline = strbuf_detach(&sb, NULL); +	return 0; -	switch (feat) { -	case HEADER_TRACE_INFO: -		trace_report(fd, false); -		break; +error: +	strbuf_release(&sb); +	return -1; +} -	case HEADER_BUILD_ID: -		if (perf_header__read_build_ids(ph, fd, self->offset, self->size)) -			pr_debug("Failed to read buildids, continuing...\n"); -		break; -	default: -		pr_debug("unknown feature %d, continuing...\n", feat); +static int process_cpu_topology(struct perf_file_section *section __maybe_unused, +				struct perf_header *ph, int fd, +				void *data __maybe_unused) +{ +	ssize_t ret; +	u32 nr, i; +	char *str; +	struct strbuf sb; + +	ret = readn(fd, &nr, sizeof(nr)); +	if (ret != sizeof(nr)) +		return -1; + +	if (ph->needs_swap) +		nr = bswap_32(nr); + +	ph->env.nr_sibling_cores = nr; +	strbuf_init(&sb, 128); + +	for (i = 0; i < nr; i++) { +		str = do_read_string(fd, ph); +		if (!str) +			goto error; + +		/* include a NULL character at the end */ +		strbuf_add(&sb, str, strlen(str) + 1); +		free(str);  	} +	ph->env.sibling_cores = strbuf_detach(&sb, NULL); + +	ret = readn(fd, &nr, sizeof(nr)); +	if (ret != sizeof(nr)) +		return -1; + +	if (ph->needs_swap) +		nr = bswap_32(nr); +	ph->env.nr_sibling_threads = nr; + +	for (i = 0; i < nr; i++) { +		str = do_read_string(fd, ph); +		if (!str) +			goto error; + +		/* include a NULL character at the end */ +		strbuf_add(&sb, str, strlen(str) + 1); +		free(str); +	} +	ph->env.sibling_threads = strbuf_detach(&sb, NULL);  	return 0; + +error: +	strbuf_release(&sb); +	return -1;  } -static int perf_file_header__read_pipe(struct perf_pipe_file_header *self, -				       struct perf_header *ph, int fd, -				       bool repipe) +static int process_numa_topology(struct perf_file_section *section __maybe_unused, +				 struct perf_header *ph, int fd, +				 void *data __maybe_unused)  { -	if (do_read(fd, self, sizeof(*self)) <= 0 || -	    memcmp(&self->magic, __perf_magic, sizeof(self->magic))) +	ssize_t ret; +	u32 nr, node, i; +	char *str; +	uint64_t mem_total, mem_free; +	struct strbuf sb; + +	/* nr nodes */ +	ret = readn(fd, &nr, sizeof(nr)); +	if (ret != sizeof(nr)) +		goto error; + +	if (ph->needs_swap) +		nr = bswap_32(nr); + +	ph->env.nr_numa_nodes = nr; +	strbuf_init(&sb, 256); + +	for (i = 0; i < nr; i++) { +		/* node number */ +		ret = readn(fd, &node, sizeof(node)); +		if (ret != sizeof(node)) +			goto error; + +		ret = readn(fd, &mem_total, sizeof(u64)); +		if (ret != sizeof(u64)) +			goto error; + +		ret = readn(fd, &mem_free, sizeof(u64)); +		if (ret != sizeof(u64)) +			goto error; + +		if (ph->needs_swap) { +			node = bswap_32(node); +			mem_total = bswap_64(mem_total); +			mem_free = bswap_64(mem_free); +		} + +		strbuf_addf(&sb, "%u:%"PRIu64":%"PRIu64":", +			    node, mem_total, mem_free); + +		str = do_read_string(fd, ph); +		if (!str) +			goto error; + +		/* include a NULL character at the end */ +		strbuf_add(&sb, str, strlen(str) + 1); +		free(str); +	} +	ph->env.numa_nodes = strbuf_detach(&sb, NULL); +	return 0; + +error: +	strbuf_release(&sb); +	return -1; +} + +static int process_pmu_mappings(struct perf_file_section *section __maybe_unused, +				struct perf_header *ph, int fd, +				void *data __maybe_unused) +{ +	ssize_t ret; +	char *name; +	u32 pmu_num; +	u32 type; +	struct strbuf sb; + +	ret = readn(fd, &pmu_num, sizeof(pmu_num)); +	if (ret != sizeof(pmu_num))  		return -1; -	if (repipe && do_write(STDOUT_FILENO, self, sizeof(*self)) < 0) +	if (ph->needs_swap) +		pmu_num = bswap_32(pmu_num); + +	if (!pmu_num) { +		pr_debug("pmu mappings not available\n"); +		return 0; +	} + +	ph->env.nr_pmu_mappings = pmu_num; +	strbuf_init(&sb, 128); + +	while (pmu_num) { +		if (readn(fd, &type, sizeof(type)) != sizeof(type)) +			goto error; +		if (ph->needs_swap) +			type = bswap_32(type); + +		name = do_read_string(fd, ph); +		if (!name) +			goto error; + +		strbuf_addf(&sb, "%u:%s", type, name); +		/* include a NULL character at the end */ +		strbuf_add(&sb, "", 1); + +		free(name); +		pmu_num--; +	} +	ph->env.pmu_mappings = strbuf_detach(&sb, NULL); +	return 0; + +error: +	strbuf_release(&sb); +	return -1; +} + +static int process_group_desc(struct perf_file_section *section __maybe_unused, +			      struct perf_header *ph, int fd, +			      void *data __maybe_unused) +{ +	size_t ret = -1; +	u32 i, nr, nr_groups; +	struct perf_session *session; +	struct perf_evsel *evsel, *leader = NULL; +	struct group_desc { +		char *name; +		u32 leader_idx; +		u32 nr_members; +	} *desc; + +	if (readn(fd, &nr_groups, sizeof(nr_groups)) != sizeof(nr_groups))  		return -1; -	if (self->size != sizeof(*self)) { -		u64 size = bswap_64(self->size); +	if (ph->needs_swap) +		nr_groups = bswap_32(nr_groups); -		if (size != sizeof(*self)) -			return -1; +	ph->env.nr_groups = nr_groups; +	if (!nr_groups) { +		pr_debug("group desc not available\n"); +		return 0; +	} + +	desc = calloc(nr_groups, sizeof(*desc)); +	if (!desc) +		return -1; -		ph->needs_swap = true; +	for (i = 0; i < nr_groups; i++) { +		desc[i].name = do_read_string(fd, ph); +		if (!desc[i].name) +			goto out_free; + +		if (readn(fd, &desc[i].leader_idx, sizeof(u32)) != sizeof(u32)) +			goto out_free; + +		if (readn(fd, &desc[i].nr_members, sizeof(u32)) != sizeof(u32)) +			goto out_free; + +		if (ph->needs_swap) { +			desc[i].leader_idx = bswap_32(desc[i].leader_idx); +			desc[i].nr_members = bswap_32(desc[i].nr_members); +		}  	} -	return 0; +	/* +	 * Rebuild group relationship based on the group_desc +	 */ +	session = container_of(ph, struct perf_session, header); +	session->evlist->nr_groups = nr_groups; + +	i = nr = 0; +	evlist__for_each(session->evlist, evsel) { +		if (evsel->idx == (int) desc[i].leader_idx) { +			evsel->leader = evsel; +			/* {anon_group} is a dummy name */ +			if (strcmp(desc[i].name, "{anon_group}")) { +				evsel->group_name = desc[i].name; +				desc[i].name = NULL; +			} +			evsel->nr_members = desc[i].nr_members; + +			if (i >= nr_groups || nr > 0) { +				pr_debug("invalid group desc\n"); +				goto out_free; +			} + +			leader = evsel; +			nr = evsel->nr_members - 1; +			i++; +		} else if (nr) { +			/* This is a group member */ +			evsel->leader = leader; + +			nr--; +		} +	} + +	if (i != nr_groups || nr != 0) { +		pr_debug("invalid group desc\n"); +		goto out_free; +	} + +	ret = 0; +out_free: +	for (i = 0; i < nr_groups; i++) +		zfree(&desc[i].name); +	free(desc); + +	return ret;  } -static int perf_header__read_pipe(struct perf_session *session, int fd) +struct feature_ops { +	int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist); +	void (*print)(struct perf_header *h, int fd, FILE *fp); +	int (*process)(struct perf_file_section *section, +		       struct perf_header *h, int fd, void *data); +	const char *name; +	bool full_only; +}; + +#define FEAT_OPA(n, func) \ +	[n] = { .name = #n, .write = write_##func, .print = print_##func } +#define FEAT_OPP(n, func) \ +	[n] = { .name = #n, .write = write_##func, .print = print_##func, \ +		.process = process_##func } +#define FEAT_OPF(n, func) \ +	[n] = { .name = #n, .write = write_##func, .print = print_##func, \ +		.process = process_##func, .full_only = true } + +/* feature_ops not implemented: */ +#define print_tracing_data	NULL +#define print_build_id		NULL + +static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = { +	FEAT_OPP(HEADER_TRACING_DATA,	tracing_data), +	FEAT_OPP(HEADER_BUILD_ID,	build_id), +	FEAT_OPP(HEADER_HOSTNAME,	hostname), +	FEAT_OPP(HEADER_OSRELEASE,	osrelease), +	FEAT_OPP(HEADER_VERSION,	version), +	FEAT_OPP(HEADER_ARCH,		arch), +	FEAT_OPP(HEADER_NRCPUS,		nrcpus), +	FEAT_OPP(HEADER_CPUDESC,	cpudesc), +	FEAT_OPP(HEADER_CPUID,		cpuid), +	FEAT_OPP(HEADER_TOTAL_MEM,	total_mem), +	FEAT_OPP(HEADER_EVENT_DESC,	event_desc), +	FEAT_OPP(HEADER_CMDLINE,	cmdline), +	FEAT_OPF(HEADER_CPU_TOPOLOGY,	cpu_topology), +	FEAT_OPF(HEADER_NUMA_TOPOLOGY,	numa_topology), +	FEAT_OPA(HEADER_BRANCH_STACK,	branch_stack), +	FEAT_OPP(HEADER_PMU_MAPPINGS,	pmu_mappings), +	FEAT_OPP(HEADER_GROUP_DESC,	group_desc), +}; + +struct header_print_data { +	FILE *fp; +	bool full; /* extended list of headers */ +}; + +static int perf_file_section__fprintf_info(struct perf_file_section *section, +					   struct perf_header *ph, +					   int feat, int fd, void *data)  { -	struct perf_header *self = &session->header; -	struct perf_pipe_file_header f_header; +	struct header_print_data *hd = data; -	if (perf_file_header__read_pipe(&f_header, self, fd, -					session->repipe) < 0) { -		pr_debug("incompatible file format\n"); -		return -EINVAL; +	if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) { +		pr_debug("Failed to lseek to %" PRIu64 " offset for feature " +				"%d, continuing...\n", section->offset, feat); +		return 0; +	} +	if (feat >= HEADER_LAST_FEATURE) { +		pr_warning("unknown feature %d\n", feat); +		return 0;  	} +	if (!feat_ops[feat].print) +		return 0; -	session->fd = fd; +	if (!feat_ops[feat].full_only || hd->full) +		feat_ops[feat].print(ph, fd, hd->fp); +	else +		fprintf(hd->fp, "# %s info available, use -I to display\n", +			feat_ops[feat].name);  	return 0;  } -int perf_header__read(struct perf_session *session, int fd) +int perf_header__fprintf_info(struct perf_session *session, FILE *fp, bool full)  { -	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; +	struct header_print_data hd; +	struct perf_header *header = &session->header; +	int fd = perf_data_file__fd(session->file); +	hd.fp = fp; +	hd.full = full; + +	perf_header__process_sections(header, fd, &hd, +				      perf_file_section__fprintf_info); +	return 0; +} -	if (session->fd_pipe) -		return perf_header__read_pipe(session, fd); +static int do_write_feat(int fd, struct perf_header *h, int type, +			 struct perf_file_section **p, +			 struct perf_evlist *evlist) +{ +	int err; +	int ret = 0; -	if (perf_file_header__read(&f_header, self, fd) < 0) { -		pr_debug("incompatible file format\n"); -		return -EINVAL; +	if (perf_header__has_feat(h, type)) { +		if (!feat_ops[type].write) +			return -1; + +		(*p)->offset = lseek(fd, 0, SEEK_CUR); + +		err = feat_ops[type].write(fd, h, evlist); +		if (err < 0) { +			pr_debug("failed to write feature %d\n", type); + +			/* undo anything written */ +			lseek(fd, (*p)->offset, SEEK_SET); + +			return -1; +		} +		(*p)->size = lseek(fd, 0, SEEK_CUR) - (*p)->offset; +		(*p)++;  	} +	return ret; +} -	nr_attrs = f_header.attrs.size / sizeof(f_attr); -	lseek(fd, f_header.attrs.offset, SEEK_SET); +static int perf_header__adds_write(struct perf_header *header, +				   struct perf_evlist *evlist, int fd) +{ +	int nr_sections; +	struct perf_file_section *feat_sec, *p; +	int sec_size; +	u64 sec_start; +	int feat; +	int err; -	for (i = 0; i < nr_attrs; i++) { -		struct perf_header_attr *attr; -		off_t tmp; +	nr_sections = bitmap_weight(header->adds_features, HEADER_FEAT_BITS); +	if (!nr_sections) +		return 0; -		if (perf_header__getbuffer64(self, fd, &f_attr, sizeof(f_attr))) -			goto out_errno; +	feat_sec = p = calloc(nr_sections, sizeof(*feat_sec)); +	if (feat_sec == NULL) +		return -ENOMEM; -		tmp = lseek(fd, 0, SEEK_CUR); +	sec_size = sizeof(*feat_sec) * nr_sections; -		attr = perf_header_attr__new(&f_attr.attr); -		if (attr == NULL) -			 return -ENOMEM; +	sec_start = header->feat_offset; +	lseek(fd, sec_start + sec_size, SEEK_SET); -		nr_ids = f_attr.ids.size / sizeof(u64); -		lseek(fd, f_attr.ids.offset, SEEK_SET); +	for_each_set_bit(feat, header->adds_features, HEADER_FEAT_BITS) { +		if (do_write_feat(fd, header, feat, &p, evlist)) +			perf_header__clear_feat(header, feat); +	} -		for (j = 0; j < nr_ids; j++) { -			if (perf_header__getbuffer64(self, fd, &f_id, sizeof(f_id))) -				goto out_errno; +	lseek(fd, sec_start, SEEK_SET); +	/* +	 * may write more than needed due to dropped feature, but +	 * this is okay, reader will skip the mising entries +	 */ +	err = do_write(fd, feat_sec, sec_size); +	if (err < 0) +		pr_debug("failed to write feature section\n"); +	free(feat_sec); +	return err; +} -			if (perf_header_attr__add_id(attr, f_id) < 0) { -				perf_header_attr__delete(attr); -				return -ENOMEM; -			} +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_session__write_header(struct perf_session *session, +			       struct perf_evlist *evlist, +			       int fd, bool at_exit) +{ +	struct perf_file_header f_header; +	struct perf_file_attr   f_attr; +	struct perf_header *header = &session->header; +	struct perf_evsel *evsel; +	u64 attr_offset; +	int err; + +	lseek(fd, sizeof(f_header), SEEK_SET); + +	evlist__for_each(session->evlist, evsel) { +		evsel->id_offset = lseek(fd, 0, SEEK_CUR); +		err = do_write(fd, evsel->id, evsel->ids * sizeof(u64)); +		if (err < 0) { +			pr_debug("failed to write perf header\n"); +			return err;  		} -		if (perf_header__add_attr(self, attr) < 0) { -			perf_header_attr__delete(attr); -			return -ENOMEM; +	} + +	attr_offset = lseek(fd, 0, SEEK_CUR); + +	evlist__for_each(evlist, evsel) { +		f_attr = (struct perf_file_attr){ +			.attr = evsel->attr, +			.ids  = { +				.offset = evsel->id_offset, +				.size   = evsel->ids * sizeof(u64), +			} +		}; +		err = do_write(fd, &f_attr, sizeof(f_attr)); +		if (err < 0) { +			pr_debug("failed to write perf header attribute\n"); +			return err;  		} +	} -		lseek(fd, tmp, SEEK_SET); +	if (!header->data_offset) +		header->data_offset = lseek(fd, 0, SEEK_CUR); +	header->feat_offset = header->data_offset + header->data_size; + +	if (at_exit) { +		err = perf_header__adds_write(header, evlist, fd); +		if (err < 0) +			return err;  	} -	if (f_header.event_types.size) { -		lseek(fd, f_header.event_types.offset, SEEK_SET); -		events = malloc(f_header.event_types.size); -		if (events == NULL) -			return -ENOMEM; -		if (perf_header__getbuffer64(self, fd, events, -					     f_header.event_types.size)) -			goto out_errno; -		event_count =  f_header.event_types.size / sizeof(struct perf_trace_event_type); +	f_header = (struct perf_file_header){ +		.magic	   = PERF_MAGIC, +		.size	   = sizeof(f_header), +		.attr_size = sizeof(f_attr), +		.attrs = { +			.offset = attr_offset, +			.size   = evlist->nr_entries * sizeof(f_attr), +		}, +		.data = { +			.offset = header->data_offset, +			.size	= header->data_size, +		}, +		/* event_types is ignored, store zeros */ +	}; + +	memcpy(&f_header.adds_features, &header->adds_features, sizeof(header->adds_features)); + +	lseek(fd, 0, SEEK_SET); +	err = do_write(fd, &f_header, sizeof(f_header)); +	if (err < 0) { +		pr_debug("failed to write perf header\n"); +		return err;  	} +	lseek(fd, header->data_offset + header->data_size, SEEK_SET); -	perf_header__process_sections(self, fd, perf_file_section__process); +	return 0; +} -	lseek(fd, self->data_offset, SEEK_SET); +static int perf_header__getbuffer64(struct perf_header *header, +				    int fd, void *buf, size_t size) +{ +	if (readn(fd, buf, size) <= 0) +		return -1; + +	if (header->needs_swap) +		mem_bswap_64(buf, size); -	self->frozen = 1;  	return 0; -out_errno: -	return -errno;  } -u64 perf_header__sample_type(struct perf_header *header) +int perf_header__process_sections(struct perf_header *header, int fd, +				  void *data, +				  int (*process)(struct perf_file_section *section, +						 struct perf_header *ph, +						 int feat, int fd, void *data))  { -	u64 type = 0; -	int i; +	struct perf_file_section *feat_sec, *sec; +	int nr_sections; +	int sec_size; +	int feat; +	int err; -	for (i = 0; i < header->attrs; i++) { -		struct perf_header_attr *attr = header->attr[i]; +	nr_sections = bitmap_weight(header->adds_features, HEADER_FEAT_BITS); +	if (!nr_sections) +		return 0; -		if (!type) -			type = attr->attr.sample_type; -		else if (type != attr->attr.sample_type) -			die("non matching sample_type"); +	feat_sec = sec = calloc(nr_sections, sizeof(*feat_sec)); +	if (!feat_sec) +		return -1; + +	sec_size = sizeof(*feat_sec) * nr_sections; + +	lseek(fd, header->feat_offset, SEEK_SET); + +	err = perf_header__getbuffer64(header, fd, feat_sec, sec_size); +	if (err < 0) +		goto out_free; + +	for_each_set_bit(feat, header->adds_features, HEADER_LAST_FEATURE) { +		err = process(sec++, header, feat, fd, data); +		if (err < 0) +			goto out_free;  	} +	err = 0; +out_free: +	free(feat_sec); +	return err; +} -	return type; +static const int attr_file_abi_sizes[] = { +	[0] = PERF_ATTR_SIZE_VER0, +	[1] = PERF_ATTR_SIZE_VER1, +	[2] = PERF_ATTR_SIZE_VER2, +	[3] = PERF_ATTR_SIZE_VER3, +	0, +}; + +/* + * In the legacy file format, the magic number is not used to encode endianness. + * hdr_sz was used to encode endianness. But given that hdr_sz can vary based + * on ABI revisions, we need to try all combinations for all endianness to + * detect the endianness. + */ +static int try_all_file_abis(uint64_t hdr_sz, struct perf_header *ph) +{ +	uint64_t ref_size, attr_size; +	int i; + +	for (i = 0 ; attr_file_abi_sizes[i]; i++) { +		ref_size = attr_file_abi_sizes[i] +			 + sizeof(struct perf_file_section); +		if (hdr_sz != ref_size) { +			attr_size = bswap_64(hdr_sz); +			if (attr_size != ref_size) +				continue; + +			ph->needs_swap = true; +		} +		pr_debug("ABI%d perf.data file detected, need_swap=%d\n", +			 i, +			 ph->needs_swap); +		return 0; +	} +	/* could not determine endianness */ +	return -1;  } -struct perf_event_attr * -perf_header__find_attr(u64 id, struct perf_header *header) +#define PERF_PIPE_HDR_VER0	16 + +static const size_t attr_pipe_abi_sizes[] = { +	[0] = PERF_PIPE_HDR_VER0, +	0, +}; + +/* + * In the legacy pipe format, there is an implicit assumption that endiannesss + * between host recording the samples, and host parsing the samples is the + * same. This is not always the case given that the pipe output may always be + * redirected into a file and analyzed on a different machine with possibly a + * different endianness and perf_event ABI revsions in the perf tool itself. + */ +static int try_all_pipe_abis(uint64_t hdr_sz, struct perf_header *ph)  { +	u64 attr_size;  	int i; +	for (i = 0 ; attr_pipe_abi_sizes[i]; i++) { +		if (hdr_sz != attr_pipe_abi_sizes[i]) { +			attr_size = bswap_64(hdr_sz); +			if (attr_size != hdr_sz) +				continue; + +			ph->needs_swap = true; +		} +		pr_debug("Pipe ABI%d perf.data file detected\n", i); +		return 0; +	} +	return -1; +} + +bool is_perf_magic(u64 magic) +{ +	if (!memcmp(&magic, __perf_magic1, sizeof(magic)) +		|| magic == __perf_magic2 +		|| magic == __perf_magic2_sw) +		return true; + +	return false; +} + +static int check_magic_endian(u64 magic, uint64_t hdr_sz, +			      bool is_pipe, struct perf_header *ph) +{ +	int ret; + +	/* check for legacy format */ +	ret = memcmp(&magic, __perf_magic1, sizeof(magic)); +	if (ret == 0) { +		ph->version = PERF_HEADER_VERSION_1; +		pr_debug("legacy perf.data format\n"); +		if (is_pipe) +			return try_all_pipe_abis(hdr_sz, ph); + +		return try_all_file_abis(hdr_sz, ph); +	}  	/* -	 * 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. +	 * the new magic number serves two purposes: +	 * - unique number to identify actual perf.data files +	 * - encode endianness of file  	 */ -	if (id == -1ULL) -		return NULL; -	for (i = 0; i < header->attrs; i++) { -		struct perf_header_attr *attr = header->attr[i]; -		int j; +	/* check magic number with one endianness */ +	if (magic == __perf_magic2) +		return 0; + +	/* check magic number with opposite endianness */ +	if (magic != __perf_magic2_sw) +		return -1; + +	ph->needs_swap = true; +	ph->version = PERF_HEADER_VERSION_2; + +	return 0; +} + +int perf_file_header__read(struct perf_file_header *header, +			   struct perf_header *ph, int fd) +{ +	ssize_t ret; + +	lseek(fd, 0, SEEK_SET); + +	ret = readn(fd, header, sizeof(*header)); +	if (ret <= 0) +		return -1; + +	if (check_magic_endian(header->magic, +			       header->attr_size, false, ph) < 0) { +		pr_debug("magic/endian check failed\n"); +		return -1; +	} -		for (j = 0; j < attr->ids; j++) { -			if (attr->id[j] == id) -				return &attr->attr; +	if (ph->needs_swap) { +		mem_bswap_64(header, offsetof(struct perf_file_header, +			     adds_features)); +	} + +	if (header->size != sizeof(*header)) { +		/* Support the previous format */ +		if (header->size == offsetof(typeof(*header), adds_features)) +			bitmap_zero(header->adds_features, HEADER_FEAT_BITS); +		else +			return -1; +	} else if (ph->needs_swap) { +		/* +		 * feature bitmap is declared as an array of unsigned longs -- +		 * not good since its size can differ between the host that +		 * generated the data file and the host analyzing the file. +		 * +		 * We need to handle endianness, but we don't know the size of +		 * the unsigned long where the file was generated. Take a best +		 * guess at determining it: try 64-bit swap first (ie., file +		 * created on a 64-bit host), and check if the hostname feature +		 * bit is set (this feature bit is forced on as of fbe96f2). +		 * If the bit is not, undo the 64-bit swap and try a 32-bit +		 * swap. If the hostname bit is still not set (e.g., older data +		 * file), punt and fallback to the original behavior -- +		 * clearing all feature bits and setting buildid. +		 */ +		mem_bswap_64(&header->adds_features, +			    BITS_TO_U64(HEADER_FEAT_BITS)); + +		if (!test_bit(HEADER_HOSTNAME, header->adds_features)) { +			/* unswap as u64 */ +			mem_bswap_64(&header->adds_features, +				    BITS_TO_U64(HEADER_FEAT_BITS)); + +			/* unswap as u32 */ +			mem_bswap_32(&header->adds_features, +				    BITS_TO_U32(HEADER_FEAT_BITS)); +		} + +		if (!test_bit(HEADER_HOSTNAME, header->adds_features)) { +			bitmap_zero(header->adds_features, HEADER_FEAT_BITS); +			set_bit(HEADER_BUILD_ID, header->adds_features);  		}  	} -	return NULL; +	memcpy(&ph->adds_features, &header->adds_features, +	       sizeof(ph->adds_features)); + +	ph->data_offset  = header->data.offset; +	ph->data_size	 = header->data.size; +	ph->feat_offset  = header->data.offset + header->data.size; +	return 0;  } -int event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id, -			   event__handler_t process, -			   struct perf_session *session) +static int perf_file_section__process(struct perf_file_section *section, +				      struct perf_header *ph, +				      int feat, int fd, void *data)  { -	event_t *ev; -	size_t size; -	int err; +	if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) { +		pr_debug("Failed to lseek to %" PRIu64 " offset for feature " +			  "%d, continuing...\n", section->offset, feat); +		return 0; +	} -	size = sizeof(struct perf_event_attr); -	size = ALIGN(size, sizeof(u64)); -	size += sizeof(struct perf_event_header); -	size += ids * sizeof(u64); +	if (feat >= HEADER_LAST_FEATURE) { +		pr_debug("unknown feature %d, continuing...\n", feat); +		return 0; +	} -	ev = malloc(size); +	if (!feat_ops[feat].process) +		return 0; -	ev->attr.attr = *attr; -	memcpy(ev->attr.id, id, ids * sizeof(u64)); +	return feat_ops[feat].process(section, ph, fd, data); +} -	ev->attr.header.type = PERF_RECORD_HEADER_ATTR; -	ev->attr.header.size = size; +static int perf_file_header__read_pipe(struct perf_pipe_file_header *header, +				       struct perf_header *ph, int fd, +				       bool repipe) +{ +	ssize_t ret; -	err = process(ev, session); +	ret = readn(fd, header, sizeof(*header)); +	if (ret <= 0) +		return -1; -	free(ev); +	if (check_magic_endian(header->magic, header->size, true, ph) < 0) { +		pr_debug("endian/magic failed\n"); +		return -1; +	} -	return err; +	if (ph->needs_swap) +		header->size = bswap_64(header->size); + +	if (repipe && do_write(STDOUT_FILENO, header, sizeof(*header)) < 0) +		return -1; + +	return 0;  } -int event__synthesize_attrs(struct perf_header *self, -			    event__handler_t process, -			    struct perf_session *session) +static int perf_header__read_pipe(struct perf_session *session)  { -	struct perf_header_attr	*attr; -	int i, err = 0; +	struct perf_header *header = &session->header; +	struct perf_pipe_file_header f_header; -	for (i = 0; i < self->attrs; i++) { -		attr = self->attr[i]; +	if (perf_file_header__read_pipe(&f_header, header, +					perf_data_file__fd(session->file), +					session->repipe) < 0) { +		pr_debug("incompatible file format\n"); +		return -EINVAL; +	} -		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 0; +} + +static int read_attr(int fd, struct perf_header *ph, +		     struct perf_file_attr *f_attr) +{ +	struct perf_event_attr *attr = &f_attr->attr; +	size_t sz, left; +	size_t our_sz = sizeof(f_attr->attr); +	ssize_t ret; + +	memset(f_attr, 0, sizeof(*f_attr)); + +	/* read minimal guaranteed structure */ +	ret = readn(fd, attr, PERF_ATTR_SIZE_VER0); +	if (ret <= 0) { +		pr_debug("cannot read %d bytes of header attr\n", +			 PERF_ATTR_SIZE_VER0); +		return -1;  	} -	return err; +	/* on file perf_event_attr size */ +	sz = attr->size; + +	if (ph->needs_swap) +		sz = bswap_32(sz); + +	if (sz == 0) { +		/* assume ABI0 */ +		sz =  PERF_ATTR_SIZE_VER0; +	} else if (sz > our_sz) { +		pr_debug("file uses a more recent and unsupported ABI" +			 " (%zu bytes extra)\n", sz - our_sz); +		return -1; +	} +	/* what we have not yet read and that we know about */ +	left = sz - PERF_ATTR_SIZE_VER0; +	if (left) { +		void *ptr = attr; +		ptr += PERF_ATTR_SIZE_VER0; + +		ret = readn(fd, ptr, left); +	} +	/* read perf_file_section, ids are read in caller */ +	ret = readn(fd, &f_attr->ids, sizeof(f_attr->ids)); + +	return ret <= 0 ? -1 : 0;  } -int event__process_attr(event_t *self, struct perf_session *session) +static int perf_evsel__prepare_tracepoint_event(struct perf_evsel *evsel, +						struct pevent *pevent)  { -	struct perf_header_attr *attr; -	unsigned int i, ids, n_ids; +	struct event_format *event; +	char bf[128]; -	attr = perf_header_attr__new(&self->attr.attr); -	if (attr == NULL) -		return -ENOMEM; +	/* already prepared */ +	if (evsel->tp_format) +		return 0; -	ids = self->header.size; -	ids -= (void *)&self->attr.id - (void *)self; -	n_ids = ids / sizeof(u64); +	if (pevent == NULL) { +		pr_debug("broken or missing trace data\n"); +		return -1; +	} -	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; -		} +	event = pevent_find_event(pevent, evsel->attr.config); +	if (event == NULL) +		return -1; + +	if (!evsel->name) { +		snprintf(bf, sizeof(bf), "%s:%s", event->system, event->name); +		evsel->name = strdup(bf); +		if (evsel->name == NULL) +			return -1;  	} -	if (perf_header__add_attr(&session->header, attr) < 0) { -		perf_header_attr__delete(attr); +	evsel->tp_format = event; +	return 0; +} + +static int perf_evlist__prepare_tracepoint_events(struct perf_evlist *evlist, +						  struct pevent *pevent) +{ +	struct perf_evsel *pos; + +	evlist__for_each(evlist, pos) { +		if (pos->attr.type == PERF_TYPE_TRACEPOINT && +		    perf_evsel__prepare_tracepoint_event(pos, pevent)) +			return -1; +	} + +	return 0; +} + +int perf_session__read_header(struct perf_session *session) +{ +	struct perf_data_file *file = session->file; +	struct perf_header *header = &session->header; +	struct perf_file_header	f_header; +	struct perf_file_attr	f_attr; +	u64			f_id; +	int nr_attrs, nr_ids, i, j; +	int fd = perf_data_file__fd(file); + +	session->evlist = perf_evlist__new(); +	if (session->evlist == NULL)  		return -ENOMEM; + +	if (perf_data_file__is_pipe(file)) +		return perf_header__read_pipe(session); + +	if (perf_file_header__read(&f_header, header, fd) < 0) +		return -EINVAL; + +	/* +	 * Sanity check that perf.data was written cleanly; data size is +	 * initialized to 0 and updated only if the on_exit function is run. +	 * If data size is still 0 then the file contains only partial +	 * information.  Just warn user and process it as much as it can. +	 */ +	if (f_header.data.size == 0) { +		pr_warning("WARNING: The %s file's data size field is 0 which is unexpected.\n" +			   "Was the 'perf record' command properly terminated?\n", +			   file->path);  	} -	perf_session__update_sample_type(session); +	nr_attrs = f_header.attrs.size / f_header.attr_size; +	lseek(fd, f_header.attrs.offset, SEEK_SET); + +	for (i = 0; i < nr_attrs; i++) { +		struct perf_evsel *evsel; +		off_t tmp; + +		if (read_attr(fd, header, &f_attr) < 0) +			goto out_errno; + +		if (header->needs_swap) +			perf_event__attr_swap(&f_attr.attr); + +		tmp = lseek(fd, 0, SEEK_CUR); +		evsel = perf_evsel__new(&f_attr.attr); + +		if (evsel == NULL) +			goto out_delete_evlist; + +		evsel->needs_swap = header->needs_swap; +		/* +		 * Do it before so that if perf_evsel__alloc_id fails, this +		 * entry gets purged too at perf_evlist__delete(). +		 */ +		perf_evlist__add(session->evlist, evsel); + +		nr_ids = f_attr.ids.size / sizeof(u64); +		/* +		 * We don't have the cpu and thread maps on the header, so +		 * for allocating the perf_sample_id table we fake 1 cpu and +		 * hattr->ids threads. +		 */ +		if (perf_evsel__alloc_id(evsel, 1, nr_ids)) +			goto out_delete_evlist; + +		lseek(fd, f_attr.ids.offset, SEEK_SET); + +		for (j = 0; j < nr_ids; j++) { +			if (perf_header__getbuffer64(header, fd, &f_id, sizeof(f_id))) +				goto out_errno; + +			perf_evlist__id_add(session->evlist, evsel, 0, j, f_id); +		} + +		lseek(fd, tmp, SEEK_SET); +	} + +	symbol_conf.nr_events = nr_attrs; + +	perf_header__process_sections(header, fd, &session->tevent, +				      perf_file_section__process); + +	if (perf_evlist__prepare_tracepoint_events(session->evlist, +						   session->tevent.pevent)) +		goto out_delete_evlist;  	return 0; +out_errno: +	return -errno; + +out_delete_evlist: +	perf_evlist__delete(session->evlist); +	session->evlist = NULL; +	return -ENOMEM;  } -int event__synthesize_event_type(u64 event_id, char *name, -				 event__handler_t process, -				 struct perf_session *session) +int perf_event__synthesize_attr(struct perf_tool *tool, +				struct perf_event_attr *attr, u32 ids, u64 *id, +				perf_event__handler_t process)  { -	event_t ev; -	size_t size = 0; -	int err = 0; +	union perf_event *ev; +	size_t size; +	int err; -	memset(&ev, 0, sizeof(ev)); +	size = sizeof(struct perf_event_attr); +	size = PERF_ALIGN(size, sizeof(u64)); +	size += sizeof(struct perf_event_header); +	size += ids * sizeof(u64); + +	ev = malloc(size); + +	if (ev == NULL) +		return -ENOMEM; + +	ev->attr.attr = *attr; +	memcpy(ev->attr.id, id, ids * sizeof(u64)); -	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->attr.header.type = PERF_RECORD_HEADER_ATTR; +	ev->attr.header.size = (u16)size; -	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); +	if (ev->attr.header.size == size) +		err = process(tool, ev, NULL, NULL); +	else +		err = -E2BIG; -	err = process(&ev, session); +	free(ev);  	return err;  } -int event__synthesize_event_types(event__handler_t process, -				  struct perf_session *session) +int perf_event__synthesize_attrs(struct perf_tool *tool, +				   struct perf_session *session, +				   perf_event__handler_t process)  { -	struct perf_trace_event_type *type; -	int i, err = 0; - -	for (i = 0; i < event_count; i++) { -		type = &events[i]; +	struct perf_evsel *evsel; +	int err = 0; -		err = event__synthesize_event_type(type->event_id, type->name, -						   process, session); +	evlist__for_each(session->evlist, evsel) { +		err = perf_event__synthesize_attr(tool, &evsel->attr, evsel->ids, +						  evsel->id, process);  		if (err) { -			pr_debug("failed to create perf header event type\n"); +			pr_debug("failed to create perf header attribute\n");  			return err;  		}  	} @@ -1089,79 +2901,138 @@ int event__synthesize_event_types(event__handler_t process,  	return err;  } -int event__process_event_type(event_t *self, -			      struct perf_session *session __unused) +int perf_event__process_attr(struct perf_tool *tool __maybe_unused, +			     union perf_event *event, +			     struct perf_evlist **pevlist)  { -	if (perf_header__push_event(self->event_type.event_type.event_id, -				    self->event_type.event_type.name) < 0) +	u32 i, ids, n_ids; +	struct perf_evsel *evsel; +	struct perf_evlist *evlist = *pevlist; + +	if (evlist == NULL) { +		*pevlist = evlist = perf_evlist__new(); +		if (evlist == NULL) +			return -ENOMEM; +	} + +	evsel = perf_evsel__new(&event->attr.attr); +	if (evsel == NULL) +		return -ENOMEM; + +	perf_evlist__add(evlist, evsel); + +	ids = event->header.size; +	ids -= (void *)&event->attr.id - (void *)event; +	n_ids = ids / sizeof(u64); +	/* +	 * We don't have the cpu and thread maps on the header, so +	 * for allocating the perf_sample_id table we fake 1 cpu and +	 * hattr->ids threads. +	 */ +	if (perf_evsel__alloc_id(evsel, 1, n_ids))  		return -ENOMEM; +	for (i = 0; i < n_ids; i++) { +		perf_evlist__id_add(evlist, evsel, 0, i, event->attr.id[i]); +	} + +	symbol_conf.nr_events = evlist->nr_entries; +  	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) +int perf_event__synthesize_tracing_data(struct perf_tool *tool, int fd, +					struct perf_evlist *evlist, +					perf_event__handler_t process)  { -	event_t ev; +	union perf_event ev; +	struct tracing_data *tdata;  	ssize_t size = 0, aligned_size = 0, padding; -	int err = 0; +	int err __maybe_unused = 0; + +	/* +	 * We are going to store the size of the data followed +	 * by the data contents. Since the fd descriptor is a pipe, +	 * we cannot seek back to store the size of the data once +	 * we know it. Instead we: +	 * +	 * - write the tracing data to the temp file +	 * - get/write the data size to pipe +	 * - write the tracing data from the temp file +	 *   to the pipe +	 */ +	tdata = tracing_data_get(&evlist->entries, fd, true); +	if (!tdata) +		return -1;  	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)); +	size = tdata->size; +	aligned_size = PERF_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); +	process(tool, &ev, NULL, NULL); + +	/* +	 * The put function will copy all the tracing data +	 * stored in temp file to the pipe. +	 */ +	tracing_data_put(tdata); -	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) +int perf_event__process_tracing_data(struct perf_tool *tool __maybe_unused, +				     union perf_event *event, +				     struct perf_session *session)  { -	ssize_t size_read, padding, size = self->tracing_data.size; -	off_t offset = lseek(session->fd, 0, SEEK_CUR); +	ssize_t size_read, padding, size = event->tracing_data.size; +	int fd = perf_data_file__fd(session->file); +	off_t offset = lseek(fd, 0, SEEK_CUR);  	char buf[BUFSIZ];  	/* setup for reading amidst mmap */ -	lseek(session->fd, offset + sizeof(struct tracing_data_event), +	lseek(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; +	size_read = trace_report(fd, &session->tevent, +				 session->repipe); +	padding = PERF_ALIGN(size_read, sizeof(u64)) - size_read; -	if (read(session->fd, buf, padding) < 0) -		die("reading input file"); +	if (readn(fd, buf, padding) < 0) { +		pr_err("%s: reading input file", __func__); +		return -1; +	}  	if (session->repipe) {  		int retw = write(STDOUT_FILENO, buf, padding); -		if (retw <= 0 || retw != padding) -			die("repiping tracing data padding"); +		if (retw <= 0 || retw != padding) { +			pr_err("%s: repiping tracing data padding", __func__); +			return -1; +		}  	} -	if (size_read + padding != size) -		die("tracing data size mismatch"); +	if (size_read + padding != size) { +		pr_err("%s: tracing data size mismatch", __func__); +		return -1; +	} + +	perf_evlist__prepare_tracepoint_events(session->evlist, +					       session->tevent.pevent);  	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) +int perf_event__synthesize_build_id(struct perf_tool *tool, +				    struct dso *pos, u16 misc, +				    perf_event__handler_t process, +				    struct machine *machine)  { -	event_t ev; +	union perf_event ev;  	size_t len;  	int err = 0; @@ -1171,7 +3042,7 @@ int event__synthesize_build_id(struct dso *pos, u16 misc,  	memset(&ev, 0, sizeof(ev));  	len = pos->long_name_len + 1; -	len = ALIGN(len, NAME_ALIGN); +	len = PERF_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; @@ -1179,16 +3050,17 @@ int event__synthesize_build_id(struct dso *pos, u16 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); +	err = process(tool, &ev, NULL, machine);  	return err;  } -int event__process_build_id(event_t *self, -			    struct perf_session *session) +int perf_event__process_build_id(struct perf_tool *tool __maybe_unused, +				 union perf_event *event, +				 struct perf_session *session)  { -	__event_process_build_id(&self->build_id, -				 self->build_id.filename, +	__event_process_build_id(&event->build_id, +				 event->build_id.filename,  				 session);  	return 0;  }  | 
