public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] perf: make perf.data more self-descriptive (v4)
@ 2011-09-07 19:10 Stephane Eranian
  2011-09-08 15:27 ` David Ahern
  2011-09-12 13:45 ` David Ahern
  0 siblings, 2 replies; 21+ messages in thread
From: Stephane Eranian @ 2011-09-07 19:10 UTC (permalink / raw)
  To: linux-kernel; +Cc: acme, peterz, mingo, dsahern


The goal of this patch is to include more information
about the host environment into the perf.data so it is
more self-descriptive. Overtime, profiles are captured
on various machines and it becomes hard to track what
was recorded, on what machine and when.

This patch provides a way to solve this by extending
the perf.data file with basic information about the
host machine. To add those extensions, we leverage
the feature bits capabilities of the perf.data format.
The change is backward compatible with existing perf.data
files.

We define the following useful new extensions:
 - HEADER_HOSTNAME: the hostname
 - HEADER_OSRELEASE: the kernel release number
 - HEADER_ARCH: the hw architecture
 - HEADER_CPUID: the cpu model description
 - HEADER_NRCPUS: number of online/avail cpus
 - HEADER_CMDLINE: perf command line
 - HEADER_VERSION: perf version
 - HEADER_TOPOLOGY: cpu topology
 - HEADER_EVENT_DESC: full event description (attrs)

The small granularity for the entries is to make it easier
to extend without breaking backward compatiblity. All entries
are provided as ASCII strings, easy to parse, except for the
event description.

In the second version, we dropped the -I option for the
perf record command. The extra information is systematically
captured. But it's still displayed optionally by perf report.
There are also a couple of cleanups of function prototypes
and global variables.

In the third version, we:
- fixed missing MIPS support for CPUID
- used u32 instead of int when writing integers to file
- fixed couple of bswap() bugs
- refactorize all write/print functions using function pointers
- improved write_cmdline() to include actual path to perf binary

In the fourth version, we:
- fixed a couple of int vs. u32 issues
- factorized HEADER_BUILD_ID, HEADER_TRACE_INFO
- added -I option to perf script report
- reverted change to evsel.c (unrelated to meta-data)
- modified write_*() to always write despite errors (adds_feature bit present)
- added HEADER_TOTAL_MEM: total memory installed on this system
- added HEADER_NUMA_TOPOLOGY: shows NUMA nodes and for each memory, cpu list
- renamed HEADER_TOPOLOGY to HEADER_CPU_TOPOLOGY


$ perf record -ecycles,instructions noploop 1
noploop for 1 seconds
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.098 MB perf.data (~4260 samples) ]
$ perf report -I --stdio
#
# captured on: Wed Sep  7 21:00:10 2011
# hostname : quad
# os release : 3.1.0-rc4-tip
# arch : x86_64
# cpuid : Intel(R) Core(TM)2 Quad CPU Q6600 @ 2.40GHz
# nrcpus online : 4
# nrcpus avail : 4
# event = cycles, type = 0, config = 0x0, config1 = 0x0, config2 = 0x0, excl_usr = 0, excl_kern = 0, id = { 536, 537, 538, 539 }
# event = instructions, type = 0, config = 0x1, config1 = 0x0, config2 = 0x0, excl_usr = 0, excl_kern = 0, id = { 540, 541, 542,
# cmdline : /home/eranian/perfmon/official/tip/build/tools/perf/perf record -ecycles,instructions noploop 1 
# perf version : 3.1.0-rc4
# CPU0   sibling cores  : 0-3
# CPU0   sibling threads: 0
# CPU1   sibling cores  : 0-3
# CPU1   sibling threads: 1
# CPU2   sibling cores  : 0-3
# CPU2   sibling threads: 2
# CPU3   sibling cores  : 0-3
# CPU3   sibling threads: 3
# total memory: 8105344 kB
# node0   meminfo : 8320608 kB
# node0   cpu list: 0-3
#
# Events: 995  cycles
#
# Overhead  Command      Shared Object                Symbol
# ........  .......  .................  ....................
#
    99.90%  noploop  noploop            [.] noploop
     0.10%  noploop  [kernel.kallsyms]  [k] trace_hardirqs_on
     0.00%  noploop  [kernel.kallsyms]  [k] lock_release
     0.00%  noploop  [kernel.kallsyms]  [k] intel_pmu_enable_all
...


Signed-off-by: Stephane Eranian <eranian@google.com>
---

diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt
index 04253c0..191e10e 100644
--- a/tools/perf/Documentation/perf-report.txt
+++ b/tools/perf/Documentation/perf-report.txt
@@ -134,6 +134,11 @@ OPTIONS
 	CPUs are specified with -: 0-2. Default is to report samples on all
 	CPUs.
 
+-I::
+--show-info::
+	Display information about the perf.data file, incl. hostname,
+	os release, perf version, machine desc.
+
 SEE ALSO
 --------
 linkperf:perf-stat[1]
diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt
index db01786..ebf8a23 100644
--- a/tools/perf/Documentation/perf-script.txt
+++ b/tools/perf/Documentation/perf-script.txt
@@ -188,6 +188,13 @@ OPTIONS
 	CPUs are specified with -: 0-2. Default is to report samples on all
 	CPUs.
 
+-I::
+--show-info::
+	Display information about the perf.data file, incl. hostname,
+	os release, perf version, machine desc. It can only be used with the
+	perf script report mode and it must be specified after the script name
+	like for instance: perf script report syscall-counts -I.
+
 SEE ALSO
 --------
 linkperf:perf-record[1], linkperf:perf-script-perl[1],
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 6b0519f..524ebbf 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -513,6 +513,18 @@ static int __cmd_record(int argc, const char **argv)
 	if (have_tracepoints(&evsel_list->entries))
 		perf_header__set_feat(&session->header, HEADER_TRACE_INFO);
 
+	perf_header__set_feat(&session->header, HEADER_HOSTNAME);
+	perf_header__set_feat(&session->header, HEADER_OSRELEASE);
+	perf_header__set_feat(&session->header, HEADER_ARCH);
+	perf_header__set_feat(&session->header, HEADER_CPUID);
+	perf_header__set_feat(&session->header, HEADER_NRCPUS);
+	perf_header__set_feat(&session->header, HEADER_EVENT_DESC);
+	perf_header__set_feat(&session->header, HEADER_CMDLINE);
+	perf_header__set_feat(&session->header, HEADER_VERSION);
+	perf_header__set_feat(&session->header, HEADER_CPU_TOPOLOGY);
+	perf_header__set_feat(&session->header, HEADER_TOTAL_MEM);
+	perf_header__set_feat(&session->header, HEADER_NUMA_TOPOLOGY);
+
 	/* 512 kiB: default amount of unprivileged mlocked memory */
 	if (mmap_pages == UINT_MAX)
 		mmap_pages = (512 * 1024) / page_size;
@@ -782,6 +794,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
 	int err = -ENOMEM;
 	struct perf_evsel *pos;
 
+	perf_header__set_cmdline(argc, argv);
+
 	evsel_list = perf_evlist__new(NULL, NULL);
 	if (evsel_list == NULL)
 		return -ENOMEM;
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index d7ff277..7721030 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -40,6 +40,7 @@ static char		const *input_name = "perf.data";
 static bool		force, use_tui, use_stdio;
 static bool		hide_unresolved;
 static bool		dont_use_callchains;
+static bool		show_info;
 
 static bool		show_threads;
 static struct perf_read_values	show_threads_values;
@@ -276,6 +277,9 @@ static int __cmd_report(void)
 			goto out_delete;
 	}
 
+	if (show_info)
+		perf_session__fprintf_info(session, stdout);
+
 	if (show_threads)
 		perf_read_values_init(&show_threads_values);
 
@@ -487,6 +491,8 @@ static const struct option options[] = {
 	OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
 		    "Look for files with symbols relative to this directory"),
 	OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"),
+	OPT_BOOLEAN('I', "show-info", &show_info,
+			"display information about perf.data file"),
 	OPT_END()
 };
 
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
index 09024ec..dac86ab 100644
--- a/tools/perf/builtin-script.c
+++ b/tools/perf/builtin-script.c
@@ -22,6 +22,7 @@ static u64			last_timestamp;
 static u64			nr_unordered;
 extern const struct option	record_options[];
 static bool			no_callchain;
+static bool			show_info = false;
 static const char		*cpu_list;
 static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
 
@@ -1083,7 +1084,8 @@ static const struct option options[] = {
 		     "comma separated output fields prepend with 'type:'. Valid types: hw,sw,trace,raw. Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso,addr",
 		     parse_output_fields),
 	OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"),
-
+	OPT_BOOLEAN('I', "show-info", &show_info,
+			"display host information from perf.data file"),
 	OPT_END()
 };
 
@@ -1268,6 +1270,9 @@ int cmd_script(int argc, const char **argv, const char *prefix __used)
 			return -1;
 	}
 
+	if (show_info)
+		perf_session__fprintf_info(session, stdout);
+
 	if (!no_callchain)
 		symbol_conf.use_callchain = true;
 	else
diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h
index 4702e24..b382bd5 100644
--- a/tools/perf/builtin.h
+++ b/tools/perf/builtin.h
@@ -4,7 +4,6 @@
 #include "util/util.h"
 #include "util/strbuf.h"
 
-extern const char perf_version_string[];
 extern const char perf_usage_string[];
 extern const char perf_more_info_string[];
 
diff --git a/tools/perf/perf.h b/tools/perf/perf.h
index a5fc660..08b0b5e 100644
--- a/tools/perf/perf.h
+++ b/tools/perf/perf.h
@@ -9,18 +9,21 @@ void get_term_dimensions(struct winsize *ws);
 #include "../../arch/x86/include/asm/unistd.h"
 #define rmb()		asm volatile("lock; addl $0,0(%%esp)" ::: "memory")
 #define cpu_relax()	asm volatile("rep; nop" ::: "memory");
+#define CPUINFO_PROC	"model name"
 #endif
 
 #if defined(__x86_64__)
 #include "../../arch/x86/include/asm/unistd.h"
 #define rmb()		asm volatile("lfence" ::: "memory")
 #define cpu_relax()	asm volatile("rep; nop" ::: "memory");
+#define CPUINFO_PROC	"model name"
 #endif
 
 #ifdef __powerpc__
 #include "../../arch/powerpc/include/asm/unistd.h"
 #define rmb()		asm volatile ("sync" ::: "memory")
 #define cpu_relax()	asm volatile ("" ::: "memory");
+#define CPUINFO_PROC	"cpu"
 #endif
 
 #ifdef __s390__
@@ -37,30 +40,35 @@ void get_term_dimensions(struct winsize *ws);
 # define rmb()		asm volatile("" ::: "memory")
 #endif
 #define cpu_relax()	asm volatile("" ::: "memory")
+#define CPUINFO_PROC	"cpu type"
 #endif
 
 #ifdef __hppa__
 #include "../../arch/parisc/include/asm/unistd.h"
 #define rmb()		asm volatile("" ::: "memory")
 #define cpu_relax()	asm volatile("" ::: "memory");
+#define CPUINFO_PROC	"cpu"
 #endif
 
 #ifdef __sparc__
 #include "../../arch/sparc/include/asm/unistd.h"
 #define rmb()		asm volatile("":::"memory")
 #define cpu_relax()	asm volatile("":::"memory")
+#define CPUINFO_PROC	"cpu"
 #endif
 
 #ifdef __alpha__
 #include "../../arch/alpha/include/asm/unistd.h"
 #define rmb()		asm volatile("mb" ::: "memory")
 #define cpu_relax()	asm volatile("" ::: "memory")
+#define CPUINFO_PROC	"cpu model"
 #endif
 
 #ifdef __ia64__
 #include "../../arch/ia64/include/asm/unistd.h"
 #define rmb()		asm volatile ("mf" ::: "memory")
 #define cpu_relax()	asm volatile ("hint @pause" ::: "memory")
+#define CPUINFO_PROC	"model name"
 #endif
 
 #ifdef __arm__
@@ -71,6 +79,7 @@ void get_term_dimensions(struct winsize *ws);
  */
 #define rmb()		((void(*)(void))0xffff0fa0)()
 #define cpu_relax()	asm volatile("":::"memory")
+#define CPUINFO_PROC	"Processor"
 #endif
 
 #ifdef __mips__
@@ -83,6 +92,7 @@ void get_term_dimensions(struct winsize *ws);
 				: /* no input */			\
 				: "memory")
 #define cpu_relax()	asm volatile("" ::: "memory")
+#define CPUINFO_PROC	"cpu model"
 #endif
 
 #include <time.h>
@@ -171,5 +181,6 @@ struct ip_callchain {
 };
 
 extern bool perf_host, perf_guest;
+extern const char perf_version_string[];
 
 #endif
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index b6c1ad1..73fa59e 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -7,6 +7,7 @@
 #include <stdlib.h>
 #include <linux/list.h>
 #include <linux/kernel.h>
+#include <sys/utsname.h>
 
 #include "evlist.h"
 #include "evsel.h"
@@ -17,12 +18,19 @@
 #include "session.h"
 #include "symbol.h"
 #include "debug.h"
+#include "cpumap.h"
 
 static bool no_buildid_cache = false;
 
 static int event_count;
 static struct perf_trace_event_type *events;
 
+static u32 header_argc;
+static const char **header_argv;
+
+static int dsos__write_buildid_table(struct perf_header *header, int fd);
+static int perf_session__cache_build_ids(struct perf_session *session);
+
 int perf_header__push_event(u64 id, const char *name)
 {
 	if (strlen(name) > MAX_EVENT_NAME)
@@ -110,6 +118,843 @@ static int write_padded(int fd, const void *bf, size_t count,
 	return err;
 }
 
+static int do_write_string(int fd, const char *str)
+{
+	u32 len, olen;
+	int ret;
+
+	olen = strlen(str) + 1;
+	len = ALIGN(olen, NAME_ALIGN);
+
+	/* write len, incl. \0 */
+	ret = do_write(fd, &len, sizeof(len));
+	if (ret < 0)
+		return ret;
+
+	return write_padded(fd, str, olen, len);
+}
+
+static char *do_read_string(int fd, struct perf_header *ph)
+{
+	ssize_t sz, ret;
+	u32 len;
+	char *buf;
+
+	sz = read(fd, &len, sizeof(len));
+	if (sz < (ssize_t)sizeof(len))
+		return NULL;
+
+	if (ph->needs_swap)
+		len = bswap_32(len);
+
+	buf = malloc(len);
+	if (!buf)
+		return NULL;
+
+	ret = read(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;
+	}
+
+	free(buf);
+	return NULL;
+}
+
+int
+perf_header__set_cmdline(int argc, const char **argv)
+{
+	int i;
+
+	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;
+}
+
+static int write_trace_info(int fd, struct perf_header *h __used,
+			    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 __used)
+{
+	struct perf_session *session;
+	int err;
+
+	session = container_of(h, struct perf_session, header);
+
+	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);
+
+	return 0;
+}
+
+static int write_hostname(int fd, struct perf_header *h __used,
+			  struct perf_evlist *evlist __used)
+{
+	struct utsname uts;
+	int ret;
+
+	ret = uname(&uts);
+	if (ret < 0)
+		return -1;
+
+	return do_write_string(fd, uts.nodename);
+}
+
+static int write_osrelease(int fd, struct perf_header *h __used,
+			   struct perf_evlist *evlist __used)
+{
+	struct utsname uts;
+	int ret;
+
+	ret = uname(&uts);
+	if (ret < 0)
+		return -1;
+
+	return do_write_string(fd, uts.release);
+}
+
+static int write_arch(int fd, struct perf_header *h __used,
+		      struct perf_evlist *evlist __used)
+{
+	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 __used,
+			 struct perf_evlist *evlist __used)
+{
+	return do_write_string(fd, perf_version_string);
+}
+
+static int write_cpuid(int fd, struct perf_header *h __used,
+		       struct perf_evlist *evlist __used)
+{
+#ifndef CPUINFO_PROC
+#define CPUINFO_PROC NULL
+#endif
+	FILE *file;
+	char buf[256];
+	char *s, *p;
+	const char *search = CPUINFO_PROC;
+
+	file = fopen("/proc/cpuinfo", "r");
+	if (!file)
+		return -1;
+
+	if (search) {
+		while (fgets(buf, sizeof(buf), file)) {
+			if (strstr(buf, search))
+				goto found;
+		}
+	}
+	strcpy(buf, "unknown type");
+found:
+	fclose(file);
+
+	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++));
+		}
+		p++;
+	}
+	return do_write_string(fd, s);
+}
+
+static int write_nrcpus(int fd, struct perf_header *h __used,
+			struct perf_evlist *evlist __used)
+{
+	long nr;
+	u32 nrc, nra;
+	int ret;
+
+	nr = sysconf(_SC_NPROCESSORS_CONF);
+	if (nr < 0)
+		return -1;
+
+	nrc = (u32)(nr & UINT_MAX);
+
+	ret = sysconf(_SC_NPROCESSORS_ONLN);
+	if (ret < 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));
+}
+
+static int write_event_desc(int fd, struct perf_header *h __used,
+			    struct perf_evlist *evlist)
+{
+	struct perf_evsel *attr;
+	u32 nre = 0, nri, sz;
+	int ret;
+
+	list_for_each_entry(attr, &evlist->entries, node)
+		nre++;
+
+	/*
+	 * 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(attr->attr);
+	ret = do_write(fd, &sz, sizeof(sz));
+	if (ret < 0)
+		return ret;
+
+	list_for_each_entry(attr, &evlist->entries, node) {
+
+		ret = do_write(fd, &attr->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 = attr->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, attr->name);
+		if (ret < 0)
+			return ret;
+		/*
+		 * write unique ids for this event
+		 */
+		ret = do_write(fd, attr->id, attr->ids * sizeof(u64));
+		if (ret < 0)
+			return ret;
+	}
+	return 0;
+}
+
+static int write_cmdline(int fd, struct perf_header *h __used,
+			 struct perf_evlist *evlist __used)
+{
+	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;
+}
+
+static const char *topo_fmt[] = {
+	"/sys/devices/system/cpu/cpu%d/topology/core_siblings_list",
+	"/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list",
+	NULL
+};
+
+static int write_topo_cpu(int fd, int cpu)
+{
+	FILE *fp;
+	char filename[MAXPATHLEN];
+	char buf[MAXPATHLEN], *p, *c;
+	const char **q = topo_fmt;
+	int ret;
+
+	while (*q) {
+
+		sprintf(filename, *q, cpu);
+
+		fp = fopen(filename, "r");
+		if (!fp)
+			return -1;
+
+		c = fgets(buf, sizeof(buf), fp);
+		if (!c) {
+			fclose(fp);
+			return -1;
+		}
+
+		p = strchr(buf, '\n');
+		if (p)
+			*p = '\0';
+
+		fclose(fp);
+
+		ret = do_write_string(fd, c);
+		if (ret < 0)
+			return ret;
+		q++;
+	}
+	return 0;
+}
+
+static int write_cpu_topology(int fd, struct perf_header *h __used,
+			  struct perf_evlist *evlist __used)
+{
+	u32 nr, i;
+	long ncpus;
+	int ret;
+
+	ncpus = sysconf(_SC_NPROCESSORS_CONF);
+	if (ncpus < 0)
+		return -1;
+
+	/* hopefully, this will keep compilers happy about the cast */
+	nr = (u32)(ncpus & UINT_MAX);
+
+	ret = do_write(fd, &nr, sizeof(nr));
+	if (ret < 0)
+		return -1;
+
+	for (i = 0; i < nr; i++) {
+		ret = do_write(fd, &i, sizeof(i));
+		if (ret < 0)
+			return ret;
+
+		ret = write_topo_cpu(fd, i);
+		if (ret < 0)
+			return ret;
+	}
+	return 0;
+}
+
+static int write_total_mem(int fd, struct perf_header *h __used,
+			  struct perf_evlist *evlist __used)
+{
+	char buf[MAXPATHLEN], *c;
+	FILE *fp;
+	int ret = -1, n;
+	uint64_t mem;
+
+	fp = fopen("/proc/meminfo", "r");
+	if (!fp)
+		return -1;
+
+	while ((c = fgets(buf, sizeof(buf), fp))) {
+		c = strstr(buf, "MemTotal:");
+		if (c)
+			break;
+	}
+	fclose(fp);
+
+	n = sscanf(buf, "%*s %"PRIu64, &mem);
+	if (n == 1)
+		ret = do_write(fd, &mem, sizeof(mem));
+
+	return ret;
+}
+
+static int write_topo_node(int fd, int node)
+{
+	char filename[MAXPATHLEN];
+	char buf[MAXPATHLEN], *p, *c;
+	FILE *fp;
+	uint64_t mem;
+	int ret;
+
+	sprintf(filename, "/sys/devices/system/node/node%d/meminfo", node);
+	fp = fopen(filename, "r");
+	if (!fp)
+		return -1;
+
+	while ((c = fgets(buf, sizeof(buf), fp))) {
+		c = strstr(buf, "MemTotal:");
+		if (c)
+			break;
+	}
+	fclose(fp);
+	if (!c)
+		return -1;
+	ret = sscanf(buf, "%*s %*d %*s %"PRIu64, &mem);
+	if (ret != 1)
+		return -1;
+
+	ret = do_write(fd, &mem, sizeof(mem));
+	if (ret < 0)
+		return -1;
+
+	sprintf(filename, "/sys/devices/system/node/node%d/cpulist", node);
+	fp = fopen(filename, "r");
+	if (!fp)
+		return -1;
+
+	c = fgets(buf, sizeof(buf), fp);
+	fclose(fp);
+	if (!c)
+		return -1;
+
+	p = strchr(buf, '\n');
+	if (p)
+		*p = '\0';
+
+	return do_write_string(fd, c);
+}
+
+static int write_numa_topology(int fd, struct perf_header *h __used,
+			  struct perf_evlist *evlist __used)
+{
+	char buf[MAXPATHLEN];
+	FILE *fp;
+	struct cpu_map *node_map;
+	char *c;
+	u32 nr, i, j;
+	int ret;
+
+	fp = fopen("/sys/devices/system/node/online", "r");
+	if (!fp)
+		return -1;
+
+	c = fgets(buf, sizeof(buf), fp);
+	fclose(fp);
+	if (!c)
+		return -1;
+
+	c = strchr(buf, '\n');
+	if (c)
+		*c = '\0';
+
+	node_map = cpu_map__new(buf);
+	if (!node_map)
+		return -1;
+
+	nr = (u32)node_map->nr;
+	ret = do_write(fd, &nr, sizeof(nr));
+	if (ret < 0)
+		return -1;
+
+	for (i = 0; i < nr; i++) {
+		j = (u32)node_map->map[i];
+		ret = do_write(fd, &j, sizeof(j));
+		if (ret < 0)
+			goto error;
+
+		ret = write_topo_node(fd, i);
+		if (ret < 0)
+			goto error;
+	}
+	ret = 0;
+error:
+	free(node_map);
+	return ret;
+}
+
+static void print_hostname(struct perf_header *ph, int fd, FILE *fp)
+{
+	char *str = do_read_string(fd, ph);
+	fprintf(fp, "# hostname : %s\n", str);
+	free(str);
+}
+
+static void print_osrelease(struct perf_header *ph, int fd, FILE *fp)
+{
+	char *str = do_read_string(fd, ph);
+	fprintf(fp, "# os release : %s\n", str);
+	free(str);
+}
+
+static void print_arch(struct perf_header *ph, int fd, FILE *fp)
+{
+	char *str = do_read_string(fd, ph);
+	fprintf(fp, "# arch : %s\n", str);
+	free(str);
+}
+
+static void print_cpuid(struct perf_header *ph, int fd, FILE *fp)
+{
+	char *str = do_read_string(fd, ph);
+	fprintf(fp, "# cpuid : %s\n", str);
+	free(str);
+}
+
+static void print_nrcpus(struct perf_header *ph, int fd, FILE *fp)
+{
+	ssize_t ret;
+	u32 nr;
+
+	ret = read(fd, &nr, sizeof(nr));
+	if (ret != (ssize_t)sizeof(nr))
+		nr = -1; /* interpreted as error */
+
+	if (ph->needs_swap)
+		nr = bswap_32(nr);
+
+	fprintf(fp, "# nrcpus online : %u\n", nr);
+
+	ret = read(fd, &nr, sizeof(nr));
+	if (ret != (ssize_t)sizeof(nr))
+		nr = -1; /* interpreted as error */
+
+	if (ph->needs_swap)
+		nr = bswap_32(nr);
+
+	fprintf(fp, "# nrcpus avail : %u\n", nr);
+}
+
+static void print_version(struct perf_header *ph, int fd, FILE *fp)
+{
+	char *str = do_read_string(fd, ph);
+	fprintf(fp, "# perf version : %s\n", str);
+	free(str);
+}
+
+static void print_cmdline(struct perf_header *ph, int fd, FILE *fp)
+{
+	ssize_t ret;
+	char *str;
+	u32 nr, i;
+
+	ret = read(fd, &nr, sizeof(nr));
+	if (ret != (ssize_t)sizeof(nr)) {
+		fprintf(fp, "# cmdline : unknown\n");
+		return;
+	}
+
+	if (ph->needs_swap)
+		nr = bswap_32(nr);
+
+	fprintf(fp, "# cmdline : ");
+
+	for (i = 0; i < nr; i++) {
+		str = do_read_string(fd, ph);
+		fprintf(fp, "%s ", str);
+		free(str);
+	}
+	fputc('\n', fp);
+}
+
+static void print_cpu_topology(struct perf_header *ph, int fd, FILE *fp)
+{
+	ssize_t ret;
+	u32 nr, c, i;
+	char *str;
+
+	ret = read(fd, &nr, sizeof(nr));
+	if (ret != (ssize_t)sizeof(nr)) {
+		fprintf(fp, "# cpu topology  : not available\n");
+		return;
+	}
+
+	if (ph->needs_swap)
+		nr = bswap_32(nr);
+
+	for (i = 0; i < nr; i++) {
+
+		ret = read(fd, &c, sizeof(c));
+		if (ret != (ssize_t)sizeof(c))
+			c = -1; /* interpreted as error */
+
+		if (ph->needs_swap)
+			c = bswap_32(c);
+
+		str = do_read_string(fd, ph);
+		fprintf(fp, "# CPU%u sibling cores  : %s\n", c, str);
+		free(str);
+
+		str = do_read_string(fd, ph);
+		fprintf(fp, "# CPU%u sibling threads: %s\n", c, str);
+		free(str);
+	}
+}
+
+static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
+{
+	struct perf_event_attr attr;
+	uint64_t id;
+	void *buf = NULL;
+	char *str;
+	u32 nre, sz, nr, i, j, msz;
+	int ret;
+
+	/* number of events */
+	ret = read(fd, &nre, sizeof(nre));
+	if (ret != (ssize_t)sizeof(nre))
+		goto error;
+
+	if (ph->needs_swap)
+		nre = bswap_32(nre);
+
+	ret = read(fd, &sz, sizeof(sz));
+	if (ret != (ssize_t)sizeof(sz))
+		goto error;
+
+	if (ph->needs_swap)
+		sz = bswap_32(sz);
+
+	/*
+	 * ensure it is at least to our ABI rev
+	 */
+	if (sz < (u32)sizeof(attr))
+		goto error;
+
+	memset(&attr, 0, sizeof(attr));
+
+	/* read entire region to sync up to next field */
+	buf = malloc(sz);
+	if (!buf)
+		goto error;
+
+	msz = sizeof(attr);
+	if (sz < msz)
+		msz = sz;
+
+	for (i = 0 ; i < nre; i++) {
+
+		ret = read(fd, buf, sz);
+		if (ret != (ssize_t)sz)
+			goto error;
+
+		if (ph->needs_swap)
+			perf_event__attr_swap(buf);
+
+		memcpy(&attr, buf, msz);
+
+		ret = read(fd, &nr, sizeof(nr));
+		if (ret != (ssize_t)sizeof(nr))
+			goto error;
+
+		if (ph->needs_swap)
+			nr = bswap_32(nr);
+
+		str = do_read_string(fd, ph);
+		fprintf(fp, "# event = %s, ", str);
+		free(str);
+
+		fprintf(fp, "type = %d, config = 0x%"PRIx64
+			    ", config1 = 0x%"PRIx64", config2 = 0x%"PRIx64,
+				attr.type,
+				(u64)attr.config,
+				(u64)attr.config1,
+				(u64)attr.config2);
+
+		fprintf(fp, ", excl_usr = %d, excl_kern = %d",
+				attr.exclude_user,
+				attr.exclude_kernel);
+
+		if (nr)
+			fprintf(fp, ", id = {");
+
+		for (j = 0 ; j < nr; j++) {
+			ret = read(fd, &id, sizeof(id));
+			if (ret != (ssize_t)sizeof(id))
+				goto error;
+
+			if (ph->needs_swap)
+				id = bswap_64(id);
+
+			if (j)
+				fputc(',', fp);
+
+			fprintf(fp, " %"PRIu64, id);
+		}
+		if (nr && j == nr)
+			fprintf(fp, " }");
+		fputc('\n', fp);
+	}
+	free(buf);
+	return;
+error:
+	fprintf(fp, "# event desc: not available or unable to read\n");
+}
+
+static void print_total_mem(struct perf_header *h __used, int fd, FILE *fp)
+{
+	uint64_t mem;
+	ssize_t ret;
+
+	ret = read(fd, &mem, sizeof(mem));
+	if (ret != sizeof(mem))
+		goto error;
+
+	if (h->needs_swap)
+		mem = bswap_64(mem);
+
+	fprintf(fp, "# total memory: %"PRIu64" kB\n", mem);
+	return;
+error:
+	fprintf(fp, "# total memory: unknown\n");
+}
+
+static void print_numa_topology(struct perf_header *h __used, int fd, FILE *fp)
+{
+	ssize_t ret;
+	u32 nr, c, i;
+	char *str;
+	uint64_t mem;
+
+	/* nr nodes */
+	ret = read(fd, &nr, sizeof(nr));
+	if (ret != (ssize_t)sizeof(nr))
+		goto error;
+
+	if (h->needs_swap)
+		nr = bswap_32(nr);
+
+	for (i = 0; i < nr; i++) {
+
+		/* node number */
+		ret = read(fd, &c, sizeof(c));
+		if (ret != (ssize_t)sizeof(c))
+			goto error;
+
+		if (h->needs_swap)
+			c = bswap_32(c);
+
+		ret = read(fd, &mem, sizeof(mem));
+		if (ret != sizeof(mem))
+			goto error;
+
+		if (h->needs_swap)
+			mem = bswap_64(mem);
+
+		fprintf(fp, "# node%u meminfo : %"PRIu64" kB\n", c, mem);
+
+		str = do_read_string(fd, h);
+		fprintf(fp, "# node%u cpu list: %s\n", c, str);
+		free(str);
+	}
+	return;
+error:
+	fprintf(fp, "# numa topology : not available\n");
+}
+
+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);
+};
+
+#define FEAT_OP(n, w, p) [n] = { .write = w, .print = p }
+
+static struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
+	FEAT_OP(HEADER_TRACE_INFO, write_trace_info, NULL),
+	FEAT_OP(HEADER_BUILD_ID, write_build_id, NULL),
+	FEAT_OP(HEADER_HOSTNAME, write_hostname, print_hostname),
+	FEAT_OP(HEADER_OSRELEASE, write_osrelease, print_osrelease),
+	FEAT_OP(HEADER_ARCH, write_arch, print_arch),
+	FEAT_OP(HEADER_CPUID, write_cpuid, print_cpuid),
+	FEAT_OP(HEADER_NRCPUS, write_nrcpus, print_nrcpus),
+	FEAT_OP(HEADER_EVENT_DESC, write_event_desc, print_event_desc),
+	FEAT_OP(HEADER_CMDLINE, write_cmdline, print_cmdline),
+	FEAT_OP(HEADER_VERSION, write_version, print_version),
+	FEAT_OP(HEADER_CPU_TOPOLOGY, write_cpu_topology, print_cpu_topology),
+	FEAT_OP(HEADER_TOTAL_MEM, write_total_mem, print_total_mem),
+	FEAT_OP(HEADER_NUMA_TOPOLOGY, write_numa_topology, print_numa_topology),
+};
+
+static int perf_header_fprintf_info(struct perf_file_section *section,
+				    struct perf_header *ph,
+				    int feat, int fd, void *data)
+{
+	FILE *fp = data;
+
+	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_TRACE_INFO || feat >= HEADER_LAST_FEATURE) {
+		pr_warning("unknown feature %d\n", feat);
+		return -1;
+	}
+
+	if (feat_ops[feat].print)
+		feat_ops[feat].print(ph, fd, fp);
+
+	return 0;
+}
+
+int perf_header__fprintf_info(struct perf_session *session, FILE *fp __used)
+{
+	int fd = session->fd;
+	struct perf_header *header = &session->header;
+	perf_header__process_sections(header, fd, fp, perf_header_fprintf_info);
+	return 0;
+}
+
 #define dsos__for_each_with_build_id(pos, head)	\
 	list_for_each_entry(pos, head, node)	\
 		if (!pos->has_build_id)		\
@@ -356,15 +1201,41 @@ static bool perf_session__read_build_ids(struct perf_session *session, bool with
 	return ret;
 }
 
+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_header__has_feat(h, type)) {
+
+		(*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;
+}
+
 static int perf_header__adds_write(struct perf_header *header,
 				   struct perf_evlist *evlist, int fd)
 {
 	int nr_sections;
 	struct perf_session *session;
-	struct perf_file_section *feat_sec;
+	struct perf_file_section *feat_sec, *p;
 	int sec_size;
 	u64 sec_start;
-	int idx = 0, err;
+	int err;
 
 	session = container_of(header, struct perf_session, header);
 
@@ -376,7 +1247,7 @@ static int perf_header__adds_write(struct perf_header *header,
 	if (!nr_sections)
 		return 0;
 
-	feat_sec = calloc(sizeof(*feat_sec), nr_sections);
+	feat_sec = p = calloc(sizeof(*feat_sec), nr_sections);
 	if (feat_sec == NULL)
 		return -ENOMEM;
 
@@ -385,36 +1256,65 @@ static int perf_header__adds_write(struct perf_header *header,
 	sec_start = header->data_offset + header->data_size;
 	lseek(fd, sec_start + sec_size, SEEK_SET);
 
-	if (perf_header__has_feat(header, HEADER_TRACE_INFO)) {
-		struct perf_file_section *trace_sec;
-
-		trace_sec = &feat_sec[idx++];
+	err = do_write_feat(fd, header, HEADER_TRACE_INFO, &p, evlist);
+	if (err)
+		goto out_free;
 
-		/* Write trace info */
-		trace_sec->offset = lseek(fd, 0, SEEK_CUR);
-		read_tracing_data(fd, &evlist->entries);
-		trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset;
+	err = do_write_feat(fd, header, HEADER_BUILD_ID, &p, evlist);
+	if (err) {
+		perf_header__clear_feat(header, HEADER_BUILD_ID);
+		goto out_free;
 	}
 
-	if (perf_header__has_feat(header, HEADER_BUILD_ID)) {
-		struct perf_file_section *buildid_sec;
+	err = do_write_feat(fd, header, HEADER_HOSTNAME, &p, evlist);
+	if (err)
+		perf_header__clear_feat(header, HEADER_HOSTNAME);
 
-		buildid_sec = &feat_sec[idx++];
+	err = do_write_feat(fd, header, HEADER_OSRELEASE, &p, evlist);
+	if (err)
+		perf_header__clear_feat(header, HEADER_OSRELEASE);
 
-		/* Write build-ids */
-		buildid_sec->offset = lseek(fd, 0, SEEK_CUR);
-		err = dsos__write_buildid_table(header, 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;
-		if (!no_buildid_cache)
-			perf_session__cache_build_ids(session);
-	}
+	err = do_write_feat(fd, header, HEADER_ARCH, &p, evlist);
+	if (err)
+		perf_header__clear_feat(header, HEADER_ARCH);
+
+	err = do_write_feat(fd, header, HEADER_CPUID, &p, evlist);
+	if (err)
+		perf_header__clear_feat(header, HEADER_CPUID);
+
+	err = do_write_feat(fd, header, HEADER_NRCPUS, &p, evlist);
+	if (err)
+		perf_header__clear_feat(header, HEADER_NRCPUS);
+
+	err = do_write_feat(fd, header, HEADER_EVENT_DESC, &p, evlist);
+	if (err)
+		perf_header__clear_feat(header, HEADER_EVENT_DESC);
+
+	err = do_write_feat(fd, header, HEADER_CMDLINE, &p, evlist);
+	if (err)
+		perf_header__clear_feat(header, HEADER_CMDLINE);
+
+	err = do_write_feat(fd, header, HEADER_VERSION, &p, evlist);
+	if (err)
+		perf_header__clear_feat(header, HEADER_VERSION);
+
+	err = do_write_feat(fd, header, HEADER_CPU_TOPOLOGY, &p, evlist);
+	if (err)
+		perf_header__clear_feat(header, HEADER_CPU_TOPOLOGY);
+
+	err = do_write_feat(fd, header, HEADER_TOTAL_MEM, &p, evlist);
+	if (err)
+		perf_header__clear_feat(header, HEADER_TOTAL_MEM);
+
+	err = do_write_feat(fd, header, HEADER_NUMA_TOPOLOGY, &p, evlist);
+	if (err)
+		perf_header__clear_feat(header, HEADER_NUMA_TOPOLOGY);
 
 	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");
@@ -554,9 +1454,10 @@ static int perf_header__getbuffer64(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))
+				  struct perf_header *ph,
+				  int feat, int fd, void *data))
 {
 	struct perf_file_section *feat_sec;
 	int nr_sections;
@@ -584,7 +1485,7 @@ int perf_header__process_sections(struct perf_header *header, int fd,
 		if (perf_header__has_feat(header, feat)) {
 			struct perf_file_section *sec = &feat_sec[idx++];
 
-			err = process(sec, header, feat, fd);
+			err = process(sec, header, feat, fd, data);
 			if (err < 0)
 				break;
 		}
@@ -796,7 +1697,7 @@ out:
 
 static int perf_file_section__process(struct perf_file_section *section,
 				      struct perf_header *ph,
-				      int feat, int fd)
+				      int feat, int fd, void *data __used)
 {
 	if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) {
 		pr_debug("Failed to lseek to %" PRIu64 " offset for feature "
@@ -935,7 +1836,8 @@ int perf_session__read_header(struct perf_session *session, int fd)
 		event_count =  f_header.event_types.size / sizeof(struct perf_trace_event_type);
 	}
 
-	perf_header__process_sections(header, fd, perf_file_section__process);
+	perf_header__process_sections(header, fd, NULL,
+				      perf_file_section__process);
 
 	lseek(fd, header->data_offset, SEEK_SET);
 
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
index 1886256..455480b 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -12,6 +12,19 @@
 enum {
 	HEADER_TRACE_INFO = 1,
 	HEADER_BUILD_ID,
+
+	HEADER_HOSTNAME,
+	HEADER_OSRELEASE,
+	HEADER_ARCH,
+	HEADER_CPUID,
+	HEADER_NRCPUS,
+	HEADER_EVENT_DESC,
+	HEADER_CMDLINE,
+	HEADER_VERSION,
+	HEADER_CPU_TOPOLOGY,
+	HEADER_TOTAL_MEM,
+	HEADER_NUMA_TOPOLOGY,
+
 	HEADER_LAST_FEATURE,
 };
 
@@ -68,10 +81,15 @@ void perf_header__set_feat(struct perf_header *header, int feat);
 void perf_header__clear_feat(struct perf_header *header, int feat);
 bool perf_header__has_feat(const struct perf_header *header, int feat);
 
+int perf_header__set_cmdline(int argc, const char **argv);
+
 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));
+				  struct perf_header *ph,
+				  int feat, int fd, void *data));
+
+int perf_header__fprintf_info(struct perf_session *session, FILE *fp);
 
 int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
 			  const char *name, bool is_kallsyms);
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 72458d9..54613a2 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -1326,3 +1326,21 @@ int perf_session__cpu_bitmap(struct perf_session *session,
 
 	return 0;
 }
+
+void perf_session__fprintf_info(struct perf_session *session, FILE *fp)
+{
+	struct stat st;
+	int ret;
+
+	if (session == NULL || fp == NULL)
+		return;
+
+	ret = fstat(session->fd, &st);
+	if (ret == -1)
+		return;
+
+	fprintf(fp, "#\n# captured on: %s", ctime(&st.st_ctime));
+	ret = perf_header__fprintf_info(session, fp);
+	if (ret == 0)
+		fprintf(fp, "#\n");
+}
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index 170601e..33a3a46 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -176,4 +176,5 @@ void perf_session__print_ip(union perf_event *event,
 int perf_session__cpu_bitmap(struct perf_session *session,
 			     const char *cpu_list, unsigned long *cpu_bitmap);
 
+void perf_session__fprintf_info(struct perf_session *session, FILE *fp);
 #endif /* __PERF_SESSION_H */

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [PATCH] perf: make perf.data more self-descriptive (v4)
  2011-09-07 19:10 [PATCH] perf: make perf.data more self-descriptive (v4) Stephane Eranian
@ 2011-09-08 15:27 ` David Ahern
       [not found]   ` <CABPqkBRKcxeTDMyVYPXU6AQEY-Hp2bnE03ebEtvjYWde2JKqoQ@mail.gmail.com>
  2011-09-08 15:42   ` Stephane Eranian
  2011-09-12 13:45 ` David Ahern
  1 sibling, 2 replies; 21+ messages in thread
From: David Ahern @ 2011-09-08 15:27 UTC (permalink / raw)
  To: Stephane Eranian; +Cc: linux-kernel, acme, peterz, mingo



On 09/07/2011 01:10 PM, Stephane Eranian wrote:
> 
> The goal of this patch is to include more information
> about the host environment into the perf.data so it is
> more self-descriptive. Overtime, profiles are captured
> on various machines and it becomes hard to track what
> was recorded, on what machine and when.
> 
> This patch provides a way to solve this by extending
> the perf.data file with basic information about the
> host machine. To add those extensions, we leverage
> the feature bits capabilities of the perf.data format.
> The change is backward compatible with existing perf.data
> files.
> 
> We define the following useful new extensions:
>  - HEADER_HOSTNAME: the hostname
>  - HEADER_OSRELEASE: the kernel release number
>  - HEADER_ARCH: the hw architecture
>  - HEADER_CPUID: the cpu model description
>  - HEADER_NRCPUS: number of online/avail cpus
>  - HEADER_CMDLINE: perf command line
>  - HEADER_VERSION: perf version
>  - HEADER_TOPOLOGY: cpu topology
>  - HEADER_EVENT_DESC: full event description (attrs)
> 
> The small granularity for the entries is to make it easier
> to extend without breaking backward compatiblity. All entries
> are provided as ASCII strings, easy to parse, except for the
> event description.
> 
> In the second version, we dropped the -I option for the
> perf record command. The extra information is systematically
> captured. But it's still displayed optionally by perf report.
> There are also a couple of cleanups of function prototypes
> and global variables.
> 
> In the third version, we:
> - fixed missing MIPS support for CPUID
> - used u32 instead of int when writing integers to file
> - fixed couple of bswap() bugs
> - refactorize all write/print functions using function pointers
> - improved write_cmdline() to include actual path to perf binary
> 
> In the fourth version, we:
> - fixed a couple of int vs. u32 issues
> - factorized HEADER_BUILD_ID, HEADER_TRACE_INFO
> - added -I option to perf script report
> - reverted change to evsel.c (unrelated to meta-data)
> - modified write_*() to always write despite errors (adds_feature bit present)
> - added HEADER_TOTAL_MEM: total memory installed on this system
> - added HEADER_NUMA_TOPOLOGY: shows NUMA nodes and for each memory, cpu list
> - renamed HEADER_TOPOLOGY to HEADER_CPU_TOPOLOGY

Still some endianness problems; cross-endian analysis is causing perf to
abort. I need some time to investigate. I can send you a ppc data file
if you have the time.

David

> 
> 
> $ perf record -ecycles,instructions noploop 1
> noploop for 1 seconds
> [ perf record: Woken up 1 times to write data ]
> [ perf record: Captured and wrote 0.098 MB perf.data (~4260 samples) ]
> $ perf report -I --stdio
> #
> # captured on: Wed Sep  7 21:00:10 2011
> # hostname : quad
> # os release : 3.1.0-rc4-tip
> # arch : x86_64
> # cpuid : Intel(R) Core(TM)2 Quad CPU Q6600 @ 2.40GHz
> # nrcpus online : 4
> # nrcpus avail : 4
> # event = cycles, type = 0, config = 0x0, config1 = 0x0, config2 = 0x0, excl_usr = 0, excl_kern = 0, id = { 536, 537, 538, 539 }
> # event = instructions, type = 0, config = 0x1, config1 = 0x0, config2 = 0x0, excl_usr = 0, excl_kern = 0, id = { 540, 541, 542,
> # cmdline : /home/eranian/perfmon/official/tip/build/tools/perf/perf record -ecycles,instructions noploop 1 
> # perf version : 3.1.0-rc4
> # CPU0   sibling cores  : 0-3
> # CPU0   sibling threads: 0
> # CPU1   sibling cores  : 0-3
> # CPU1   sibling threads: 1
> # CPU2   sibling cores  : 0-3
> # CPU2   sibling threads: 2
> # CPU3   sibling cores  : 0-3
> # CPU3   sibling threads: 3
> # total memory: 8105344 kB
> # node0   meminfo : 8320608 kB
> # node0   cpu list: 0-3
> #
> # Events: 995  cycles
> #
> # Overhead  Command      Shared Object                Symbol
> # ........  .......  .................  ....................
> #
>     99.90%  noploop  noploop            [.] noploop
>      0.10%  noploop  [kernel.kallsyms]  [k] trace_hardirqs_on
>      0.00%  noploop  [kernel.kallsyms]  [k] lock_release
>      0.00%  noploop  [kernel.kallsyms]  [k] intel_pmu_enable_all
> ...
> 
> 
> Signed-off-by: Stephane Eranian <eranian@google.com>
> ---
> 
> diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt
> index 04253c0..191e10e 100644
> --- a/tools/perf/Documentation/perf-report.txt
> +++ b/tools/perf/Documentation/perf-report.txt
> @@ -134,6 +134,11 @@ OPTIONS
>  	CPUs are specified with -: 0-2. Default is to report samples on all
>  	CPUs.
>  
> +-I::
> +--show-info::
> +	Display information about the perf.data file, incl. hostname,
> +	os release, perf version, machine desc.
> +
>  SEE ALSO
>  --------
>  linkperf:perf-stat[1]
> diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt
> index db01786..ebf8a23 100644
> --- a/tools/perf/Documentation/perf-script.txt
> +++ b/tools/perf/Documentation/perf-script.txt
> @@ -188,6 +188,13 @@ OPTIONS
>  	CPUs are specified with -: 0-2. Default is to report samples on all
>  	CPUs.
>  
> +-I::
> +--show-info::
> +	Display information about the perf.data file, incl. hostname,
> +	os release, perf version, machine desc. It can only be used with the
> +	perf script report mode and it must be specified after the script name
> +	like for instance: perf script report syscall-counts -I.
> +
>  SEE ALSO
>  --------
>  linkperf:perf-record[1], linkperf:perf-script-perl[1],
> diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
> index 6b0519f..524ebbf 100644
> --- a/tools/perf/builtin-record.c
> +++ b/tools/perf/builtin-record.c
> @@ -513,6 +513,18 @@ static int __cmd_record(int argc, const char **argv)
>  	if (have_tracepoints(&evsel_list->entries))
>  		perf_header__set_feat(&session->header, HEADER_TRACE_INFO);
>  
> +	perf_header__set_feat(&session->header, HEADER_HOSTNAME);
> +	perf_header__set_feat(&session->header, HEADER_OSRELEASE);
> +	perf_header__set_feat(&session->header, HEADER_ARCH);
> +	perf_header__set_feat(&session->header, HEADER_CPUID);
> +	perf_header__set_feat(&session->header, HEADER_NRCPUS);
> +	perf_header__set_feat(&session->header, HEADER_EVENT_DESC);
> +	perf_header__set_feat(&session->header, HEADER_CMDLINE);
> +	perf_header__set_feat(&session->header, HEADER_VERSION);
> +	perf_header__set_feat(&session->header, HEADER_CPU_TOPOLOGY);
> +	perf_header__set_feat(&session->header, HEADER_TOTAL_MEM);
> +	perf_header__set_feat(&session->header, HEADER_NUMA_TOPOLOGY);
> +
>  	/* 512 kiB: default amount of unprivileged mlocked memory */
>  	if (mmap_pages == UINT_MAX)
>  		mmap_pages = (512 * 1024) / page_size;
> @@ -782,6 +794,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
>  	int err = -ENOMEM;
>  	struct perf_evsel *pos;
>  
> +	perf_header__set_cmdline(argc, argv);
> +
>  	evsel_list = perf_evlist__new(NULL, NULL);
>  	if (evsel_list == NULL)
>  		return -ENOMEM;
> diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
> index d7ff277..7721030 100644
> --- a/tools/perf/builtin-report.c
> +++ b/tools/perf/builtin-report.c
> @@ -40,6 +40,7 @@ static char		const *input_name = "perf.data";
>  static bool		force, use_tui, use_stdio;
>  static bool		hide_unresolved;
>  static bool		dont_use_callchains;
> +static bool		show_info;
>  
>  static bool		show_threads;
>  static struct perf_read_values	show_threads_values;
> @@ -276,6 +277,9 @@ static int __cmd_report(void)
>  			goto out_delete;
>  	}
>  
> +	if (show_info)
> +		perf_session__fprintf_info(session, stdout);
> +
>  	if (show_threads)
>  		perf_read_values_init(&show_threads_values);
>  
> @@ -487,6 +491,8 @@ static const struct option options[] = {
>  	OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
>  		    "Look for files with symbols relative to this directory"),
>  	OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"),
> +	OPT_BOOLEAN('I', "show-info", &show_info,
> +			"display information about perf.data file"),
>  	OPT_END()
>  };
>  
> diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
> index 09024ec..dac86ab 100644
> --- a/tools/perf/builtin-script.c
> +++ b/tools/perf/builtin-script.c
> @@ -22,6 +22,7 @@ static u64			last_timestamp;
>  static u64			nr_unordered;
>  extern const struct option	record_options[];
>  static bool			no_callchain;
> +static bool			show_info = false;
>  static const char		*cpu_list;
>  static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
>  
> @@ -1083,7 +1084,8 @@ static const struct option options[] = {
>  		     "comma separated output fields prepend with 'type:'. Valid types: hw,sw,trace,raw. Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso,addr",
>  		     parse_output_fields),
>  	OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"),
> -
> +	OPT_BOOLEAN('I', "show-info", &show_info,
> +			"display host information from perf.data file"),
>  	OPT_END()
>  };
>  
> @@ -1268,6 +1270,9 @@ int cmd_script(int argc, const char **argv, const char *prefix __used)
>  			return -1;
>  	}
>  
> +	if (show_info)
> +		perf_session__fprintf_info(session, stdout);
> +
>  	if (!no_callchain)
>  		symbol_conf.use_callchain = true;
>  	else
> diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h
> index 4702e24..b382bd5 100644
> --- a/tools/perf/builtin.h
> +++ b/tools/perf/builtin.h
> @@ -4,7 +4,6 @@
>  #include "util/util.h"
>  #include "util/strbuf.h"
>  
> -extern const char perf_version_string[];
>  extern const char perf_usage_string[];
>  extern const char perf_more_info_string[];
>  
> diff --git a/tools/perf/perf.h b/tools/perf/perf.h
> index a5fc660..08b0b5e 100644
> --- a/tools/perf/perf.h
> +++ b/tools/perf/perf.h
> @@ -9,18 +9,21 @@ void get_term_dimensions(struct winsize *ws);
>  #include "../../arch/x86/include/asm/unistd.h"
>  #define rmb()		asm volatile("lock; addl $0,0(%%esp)" ::: "memory")
>  #define cpu_relax()	asm volatile("rep; nop" ::: "memory");
> +#define CPUINFO_PROC	"model name"
>  #endif
>  
>  #if defined(__x86_64__)
>  #include "../../arch/x86/include/asm/unistd.h"
>  #define rmb()		asm volatile("lfence" ::: "memory")
>  #define cpu_relax()	asm volatile("rep; nop" ::: "memory");
> +#define CPUINFO_PROC	"model name"
>  #endif
>  
>  #ifdef __powerpc__
>  #include "../../arch/powerpc/include/asm/unistd.h"
>  #define rmb()		asm volatile ("sync" ::: "memory")
>  #define cpu_relax()	asm volatile ("" ::: "memory");
> +#define CPUINFO_PROC	"cpu"
>  #endif
>  
>  #ifdef __s390__
> @@ -37,30 +40,35 @@ void get_term_dimensions(struct winsize *ws);
>  # define rmb()		asm volatile("" ::: "memory")
>  #endif
>  #define cpu_relax()	asm volatile("" ::: "memory")
> +#define CPUINFO_PROC	"cpu type"
>  #endif
>  
>  #ifdef __hppa__
>  #include "../../arch/parisc/include/asm/unistd.h"
>  #define rmb()		asm volatile("" ::: "memory")
>  #define cpu_relax()	asm volatile("" ::: "memory");
> +#define CPUINFO_PROC	"cpu"
>  #endif
>  
>  #ifdef __sparc__
>  #include "../../arch/sparc/include/asm/unistd.h"
>  #define rmb()		asm volatile("":::"memory")
>  #define cpu_relax()	asm volatile("":::"memory")
> +#define CPUINFO_PROC	"cpu"
>  #endif
>  
>  #ifdef __alpha__
>  #include "../../arch/alpha/include/asm/unistd.h"
>  #define rmb()		asm volatile("mb" ::: "memory")
>  #define cpu_relax()	asm volatile("" ::: "memory")
> +#define CPUINFO_PROC	"cpu model"
>  #endif
>  
>  #ifdef __ia64__
>  #include "../../arch/ia64/include/asm/unistd.h"
>  #define rmb()		asm volatile ("mf" ::: "memory")
>  #define cpu_relax()	asm volatile ("hint @pause" ::: "memory")
> +#define CPUINFO_PROC	"model name"
>  #endif
>  
>  #ifdef __arm__
> @@ -71,6 +79,7 @@ void get_term_dimensions(struct winsize *ws);
>   */
>  #define rmb()		((void(*)(void))0xffff0fa0)()
>  #define cpu_relax()	asm volatile("":::"memory")
> +#define CPUINFO_PROC	"Processor"
>  #endif
>  
>  #ifdef __mips__
> @@ -83,6 +92,7 @@ void get_term_dimensions(struct winsize *ws);
>  				: /* no input */			\
>  				: "memory")
>  #define cpu_relax()	asm volatile("" ::: "memory")
> +#define CPUINFO_PROC	"cpu model"
>  #endif
>  
>  #include <time.h>
> @@ -171,5 +181,6 @@ struct ip_callchain {
>  };
>  
>  extern bool perf_host, perf_guest;
> +extern const char perf_version_string[];
>  
>  #endif
> diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
> index b6c1ad1..73fa59e 100644
> --- a/tools/perf/util/header.c
> +++ b/tools/perf/util/header.c
> @@ -7,6 +7,7 @@
>  #include <stdlib.h>
>  #include <linux/list.h>
>  #include <linux/kernel.h>
> +#include <sys/utsname.h>
>  
>  #include "evlist.h"
>  #include "evsel.h"
> @@ -17,12 +18,19 @@
>  #include "session.h"
>  #include "symbol.h"
>  #include "debug.h"
> +#include "cpumap.h"
>  
>  static bool no_buildid_cache = false;
>  
>  static int event_count;
>  static struct perf_trace_event_type *events;
>  
> +static u32 header_argc;
> +static const char **header_argv;
> +
> +static int dsos__write_buildid_table(struct perf_header *header, int fd);
> +static int perf_session__cache_build_ids(struct perf_session *session);
> +
>  int perf_header__push_event(u64 id, const char *name)
>  {
>  	if (strlen(name) > MAX_EVENT_NAME)
> @@ -110,6 +118,843 @@ static int write_padded(int fd, const void *bf, size_t count,
>  	return err;
>  }
>  
> +static int do_write_string(int fd, const char *str)
> +{
> +	u32 len, olen;
> +	int ret;
> +
> +	olen = strlen(str) + 1;
> +	len = ALIGN(olen, NAME_ALIGN);
> +
> +	/* write len, incl. \0 */
> +	ret = do_write(fd, &len, sizeof(len));
> +	if (ret < 0)
> +		return ret;
> +
> +	return write_padded(fd, str, olen, len);
> +}
> +
> +static char *do_read_string(int fd, struct perf_header *ph)
> +{
> +	ssize_t sz, ret;
> +	u32 len;
> +	char *buf;
> +
> +	sz = read(fd, &len, sizeof(len));
> +	if (sz < (ssize_t)sizeof(len))
> +		return NULL;
> +
> +	if (ph->needs_swap)
> +		len = bswap_32(len);
> +
> +	buf = malloc(len);
> +	if (!buf)
> +		return NULL;
> +
> +	ret = read(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;
> +	}
> +
> +	free(buf);
> +	return NULL;
> +}
> +
> +int
> +perf_header__set_cmdline(int argc, const char **argv)
> +{
> +	int i;
> +
> +	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;
> +}
> +
> +static int write_trace_info(int fd, struct perf_header *h __used,
> +			    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 __used)
> +{
> +	struct perf_session *session;
> +	int err;
> +
> +	session = container_of(h, struct perf_session, header);
> +
> +	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);
> +
> +	return 0;
> +}
> +
> +static int write_hostname(int fd, struct perf_header *h __used,
> +			  struct perf_evlist *evlist __used)
> +{
> +	struct utsname uts;
> +	int ret;
> +
> +	ret = uname(&uts);
> +	if (ret < 0)
> +		return -1;
> +
> +	return do_write_string(fd, uts.nodename);
> +}
> +
> +static int write_osrelease(int fd, struct perf_header *h __used,
> +			   struct perf_evlist *evlist __used)
> +{
> +	struct utsname uts;
> +	int ret;
> +
> +	ret = uname(&uts);
> +	if (ret < 0)
> +		return -1;
> +
> +	return do_write_string(fd, uts.release);
> +}
> +
> +static int write_arch(int fd, struct perf_header *h __used,
> +		      struct perf_evlist *evlist __used)
> +{
> +	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 __used,
> +			 struct perf_evlist *evlist __used)
> +{
> +	return do_write_string(fd, perf_version_string);
> +}
> +
> +static int write_cpuid(int fd, struct perf_header *h __used,
> +		       struct perf_evlist *evlist __used)
> +{
> +#ifndef CPUINFO_PROC
> +#define CPUINFO_PROC NULL
> +#endif
> +	FILE *file;
> +	char buf[256];
> +	char *s, *p;
> +	const char *search = CPUINFO_PROC;
> +
> +	file = fopen("/proc/cpuinfo", "r");
> +	if (!file)
> +		return -1;
> +
> +	if (search) {
> +		while (fgets(buf, sizeof(buf), file)) {
> +			if (strstr(buf, search))
> +				goto found;
> +		}
> +	}
> +	strcpy(buf, "unknown type");
> +found:
> +	fclose(file);
> +
> +	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++));
> +		}
> +		p++;
> +	}
> +	return do_write_string(fd, s);
> +}
> +
> +static int write_nrcpus(int fd, struct perf_header *h __used,
> +			struct perf_evlist *evlist __used)
> +{
> +	long nr;
> +	u32 nrc, nra;
> +	int ret;
> +
> +	nr = sysconf(_SC_NPROCESSORS_CONF);
> +	if (nr < 0)
> +		return -1;
> +
> +	nrc = (u32)(nr & UINT_MAX);
> +
> +	ret = sysconf(_SC_NPROCESSORS_ONLN);
> +	if (ret < 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));
> +}
> +
> +static int write_event_desc(int fd, struct perf_header *h __used,
> +			    struct perf_evlist *evlist)
> +{
> +	struct perf_evsel *attr;
> +	u32 nre = 0, nri, sz;
> +	int ret;
> +
> +	list_for_each_entry(attr, &evlist->entries, node)
> +		nre++;
> +
> +	/*
> +	 * 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(attr->attr);
> +	ret = do_write(fd, &sz, sizeof(sz));
> +	if (ret < 0)
> +		return ret;
> +
> +	list_for_each_entry(attr, &evlist->entries, node) {
> +
> +		ret = do_write(fd, &attr->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 = attr->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, attr->name);
> +		if (ret < 0)
> +			return ret;
> +		/*
> +		 * write unique ids for this event
> +		 */
> +		ret = do_write(fd, attr->id, attr->ids * sizeof(u64));
> +		if (ret < 0)
> +			return ret;
> +	}
> +	return 0;
> +}
> +
> +static int write_cmdline(int fd, struct perf_header *h __used,
> +			 struct perf_evlist *evlist __used)
> +{
> +	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;
> +}
> +
> +static const char *topo_fmt[] = {
> +	"/sys/devices/system/cpu/cpu%d/topology/core_siblings_list",
> +	"/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list",
> +	NULL
> +};
> +
> +static int write_topo_cpu(int fd, int cpu)
> +{
> +	FILE *fp;
> +	char filename[MAXPATHLEN];
> +	char buf[MAXPATHLEN], *p, *c;
> +	const char **q = topo_fmt;
> +	int ret;
> +
> +	while (*q) {
> +
> +		sprintf(filename, *q, cpu);
> +
> +		fp = fopen(filename, "r");
> +		if (!fp)
> +			return -1;
> +
> +		c = fgets(buf, sizeof(buf), fp);
> +		if (!c) {
> +			fclose(fp);
> +			return -1;
> +		}
> +
> +		p = strchr(buf, '\n');
> +		if (p)
> +			*p = '\0';
> +
> +		fclose(fp);
> +
> +		ret = do_write_string(fd, c);
> +		if (ret < 0)
> +			return ret;
> +		q++;
> +	}
> +	return 0;
> +}
> +
> +static int write_cpu_topology(int fd, struct perf_header *h __used,
> +			  struct perf_evlist *evlist __used)
> +{
> +	u32 nr, i;
> +	long ncpus;
> +	int ret;
> +
> +	ncpus = sysconf(_SC_NPROCESSORS_CONF);
> +	if (ncpus < 0)
> +		return -1;
> +
> +	/* hopefully, this will keep compilers happy about the cast */
> +	nr = (u32)(ncpus & UINT_MAX);
> +
> +	ret = do_write(fd, &nr, sizeof(nr));
> +	if (ret < 0)
> +		return -1;
> +
> +	for (i = 0; i < nr; i++) {
> +		ret = do_write(fd, &i, sizeof(i));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = write_topo_cpu(fd, i);
> +		if (ret < 0)
> +			return ret;
> +	}
> +	return 0;
> +}
> +
> +static int write_total_mem(int fd, struct perf_header *h __used,
> +			  struct perf_evlist *evlist __used)
> +{
> +	char buf[MAXPATHLEN], *c;
> +	FILE *fp;
> +	int ret = -1, n;
> +	uint64_t mem;
> +
> +	fp = fopen("/proc/meminfo", "r");
> +	if (!fp)
> +		return -1;
> +
> +	while ((c = fgets(buf, sizeof(buf), fp))) {
> +		c = strstr(buf, "MemTotal:");
> +		if (c)
> +			break;
> +	}
> +	fclose(fp);
> +
> +	n = sscanf(buf, "%*s %"PRIu64, &mem);
> +	if (n == 1)
> +		ret = do_write(fd, &mem, sizeof(mem));
> +
> +	return ret;
> +}
> +
> +static int write_topo_node(int fd, int node)
> +{
> +	char filename[MAXPATHLEN];
> +	char buf[MAXPATHLEN], *p, *c;
> +	FILE *fp;
> +	uint64_t mem;
> +	int ret;
> +
> +	sprintf(filename, "/sys/devices/system/node/node%d/meminfo", node);
> +	fp = fopen(filename, "r");
> +	if (!fp)
> +		return -1;
> +
> +	while ((c = fgets(buf, sizeof(buf), fp))) {
> +		c = strstr(buf, "MemTotal:");
> +		if (c)
> +			break;
> +	}
> +	fclose(fp);
> +	if (!c)
> +		return -1;
> +	ret = sscanf(buf, "%*s %*d %*s %"PRIu64, &mem);
> +	if (ret != 1)
> +		return -1;
> +
> +	ret = do_write(fd, &mem, sizeof(mem));
> +	if (ret < 0)
> +		return -1;
> +
> +	sprintf(filename, "/sys/devices/system/node/node%d/cpulist", node);
> +	fp = fopen(filename, "r");
> +	if (!fp)
> +		return -1;
> +
> +	c = fgets(buf, sizeof(buf), fp);
> +	fclose(fp);
> +	if (!c)
> +		return -1;
> +
> +	p = strchr(buf, '\n');
> +	if (p)
> +		*p = '\0';
> +
> +	return do_write_string(fd, c);
> +}
> +
> +static int write_numa_topology(int fd, struct perf_header *h __used,
> +			  struct perf_evlist *evlist __used)
> +{
> +	char buf[MAXPATHLEN];
> +	FILE *fp;
> +	struct cpu_map *node_map;
> +	char *c;
> +	u32 nr, i, j;
> +	int ret;
> +
> +	fp = fopen("/sys/devices/system/node/online", "r");
> +	if (!fp)
> +		return -1;
> +
> +	c = fgets(buf, sizeof(buf), fp);
> +	fclose(fp);
> +	if (!c)
> +		return -1;
> +
> +	c = strchr(buf, '\n');
> +	if (c)
> +		*c = '\0';
> +
> +	node_map = cpu_map__new(buf);
> +	if (!node_map)
> +		return -1;
> +
> +	nr = (u32)node_map->nr;
> +	ret = do_write(fd, &nr, sizeof(nr));
> +	if (ret < 0)
> +		return -1;
> +
> +	for (i = 0; i < nr; i++) {
> +		j = (u32)node_map->map[i];
> +		ret = do_write(fd, &j, sizeof(j));
> +		if (ret < 0)
> +			goto error;
> +
> +		ret = write_topo_node(fd, i);
> +		if (ret < 0)
> +			goto error;
> +	}
> +	ret = 0;
> +error:
> +	free(node_map);
> +	return ret;
> +}
> +
> +static void print_hostname(struct perf_header *ph, int fd, FILE *fp)
> +{
> +	char *str = do_read_string(fd, ph);
> +	fprintf(fp, "# hostname : %s\n", str);
> +	free(str);
> +}
> +
> +static void print_osrelease(struct perf_header *ph, int fd, FILE *fp)
> +{
> +	char *str = do_read_string(fd, ph);
> +	fprintf(fp, "# os release : %s\n", str);
> +	free(str);
> +}
> +
> +static void print_arch(struct perf_header *ph, int fd, FILE *fp)
> +{
> +	char *str = do_read_string(fd, ph);
> +	fprintf(fp, "# arch : %s\n", str);
> +	free(str);
> +}
> +
> +static void print_cpuid(struct perf_header *ph, int fd, FILE *fp)
> +{
> +	char *str = do_read_string(fd, ph);
> +	fprintf(fp, "# cpuid : %s\n", str);
> +	free(str);
> +}
> +
> +static void print_nrcpus(struct perf_header *ph, int fd, FILE *fp)
> +{
> +	ssize_t ret;
> +	u32 nr;
> +
> +	ret = read(fd, &nr, sizeof(nr));
> +	if (ret != (ssize_t)sizeof(nr))
> +		nr = -1; /* interpreted as error */
> +
> +	if (ph->needs_swap)
> +		nr = bswap_32(nr);
> +
> +	fprintf(fp, "# nrcpus online : %u\n", nr);
> +
> +	ret = read(fd, &nr, sizeof(nr));
> +	if (ret != (ssize_t)sizeof(nr))
> +		nr = -1; /* interpreted as error */
> +
> +	if (ph->needs_swap)
> +		nr = bswap_32(nr);
> +
> +	fprintf(fp, "# nrcpus avail : %u\n", nr);
> +}
> +
> +static void print_version(struct perf_header *ph, int fd, FILE *fp)
> +{
> +	char *str = do_read_string(fd, ph);
> +	fprintf(fp, "# perf version : %s\n", str);
> +	free(str);
> +}
> +
> +static void print_cmdline(struct perf_header *ph, int fd, FILE *fp)
> +{
> +	ssize_t ret;
> +	char *str;
> +	u32 nr, i;
> +
> +	ret = read(fd, &nr, sizeof(nr));
> +	if (ret != (ssize_t)sizeof(nr)) {
> +		fprintf(fp, "# cmdline : unknown\n");
> +		return;
> +	}
> +
> +	if (ph->needs_swap)
> +		nr = bswap_32(nr);
> +
> +	fprintf(fp, "# cmdline : ");
> +
> +	for (i = 0; i < nr; i++) {
> +		str = do_read_string(fd, ph);
> +		fprintf(fp, "%s ", str);
> +		free(str);
> +	}
> +	fputc('\n', fp);
> +}
> +
> +static void print_cpu_topology(struct perf_header *ph, int fd, FILE *fp)
> +{
> +	ssize_t ret;
> +	u32 nr, c, i;
> +	char *str;
> +
> +	ret = read(fd, &nr, sizeof(nr));
> +	if (ret != (ssize_t)sizeof(nr)) {
> +		fprintf(fp, "# cpu topology  : not available\n");
> +		return;
> +	}
> +
> +	if (ph->needs_swap)
> +		nr = bswap_32(nr);
> +
> +	for (i = 0; i < nr; i++) {
> +
> +		ret = read(fd, &c, sizeof(c));
> +		if (ret != (ssize_t)sizeof(c))
> +			c = -1; /* interpreted as error */
> +
> +		if (ph->needs_swap)
> +			c = bswap_32(c);
> +
> +		str = do_read_string(fd, ph);
> +		fprintf(fp, "# CPU%u sibling cores  : %s\n", c, str);
> +		free(str);
> +
> +		str = do_read_string(fd, ph);
> +		fprintf(fp, "# CPU%u sibling threads: %s\n", c, str);
> +		free(str);
> +	}
> +}
> +
> +static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
> +{
> +	struct perf_event_attr attr;
> +	uint64_t id;
> +	void *buf = NULL;
> +	char *str;
> +	u32 nre, sz, nr, i, j, msz;
> +	int ret;
> +
> +	/* number of events */
> +	ret = read(fd, &nre, sizeof(nre));
> +	if (ret != (ssize_t)sizeof(nre))
> +		goto error;
> +
> +	if (ph->needs_swap)
> +		nre = bswap_32(nre);
> +
> +	ret = read(fd, &sz, sizeof(sz));
> +	if (ret != (ssize_t)sizeof(sz))
> +		goto error;
> +
> +	if (ph->needs_swap)
> +		sz = bswap_32(sz);
> +
> +	/*
> +	 * ensure it is at least to our ABI rev
> +	 */
> +	if (sz < (u32)sizeof(attr))
> +		goto error;
> +
> +	memset(&attr, 0, sizeof(attr));
> +
> +	/* read entire region to sync up to next field */
> +	buf = malloc(sz);
> +	if (!buf)
> +		goto error;
> +
> +	msz = sizeof(attr);
> +	if (sz < msz)
> +		msz = sz;
> +
> +	for (i = 0 ; i < nre; i++) {
> +
> +		ret = read(fd, buf, sz);
> +		if (ret != (ssize_t)sz)
> +			goto error;
> +
> +		if (ph->needs_swap)
> +			perf_event__attr_swap(buf);
> +
> +		memcpy(&attr, buf, msz);
> +
> +		ret = read(fd, &nr, sizeof(nr));
> +		if (ret != (ssize_t)sizeof(nr))
> +			goto error;
> +
> +		if (ph->needs_swap)
> +			nr = bswap_32(nr);
> +
> +		str = do_read_string(fd, ph);
> +		fprintf(fp, "# event = %s, ", str);
> +		free(str);
> +
> +		fprintf(fp, "type = %d, config = 0x%"PRIx64
> +			    ", config1 = 0x%"PRIx64", config2 = 0x%"PRIx64,
> +				attr.type,
> +				(u64)attr.config,
> +				(u64)attr.config1,
> +				(u64)attr.config2);
> +
> +		fprintf(fp, ", excl_usr = %d, excl_kern = %d",
> +				attr.exclude_user,
> +				attr.exclude_kernel);
> +
> +		if (nr)
> +			fprintf(fp, ", id = {");
> +
> +		for (j = 0 ; j < nr; j++) {
> +			ret = read(fd, &id, sizeof(id));
> +			if (ret != (ssize_t)sizeof(id))
> +				goto error;
> +
> +			if (ph->needs_swap)
> +				id = bswap_64(id);
> +
> +			if (j)
> +				fputc(',', fp);
> +
> +			fprintf(fp, " %"PRIu64, id);
> +		}
> +		if (nr && j == nr)
> +			fprintf(fp, " }");
> +		fputc('\n', fp);
> +	}
> +	free(buf);
> +	return;
> +error:
> +	fprintf(fp, "# event desc: not available or unable to read\n");
> +}
> +
> +static void print_total_mem(struct perf_header *h __used, int fd, FILE *fp)
> +{
> +	uint64_t mem;
> +	ssize_t ret;
> +
> +	ret = read(fd, &mem, sizeof(mem));
> +	if (ret != sizeof(mem))
> +		goto error;
> +
> +	if (h->needs_swap)
> +		mem = bswap_64(mem);
> +
> +	fprintf(fp, "# total memory: %"PRIu64" kB\n", mem);
> +	return;
> +error:
> +	fprintf(fp, "# total memory: unknown\n");
> +}
> +
> +static void print_numa_topology(struct perf_header *h __used, int fd, FILE *fp)
> +{
> +	ssize_t ret;
> +	u32 nr, c, i;
> +	char *str;
> +	uint64_t mem;
> +
> +	/* nr nodes */
> +	ret = read(fd, &nr, sizeof(nr));
> +	if (ret != (ssize_t)sizeof(nr))
> +		goto error;
> +
> +	if (h->needs_swap)
> +		nr = bswap_32(nr);
> +
> +	for (i = 0; i < nr; i++) {
> +
> +		/* node number */
> +		ret = read(fd, &c, sizeof(c));
> +		if (ret != (ssize_t)sizeof(c))
> +			goto error;
> +
> +		if (h->needs_swap)
> +			c = bswap_32(c);
> +
> +		ret = read(fd, &mem, sizeof(mem));
> +		if (ret != sizeof(mem))
> +			goto error;
> +
> +		if (h->needs_swap)
> +			mem = bswap_64(mem);
> +
> +		fprintf(fp, "# node%u meminfo : %"PRIu64" kB\n", c, mem);
> +
> +		str = do_read_string(fd, h);
> +		fprintf(fp, "# node%u cpu list: %s\n", c, str);
> +		free(str);
> +	}
> +	return;
> +error:
> +	fprintf(fp, "# numa topology : not available\n");
> +}
> +
> +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);
> +};
> +
> +#define FEAT_OP(n, w, p) [n] = { .write = w, .print = p }
> +
> +static struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
> +	FEAT_OP(HEADER_TRACE_INFO, write_trace_info, NULL),
> +	FEAT_OP(HEADER_BUILD_ID, write_build_id, NULL),
> +	FEAT_OP(HEADER_HOSTNAME, write_hostname, print_hostname),
> +	FEAT_OP(HEADER_OSRELEASE, write_osrelease, print_osrelease),
> +	FEAT_OP(HEADER_ARCH, write_arch, print_arch),
> +	FEAT_OP(HEADER_CPUID, write_cpuid, print_cpuid),
> +	FEAT_OP(HEADER_NRCPUS, write_nrcpus, print_nrcpus),
> +	FEAT_OP(HEADER_EVENT_DESC, write_event_desc, print_event_desc),
> +	FEAT_OP(HEADER_CMDLINE, write_cmdline, print_cmdline),
> +	FEAT_OP(HEADER_VERSION, write_version, print_version),
> +	FEAT_OP(HEADER_CPU_TOPOLOGY, write_cpu_topology, print_cpu_topology),
> +	FEAT_OP(HEADER_TOTAL_MEM, write_total_mem, print_total_mem),
> +	FEAT_OP(HEADER_NUMA_TOPOLOGY, write_numa_topology, print_numa_topology),
> +};
> +
> +static int perf_header_fprintf_info(struct perf_file_section *section,
> +				    struct perf_header *ph,
> +				    int feat, int fd, void *data)
> +{
> +	FILE *fp = data;
> +
> +	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_TRACE_INFO || feat >= HEADER_LAST_FEATURE) {
> +		pr_warning("unknown feature %d\n", feat);
> +		return -1;
> +	}
> +
> +	if (feat_ops[feat].print)
> +		feat_ops[feat].print(ph, fd, fp);
> +
> +	return 0;
> +}
> +
> +int perf_header__fprintf_info(struct perf_session *session, FILE *fp __used)
> +{
> +	int fd = session->fd;
> +	struct perf_header *header = &session->header;
> +	perf_header__process_sections(header, fd, fp, perf_header_fprintf_info);
> +	return 0;
> +}
> +
>  #define dsos__for_each_with_build_id(pos, head)	\
>  	list_for_each_entry(pos, head, node)	\
>  		if (!pos->has_build_id)		\
> @@ -356,15 +1201,41 @@ static bool perf_session__read_build_ids(struct perf_session *session, bool with
>  	return ret;
>  }
>  
> +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_header__has_feat(h, type)) {
> +
> +		(*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;
> +}
> +
>  static int perf_header__adds_write(struct perf_header *header,
>  				   struct perf_evlist *evlist, int fd)
>  {
>  	int nr_sections;
>  	struct perf_session *session;
> -	struct perf_file_section *feat_sec;
> +	struct perf_file_section *feat_sec, *p;
>  	int sec_size;
>  	u64 sec_start;
> -	int idx = 0, err;
> +	int err;
>  
>  	session = container_of(header, struct perf_session, header);
>  
> @@ -376,7 +1247,7 @@ static int perf_header__adds_write(struct perf_header *header,
>  	if (!nr_sections)
>  		return 0;
>  
> -	feat_sec = calloc(sizeof(*feat_sec), nr_sections);
> +	feat_sec = p = calloc(sizeof(*feat_sec), nr_sections);
>  	if (feat_sec == NULL)
>  		return -ENOMEM;
>  
> @@ -385,36 +1256,65 @@ static int perf_header__adds_write(struct perf_header *header,
>  	sec_start = header->data_offset + header->data_size;
>  	lseek(fd, sec_start + sec_size, SEEK_SET);
>  
> -	if (perf_header__has_feat(header, HEADER_TRACE_INFO)) {
> -		struct perf_file_section *trace_sec;
> -
> -		trace_sec = &feat_sec[idx++];
> +	err = do_write_feat(fd, header, HEADER_TRACE_INFO, &p, evlist);
> +	if (err)
> +		goto out_free;
>  
> -		/* Write trace info */
> -		trace_sec->offset = lseek(fd, 0, SEEK_CUR);
> -		read_tracing_data(fd, &evlist->entries);
> -		trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset;
> +	err = do_write_feat(fd, header, HEADER_BUILD_ID, &p, evlist);
> +	if (err) {
> +		perf_header__clear_feat(header, HEADER_BUILD_ID);
> +		goto out_free;
>  	}
>  
> -	if (perf_header__has_feat(header, HEADER_BUILD_ID)) {
> -		struct perf_file_section *buildid_sec;
> +	err = do_write_feat(fd, header, HEADER_HOSTNAME, &p, evlist);
> +	if (err)
> +		perf_header__clear_feat(header, HEADER_HOSTNAME);
>  
> -		buildid_sec = &feat_sec[idx++];
> +	err = do_write_feat(fd, header, HEADER_OSRELEASE, &p, evlist);
> +	if (err)
> +		perf_header__clear_feat(header, HEADER_OSRELEASE);
>  
> -		/* Write build-ids */
> -		buildid_sec->offset = lseek(fd, 0, SEEK_CUR);
> -		err = dsos__write_buildid_table(header, 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;
> -		if (!no_buildid_cache)
> -			perf_session__cache_build_ids(session);
> -	}
> +	err = do_write_feat(fd, header, HEADER_ARCH, &p, evlist);
> +	if (err)
> +		perf_header__clear_feat(header, HEADER_ARCH);
> +
> +	err = do_write_feat(fd, header, HEADER_CPUID, &p, evlist);
> +	if (err)
> +		perf_header__clear_feat(header, HEADER_CPUID);
> +
> +	err = do_write_feat(fd, header, HEADER_NRCPUS, &p, evlist);
> +	if (err)
> +		perf_header__clear_feat(header, HEADER_NRCPUS);
> +
> +	err = do_write_feat(fd, header, HEADER_EVENT_DESC, &p, evlist);
> +	if (err)
> +		perf_header__clear_feat(header, HEADER_EVENT_DESC);
> +
> +	err = do_write_feat(fd, header, HEADER_CMDLINE, &p, evlist);
> +	if (err)
> +		perf_header__clear_feat(header, HEADER_CMDLINE);
> +
> +	err = do_write_feat(fd, header, HEADER_VERSION, &p, evlist);
> +	if (err)
> +		perf_header__clear_feat(header, HEADER_VERSION);
> +
> +	err = do_write_feat(fd, header, HEADER_CPU_TOPOLOGY, &p, evlist);
> +	if (err)
> +		perf_header__clear_feat(header, HEADER_CPU_TOPOLOGY);
> +
> +	err = do_write_feat(fd, header, HEADER_TOTAL_MEM, &p, evlist);
> +	if (err)
> +		perf_header__clear_feat(header, HEADER_TOTAL_MEM);
> +
> +	err = do_write_feat(fd, header, HEADER_NUMA_TOPOLOGY, &p, evlist);
> +	if (err)
> +		perf_header__clear_feat(header, HEADER_NUMA_TOPOLOGY);
>  
>  	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");
> @@ -554,9 +1454,10 @@ static int perf_header__getbuffer64(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))
> +				  struct perf_header *ph,
> +				  int feat, int fd, void *data))
>  {
>  	struct perf_file_section *feat_sec;
>  	int nr_sections;
> @@ -584,7 +1485,7 @@ int perf_header__process_sections(struct perf_header *header, int fd,
>  		if (perf_header__has_feat(header, feat)) {
>  			struct perf_file_section *sec = &feat_sec[idx++];
>  
> -			err = process(sec, header, feat, fd);
> +			err = process(sec, header, feat, fd, data);
>  			if (err < 0)
>  				break;
>  		}
> @@ -796,7 +1697,7 @@ out:
>  
>  static int perf_file_section__process(struct perf_file_section *section,
>  				      struct perf_header *ph,
> -				      int feat, int fd)
> +				      int feat, int fd, void *data __used)
>  {
>  	if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) {
>  		pr_debug("Failed to lseek to %" PRIu64 " offset for feature "
> @@ -935,7 +1836,8 @@ int perf_session__read_header(struct perf_session *session, int fd)
>  		event_count =  f_header.event_types.size / sizeof(struct perf_trace_event_type);
>  	}
>  
> -	perf_header__process_sections(header, fd, perf_file_section__process);
> +	perf_header__process_sections(header, fd, NULL,
> +				      perf_file_section__process);
>  
>  	lseek(fd, header->data_offset, SEEK_SET);
>  
> diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
> index 1886256..455480b 100644
> --- a/tools/perf/util/header.h
> +++ b/tools/perf/util/header.h
> @@ -12,6 +12,19 @@
>  enum {
>  	HEADER_TRACE_INFO = 1,
>  	HEADER_BUILD_ID,
> +
> +	HEADER_HOSTNAME,
> +	HEADER_OSRELEASE,
> +	HEADER_ARCH,
> +	HEADER_CPUID,
> +	HEADER_NRCPUS,
> +	HEADER_EVENT_DESC,
> +	HEADER_CMDLINE,
> +	HEADER_VERSION,
> +	HEADER_CPU_TOPOLOGY,
> +	HEADER_TOTAL_MEM,
> +	HEADER_NUMA_TOPOLOGY,
> +
>  	HEADER_LAST_FEATURE,
>  };
>  
> @@ -68,10 +81,15 @@ void perf_header__set_feat(struct perf_header *header, int feat);
>  void perf_header__clear_feat(struct perf_header *header, int feat);
>  bool perf_header__has_feat(const struct perf_header *header, int feat);
>  
> +int perf_header__set_cmdline(int argc, const char **argv);
> +
>  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));
> +				  struct perf_header *ph,
> +				  int feat, int fd, void *data));
> +
> +int perf_header__fprintf_info(struct perf_session *session, FILE *fp);
>  
>  int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
>  			  const char *name, bool is_kallsyms);
> diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
> index 72458d9..54613a2 100644
> --- a/tools/perf/util/session.c
> +++ b/tools/perf/util/session.c
> @@ -1326,3 +1326,21 @@ int perf_session__cpu_bitmap(struct perf_session *session,
>  
>  	return 0;
>  }
> +
> +void perf_session__fprintf_info(struct perf_session *session, FILE *fp)
> +{
> +	struct stat st;
> +	int ret;
> +
> +	if (session == NULL || fp == NULL)
> +		return;
> +
> +	ret = fstat(session->fd, &st);
> +	if (ret == -1)
> +		return;
> +
> +	fprintf(fp, "#\n# captured on: %s", ctime(&st.st_ctime));
> +	ret = perf_header__fprintf_info(session, fp);
> +	if (ret == 0)
> +		fprintf(fp, "#\n");
> +}
> diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
> index 170601e..33a3a46 100644
> --- a/tools/perf/util/session.h
> +++ b/tools/perf/util/session.h
> @@ -176,4 +176,5 @@ void perf_session__print_ip(union perf_event *event,
>  int perf_session__cpu_bitmap(struct perf_session *session,
>  			     const char *cpu_list, unsigned long *cpu_bitmap);
>  
> +void perf_session__fprintf_info(struct perf_session *session, FILE *fp);
>  #endif /* __PERF_SESSION_H */

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] perf: make perf.data more self-descriptive (v4)
       [not found]   ` <CABPqkBRKcxeTDMyVYPXU6AQEY-Hp2bnE03ebEtvjYWde2JKqoQ@mail.gmail.com>
@ 2011-09-08 15:37     ` David Ahern
  0 siblings, 0 replies; 21+ messages in thread
From: David Ahern @ 2011-09-08 15:37 UTC (permalink / raw)
  To: Stephane Eranian; +Cc: linux-kernel, acme, peterz, mingo



On 09/08/2011 09:35 AM, Stephane Eranian wrote:
> David,
> 
> Something I don't quite understand with your issues with endianess.
> 
> How does endianess change anything to a int32 (aka int) vs. uint32?
> 
> The byte swapping you need to do is regardless of the sign of
> the data or am I missing something here?

Cross bitness is a factor too. A use case on my end is data collection
on 32-bit PPC and analysis on 64-bit x86.

David

> 
> 
> 
> On Thu, Sep 8, 2011 at 5:27 PM, David Ahern <dsahern@gmail.com
> <mailto:dsahern@gmail.com>> wrote:
> 
> 
> 
>     On 09/07/2011 01:10 PM, Stephane Eranian wrote:
>     >
>     > The goal of this patch is to include more information
>     > about the host environment into the perf.data so it is
>     > more self-descriptive. Overtime, profiles are captured
>     > on various machines and it becomes hard to track what
>     > was recorded, on what machine and when.
>     >
>     > This patch provides a way to solve this by extending
>     > the perf.data file with basic information about the
>     > host machine. To add those extensions, we leverage
>     > the feature bits capabilities of the perf.data format.
>     > The change is backward compatible with existing perf.data
>     > files.
>     >
>     > We define the following useful new extensions:
>     >  - HEADER_HOSTNAME: the hostname
>     >  - HEADER_OSRELEASE: the kernel release number
>     >  - HEADER_ARCH: the hw architecture
>     >  - HEADER_CPUID: the cpu model description
>     >  - HEADER_NRCPUS: number of online/avail cpus
>     >  - HEADER_CMDLINE: perf command line
>     >  - HEADER_VERSION: perf version
>     >  - HEADER_TOPOLOGY: cpu topology
>     >  - HEADER_EVENT_DESC: full event description (attrs)
>     >
>     > The small granularity for the entries is to make it easier
>     > to extend without breaking backward compatiblity. All entries
>     > are provided as ASCII strings, easy to parse, except for the
>     > event description.
>     >
>     > In the second version, we dropped the -I option for the
>     > perf record command. The extra information is systematically
>     > captured. But it's still displayed optionally by perf report.
>     > There are also a couple of cleanups of function prototypes
>     > and global variables.
>     >
>     > In the third version, we:
>     > - fixed missing MIPS support for CPUID
>     > - used u32 instead of int when writing integers to file
>     > - fixed couple of bswap() bugs
>     > - refactorize all write/print functions using function pointers
>     > - improved write_cmdline() to include actual path to perf binary
>     >
>     > In the fourth version, we:
>     > - fixed a couple of int vs. u32 issues
>     > - factorized HEADER_BUILD_ID, HEADER_TRACE_INFO
>     > - added -I option to perf script report
>     > - reverted change to evsel.c (unrelated to meta-data)
>     > - modified write_*() to always write despite errors (adds_feature
>     bit present)
>     > - added HEADER_TOTAL_MEM: total memory installed on this system
>     > - added HEADER_NUMA_TOPOLOGY: shows NUMA nodes and for each
>     memory, cpu list
>     > - renamed HEADER_TOPOLOGY to HEADER_CPU_TOPOLOGY
> 
>     Still some endianness problems; cross-endian analysis is causing perf to
>     abort. I need some time to investigate. I can send you a ppc data file
>     if you have the time.
> 
>     David
> 
>     >
>     >
>     > $ perf record -ecycles,instructions noploop 1
>     > noploop for 1 seconds
>     > [ perf record: Woken up 1 times to write data ]
>     > [ perf record: Captured and wrote 0.098 MB perf.data (~4260 samples) ]
>     > $ perf report -I --stdio
>     > #
>     > # captured on: Wed Sep  7 21:00:10 2011
>     > # hostname : quad
>     > # os release : 3.1.0-rc4-tip
>     > # arch : x86_64
>     > # cpuid : Intel(R) Core(TM)2 Quad CPU Q6600 @ 2.40GHz
>     > # nrcpus online : 4
>     > # nrcpus avail : 4
>     > # event = cycles, type = 0, config = 0x0, config1 = 0x0, config2 =
>     0x0, excl_usr = 0, excl_kern = 0, id = { 536, 537, 538, 539 }
>     > # event = instructions, type = 0, config = 0x1, config1 = 0x0,
>     config2 = 0x0, excl_usr = 0, excl_kern = 0, id = { 540, 541, 542,
>     > # cmdline :
>     /home/eranian/perfmon/official/tip/build/tools/perf/perf record
>     -ecycles,instructions noploop 1
>     > # perf version : 3.1.0-rc4
>     > # CPU0   sibling cores  : 0-3
>     > # CPU0   sibling threads: 0
>     > # CPU1   sibling cores  : 0-3
>     > # CPU1   sibling threads: 1
>     > # CPU2   sibling cores  : 0-3
>     > # CPU2   sibling threads: 2
>     > # CPU3   sibling cores  : 0-3
>     > # CPU3   sibling threads: 3
>     > # total memory: 8105344 kB
>     > # node0   meminfo : 8320608 kB
>     > # node0   cpu list: 0-3
>     > #
>     > # Events: 995  cycles
>     > #
>     > # Overhead  Command      Shared Object                Symbol
>     > # ........  .......  .................  ....................
>     > #
>     >     99.90%  noploop  noploop            [.] noploop
>     >      0.10%  noploop  [kernel.kallsyms]  [k] trace_hardirqs_on
>     >      0.00%  noploop  [kernel.kallsyms]  [k] lock_release
>     >      0.00%  noploop  [kernel.kallsyms]  [k] intel_pmu_enable_all
>     > ...
>     >
>     >
>     > Signed-off-by: Stephane Eranian <eranian@google.com
>     <mailto:eranian@google.com>>
>     > ---
>     >
>     > diff --git a/tools/perf/Documentation/perf-report.txt
>     b/tools/perf/Documentation/perf-report.txt
>     > index 04253c0..191e10e 100644
>     > --- a/tools/perf/Documentation/perf-report.txt
>     > +++ b/tools/perf/Documentation/perf-report.txt
>     > @@ -134,6 +134,11 @@ OPTIONS
>     >       CPUs are specified with -: 0-2. Default is to report samples
>     on all
>     >       CPUs.
>     >
>     > +-I::
>     > +--show-info::
>     > +     Display information about the perf.data file, incl. hostname,
>     > +     os release, perf version, machine desc.
>     > +
>     >  SEE ALSO
>     >  --------
>     >  linkperf:perf-stat[1]
>     > diff --git a/tools/perf/Documentation/perf-script.txt
>     b/tools/perf/Documentation/perf-script.txt
>     > index db01786..ebf8a23 100644
>     > --- a/tools/perf/Documentation/perf-script.txt
>     > +++ b/tools/perf/Documentation/perf-script.txt
>     > @@ -188,6 +188,13 @@ OPTIONS
>     >       CPUs are specified with -: 0-2. Default is to report samples
>     on all
>     >       CPUs.
>     >
>     > +-I::
>     > +--show-info::
>     > +     Display information about the perf.data file, incl. hostname,
>     > +     os release, perf version, machine desc. It can only be used
>     with the
>     > +     perf script report mode and it must be specified after the
>     script name
>     > +     like for instance: perf script report syscall-counts -I.
>     > +
>     >  SEE ALSO
>     >  --------
>     >  linkperf:perf-record[1], linkperf:perf-script-perl[1],
>     > diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
>     > index 6b0519f..524ebbf 100644
>     > --- a/tools/perf/builtin-record.c
>     > +++ b/tools/perf/builtin-record.c
>     > @@ -513,6 +513,18 @@ static int __cmd_record(int argc, const char
>     **argv)
>     >       if (have_tracepoints(&evsel_list->entries))
>     >               perf_header__set_feat(&session->header,
>     HEADER_TRACE_INFO);
>     >
>     > +     perf_header__set_feat(&session->header, HEADER_HOSTNAME);
>     > +     perf_header__set_feat(&session->header, HEADER_OSRELEASE);
>     > +     perf_header__set_feat(&session->header, HEADER_ARCH);
>     > +     perf_header__set_feat(&session->header, HEADER_CPUID);
>     > +     perf_header__set_feat(&session->header, HEADER_NRCPUS);
>     > +     perf_header__set_feat(&session->header, HEADER_EVENT_DESC);
>     > +     perf_header__set_feat(&session->header, HEADER_CMDLINE);
>     > +     perf_header__set_feat(&session->header, HEADER_VERSION);
>     > +     perf_header__set_feat(&session->header, HEADER_CPU_TOPOLOGY);
>     > +     perf_header__set_feat(&session->header, HEADER_TOTAL_MEM);
>     > +     perf_header__set_feat(&session->header, HEADER_NUMA_TOPOLOGY);
>     > +
>     >       /* 512 kiB: default amount of unprivileged mlocked memory */
>     >       if (mmap_pages == UINT_MAX)
>     >               mmap_pages = (512 * 1024) / page_size;
>     > @@ -782,6 +794,8 @@ int cmd_record(int argc, const char **argv,
>     const char *prefix __used)
>     >       int err = -ENOMEM;
>     >       struct perf_evsel *pos;
>     >
>     > +     perf_header__set_cmdline(argc, argv);
>     > +
>     >       evsel_list = perf_evlist__new(NULL, NULL);
>     >       if (evsel_list == NULL)
>     >               return -ENOMEM;
>     > diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
>     > index d7ff277..7721030 100644
>     > --- a/tools/perf/builtin-report.c
>     > +++ b/tools/perf/builtin-report.c
>     > @@ -40,6 +40,7 @@ static char         const *input_name = "perf.data";
>     >  static bool          force, use_tui, use_stdio;
>     >  static bool          hide_unresolved;
>     >  static bool          dont_use_callchains;
>     > +static bool          show_info;
>     >
>     >  static bool          show_threads;
>     >  static struct perf_read_values       show_threads_values;
>     > @@ -276,6 +277,9 @@ static int __cmd_report(void)
>     >                       goto out_delete;
>     >       }
>     >
>     > +     if (show_info)
>     > +             perf_session__fprintf_info(session, stdout);
>     > +
>     >       if (show_threads)
>     >               perf_read_values_init(&show_threads_values);
>     >
>     > @@ -487,6 +491,8 @@ static const struct option options[] = {
>     >       OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
>     >                   "Look for files with symbols relative to this
>     directory"),
>     >       OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to
>     profile"),
>     > +     OPT_BOOLEAN('I', "show-info", &show_info,
>     > +                     "display information about perf.data file"),
>     >       OPT_END()
>     >  };
>     >
>     > diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
>     > index 09024ec..dac86ab 100644
>     > --- a/tools/perf/builtin-script.c
>     > +++ b/tools/perf/builtin-script.c
>     > @@ -22,6 +22,7 @@ static u64                  last_timestamp;
>     >  static u64                   nr_unordered;
>     >  extern const struct option   record_options[];
>     >  static bool                  no_callchain;
>     > +static bool                  show_info = false;
>     >  static const char            *cpu_list;
>     >  static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
>     >
>     > @@ -1083,7 +1084,8 @@ static const struct option options[] = {
>     >                    "comma separated output fields prepend with
>     'type:'. Valid types: hw,sw,trace,raw. Fields:
>     comm,tid,pid,time,cpu,event,trace,ip,sym,dso,addr",
>     >                    parse_output_fields),
>     >       OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to
>     profile"),
>     > -
>     > +     OPT_BOOLEAN('I', "show-info", &show_info,
>     > +                     "display host information from perf.data file"),
>     >       OPT_END()
>     >  };
>     >
>     > @@ -1268,6 +1270,9 @@ int cmd_script(int argc, const char **argv,
>     const char *prefix __used)
>     >                       return -1;
>     >       }
>     >
>     > +     if (show_info)
>     > +             perf_session__fprintf_info(session, stdout);
>     > +
>     >       if (!no_callchain)
>     >               symbol_conf.use_callchain = true;
>     >       else
>     > diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h
>     > index 4702e24..b382bd5 100644
>     > --- a/tools/perf/builtin.h
>     > +++ b/tools/perf/builtin.h
>     > @@ -4,7 +4,6 @@
>     >  #include "util/util.h"
>     >  #include "util/strbuf.h"
>     >
>     > -extern const char perf_version_string[];
>     >  extern const char perf_usage_string[];
>     >  extern const char perf_more_info_string[];
>     >
>     > diff --git a/tools/perf/perf.h b/tools/perf/perf.h
>     > index a5fc660..08b0b5e 100644
>     > --- a/tools/perf/perf.h
>     > +++ b/tools/perf/perf.h
>     > @@ -9,18 +9,21 @@ void get_term_dimensions(struct winsize *ws);
>     >  #include "../../arch/x86/include/asm/unistd.h"
>     >  #define rmb()                asm volatile("lock; addl
>     $0,0(%%esp)" ::: "memory")
>     >  #define cpu_relax()  asm volatile("rep; nop" ::: "memory");
>     > +#define CPUINFO_PROC "model name"
>     >  #endif
>     >
>     >  #if defined(__x86_64__)
>     >  #include "../../arch/x86/include/asm/unistd.h"
>     >  #define rmb()                asm volatile("lfence" ::: "memory")
>     >  #define cpu_relax()  asm volatile("rep; nop" ::: "memory");
>     > +#define CPUINFO_PROC "model name"
>     >  #endif
>     >
>     >  #ifdef __powerpc__
>     >  #include "../../arch/powerpc/include/asm/unistd.h"
>     >  #define rmb()                asm volatile ("sync" ::: "memory")
>     >  #define cpu_relax()  asm volatile ("" ::: "memory");
>     > +#define CPUINFO_PROC "cpu"
>     >  #endif
>     >
>     >  #ifdef __s390__
>     > @@ -37,30 +40,35 @@ void get_term_dimensions(struct winsize *ws);
>     >  # define rmb()               asm volatile("" ::: "memory")
>     >  #endif
>     >  #define cpu_relax()  asm volatile("" ::: "memory")
>     > +#define CPUINFO_PROC "cpu type"
>     >  #endif
>     >
>     >  #ifdef __hppa__
>     >  #include "../../arch/parisc/include/asm/unistd.h"
>     >  #define rmb()                asm volatile("" ::: "memory")
>     >  #define cpu_relax()  asm volatile("" ::: "memory");
>     > +#define CPUINFO_PROC "cpu"
>     >  #endif
>     >
>     >  #ifdef __sparc__
>     >  #include "../../arch/sparc/include/asm/unistd.h"
>     >  #define rmb()                asm volatile("":::"memory")
>     >  #define cpu_relax()  asm volatile("":::"memory")
>     > +#define CPUINFO_PROC "cpu"
>     >  #endif
>     >
>     >  #ifdef __alpha__
>     >  #include "../../arch/alpha/include/asm/unistd.h"
>     >  #define rmb()                asm volatile("mb" ::: "memory")
>     >  #define cpu_relax()  asm volatile("" ::: "memory")
>     > +#define CPUINFO_PROC "cpu model"
>     >  #endif
>     >
>     >  #ifdef __ia64__
>     >  #include "../../arch/ia64/include/asm/unistd.h"
>     >  #define rmb()                asm volatile ("mf" ::: "memory")
>     >  #define cpu_relax()  asm volatile ("hint @pause" ::: "memory")
>     > +#define CPUINFO_PROC "model name"
>     >  #endif
>     >
>     >  #ifdef __arm__
>     > @@ -71,6 +79,7 @@ void get_term_dimensions(struct winsize *ws);
>     >   */
>     >  #define rmb()                ((void(*)(void))0xffff0fa0)()
>     >  #define cpu_relax()  asm volatile("":::"memory")
>     > +#define CPUINFO_PROC "Processor"
>     >  #endif
>     >
>     >  #ifdef __mips__
>     > @@ -83,6 +92,7 @@ void get_term_dimensions(struct winsize *ws);
>     >                               : /* no input */                    
>        \
>     >                               : "memory")
>     >  #define cpu_relax()  asm volatile("" ::: "memory")
>     > +#define CPUINFO_PROC "cpu model"
>     >  #endif
>     >
>     >  #include <time.h>
>     > @@ -171,5 +181,6 @@ struct ip_callchain {
>     >  };
>     >
>     >  extern bool perf_host, perf_guest;
>     > +extern const char perf_version_string[];
>     >
>     >  #endif
>     > diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
>     > index b6c1ad1..73fa59e 100644
>     > --- a/tools/perf/util/header.c
>     > +++ b/tools/perf/util/header.c
>     > @@ -7,6 +7,7 @@
>     >  #include <stdlib.h>
>     >  #include <linux/list.h>
>     >  #include <linux/kernel.h>
>     > +#include <sys/utsname.h>
>     >
>     >  #include "evlist.h"
>     >  #include "evsel.h"
>     > @@ -17,12 +18,19 @@
>     >  #include "session.h"
>     >  #include "symbol.h"
>     >  #include "debug.h"
>     > +#include "cpumap.h"
>     >
>     >  static bool no_buildid_cache = false;
>     >
>     >  static int event_count;
>     >  static struct perf_trace_event_type *events;
>     >
>     > +static u32 header_argc;
>     > +static const char **header_argv;
>     > +
>     > +static int dsos__write_buildid_table(struct perf_header *header,
>     int fd);
>     > +static int perf_session__cache_build_ids(struct perf_session
>     *session);
>     > +
>     >  int perf_header__push_event(u64 id, const char *name)
>     >  {
>     >       if (strlen(name) > MAX_EVENT_NAME)
>     > @@ -110,6 +118,843 @@ static int write_padded(int fd, const void
>     *bf, size_t count,
>     >       return err;
>     >  }
>     >
>     > +static int do_write_string(int fd, const char *str)
>     > +{
>     > +     u32 len, olen;
>     > +     int ret;
>     > +
>     > +     olen = strlen(str) + 1;
>     > +     len = ALIGN(olen, NAME_ALIGN);
>     > +
>     > +     /* write len, incl. \0 */
>     > +     ret = do_write(fd, &len, sizeof(len));
>     > +     if (ret < 0)
>     > +             return ret;
>     > +
>     > +     return write_padded(fd, str, olen, len);
>     > +}
>     > +
>     > +static char *do_read_string(int fd, struct perf_header *ph)
>     > +{
>     > +     ssize_t sz, ret;
>     > +     u32 len;
>     > +     char *buf;
>     > +
>     > +     sz = read(fd, &len, sizeof(len));
>     > +     if (sz < (ssize_t)sizeof(len))
>     > +             return NULL;
>     > +
>     > +     if (ph->needs_swap)
>     > +             len = bswap_32(len);
>     > +
>     > +     buf = malloc(len);
>     > +     if (!buf)
>     > +             return NULL;
>     > +
>     > +     ret = read(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;
>     > +     }
>     > +
>     > +     free(buf);
>     > +     return NULL;
>     > +}
>     > +
>     > +int
>     > +perf_header__set_cmdline(int argc, const char **argv)
>     > +{
>     > +     int i;
>     > +
>     > +     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;
>     > +}
>     > +
>     > +static int write_trace_info(int fd, struct perf_header *h __used,
>     > +                         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 __used)
>     > +{
>     > +     struct perf_session *session;
>     > +     int err;
>     > +
>     > +     session = container_of(h, struct perf_session, header);
>     > +
>     > +     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);
>     > +
>     > +     return 0;
>     > +}
>     > +
>     > +static int write_hostname(int fd, struct perf_header *h __used,
>     > +                       struct perf_evlist *evlist __used)
>     > +{
>     > +     struct utsname uts;
>     > +     int ret;
>     > +
>     > +     ret = uname(&uts);
>     > +     if (ret < 0)
>     > +             return -1;
>     > +
>     > +     return do_write_string(fd, uts.nodename);
>     > +}
>     > +
>     > +static int write_osrelease(int fd, struct perf_header *h __used,
>     > +                        struct perf_evlist *evlist __used)
>     > +{
>     > +     struct utsname uts;
>     > +     int ret;
>     > +
>     > +     ret = uname(&uts);
>     > +     if (ret < 0)
>     > +             return -1;
>     > +
>     > +     return do_write_string(fd, uts.release);
>     > +}
>     > +
>     > +static int write_arch(int fd, struct perf_header *h __used,
>     > +                   struct perf_evlist *evlist __used)
>     > +{
>     > +     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 __used,
>     > +                      struct perf_evlist *evlist __used)
>     > +{
>     > +     return do_write_string(fd, perf_version_string);
>     > +}
>     > +
>     > +static int write_cpuid(int fd, struct perf_header *h __used,
>     > +                    struct perf_evlist *evlist __used)
>     > +{
>     > +#ifndef CPUINFO_PROC
>     > +#define CPUINFO_PROC NULL
>     > +#endif
>     > +     FILE *file;
>     > +     char buf[256];
>     > +     char *s, *p;
>     > +     const char *search = CPUINFO_PROC;
>     > +
>     > +     file = fopen("/proc/cpuinfo", "r");
>     > +     if (!file)
>     > +             return -1;
>     > +
>     > +     if (search) {
>     > +             while (fgets(buf, sizeof(buf), file)) {
>     > +                     if (strstr(buf, search))
>     > +                             goto found;
>     > +             }
>     > +     }
>     > +     strcpy(buf, "unknown type");
>     > +found:
>     > +     fclose(file);
>     > +
>     > +     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++));
>     > +             }
>     > +             p++;
>     > +     }
>     > +     return do_write_string(fd, s);
>     > +}
>     > +
>     > +static int write_nrcpus(int fd, struct perf_header *h __used,
>     > +                     struct perf_evlist *evlist __used)
>     > +{
>     > +     long nr;
>     > +     u32 nrc, nra;
>     > +     int ret;
>     > +
>     > +     nr = sysconf(_SC_NPROCESSORS_CONF);
>     > +     if (nr < 0)
>     > +             return -1;
>     > +
>     > +     nrc = (u32)(nr & UINT_MAX);
>     > +
>     > +     ret = sysconf(_SC_NPROCESSORS_ONLN);
>     > +     if (ret < 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));
>     > +}
>     > +
>     > +static int write_event_desc(int fd, struct perf_header *h __used,
>     > +                         struct perf_evlist *evlist)
>     > +{
>     > +     struct perf_evsel *attr;
>     > +     u32 nre = 0, nri, sz;
>     > +     int ret;
>     > +
>     > +     list_for_each_entry(attr, &evlist->entries, node)
>     > +             nre++;
>     > +
>     > +     /*
>     > +      * 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(attr->attr);
>     > +     ret = do_write(fd, &sz, sizeof(sz));
>     > +     if (ret < 0)
>     > +             return ret;
>     > +
>     > +     list_for_each_entry(attr, &evlist->entries, node) {
>     > +
>     > +             ret = do_write(fd, &attr->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 = attr->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, attr->name);
>     > +             if (ret < 0)
>     > +                     return ret;
>     > +             /*
>     > +              * write unique ids for this event
>     > +              */
>     > +             ret = do_write(fd, attr->id, attr->ids * sizeof(u64));
>     > +             if (ret < 0)
>     > +                     return ret;
>     > +     }
>     > +     return 0;
>     > +}
>     > +
>     > +static int write_cmdline(int fd, struct perf_header *h __used,
>     > +                      struct perf_evlist *evlist __used)
>     > +{
>     > +     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;
>     > +}
>     > +
>     > +static const char *topo_fmt[] = {
>     > +     "/sys/devices/system/cpu/cpu%d/topology/core_siblings_list",
>     > +     "/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list",
>     > +     NULL
>     > +};
>     > +
>     > +static int write_topo_cpu(int fd, int cpu)
>     > +{
>     > +     FILE *fp;
>     > +     char filename[MAXPATHLEN];
>     > +     char buf[MAXPATHLEN], *p, *c;
>     > +     const char **q = topo_fmt;
>     > +     int ret;
>     > +
>     > +     while (*q) {
>     > +
>     > +             sprintf(filename, *q, cpu);
>     > +
>     > +             fp = fopen(filename, "r");
>     > +             if (!fp)
>     > +                     return -1;
>     > +
>     > +             c = fgets(buf, sizeof(buf), fp);
>     > +             if (!c) {
>     > +                     fclose(fp);
>     > +                     return -1;
>     > +             }
>     > +
>     > +             p = strchr(buf, '\n');
>     > +             if (p)
>     > +                     *p = '\0';
>     > +
>     > +             fclose(fp);
>     > +
>     > +             ret = do_write_string(fd, c);
>     > +             if (ret < 0)
>     > +                     return ret;
>     > +             q++;
>     > +     }
>     > +     return 0;
>     > +}
>     > +
>     > +static int write_cpu_topology(int fd, struct perf_header *h __used,
>     > +                       struct perf_evlist *evlist __used)
>     > +{
>     > +     u32 nr, i;
>     > +     long ncpus;
>     > +     int ret;
>     > +
>     > +     ncpus = sysconf(_SC_NPROCESSORS_CONF);
>     > +     if (ncpus < 0)
>     > +             return -1;
>     > +
>     > +     /* hopefully, this will keep compilers happy about the cast */
>     > +     nr = (u32)(ncpus & UINT_MAX);
>     > +
>     > +     ret = do_write(fd, &nr, sizeof(nr));
>     > +     if (ret < 0)
>     > +             return -1;
>     > +
>     > +     for (i = 0; i < nr; i++) {
>     > +             ret = do_write(fd, &i, sizeof(i));
>     > +             if (ret < 0)
>     > +                     return ret;
>     > +
>     > +             ret = write_topo_cpu(fd, i);
>     > +             if (ret < 0)
>     > +                     return ret;
>     > +     }
>     > +     return 0;
>     > +}
>     > +
>     > +static int write_total_mem(int fd, struct perf_header *h __used,
>     > +                       struct perf_evlist *evlist __used)
>     > +{
>     > +     char buf[MAXPATHLEN], *c;
>     > +     FILE *fp;
>     > +     int ret = -1, n;
>     > +     uint64_t mem;
>     > +
>     > +     fp = fopen("/proc/meminfo", "r");
>     > +     if (!fp)
>     > +             return -1;
>     > +
>     > +     while ((c = fgets(buf, sizeof(buf), fp))) {
>     > +             c = strstr(buf, "MemTotal:");
>     > +             if (c)
>     > +                     break;
>     > +     }
>     > +     fclose(fp);
>     > +
>     > +     n = sscanf(buf, "%*s %"PRIu64, &mem);
>     > +     if (n == 1)
>     > +             ret = do_write(fd, &mem, sizeof(mem));
>     > +
>     > +     return ret;
>     > +}
>     > +
>     > +static int write_topo_node(int fd, int node)
>     > +{
>     > +     char filename[MAXPATHLEN];
>     > +     char buf[MAXPATHLEN], *p, *c;
>     > +     FILE *fp;
>     > +     uint64_t mem;
>     > +     int ret;
>     > +
>     > +     sprintf(filename, "/sys/devices/system/node/node%d/meminfo",
>     node);
>     > +     fp = fopen(filename, "r");
>     > +     if (!fp)
>     > +             return -1;
>     > +
>     > +     while ((c = fgets(buf, sizeof(buf), fp))) {
>     > +             c = strstr(buf, "MemTotal:");
>     > +             if (c)
>     > +                     break;
>     > +     }
>     > +     fclose(fp);
>     > +     if (!c)
>     > +             return -1;
>     > +     ret = sscanf(buf, "%*s %*d %*s %"PRIu64, &mem);
>     > +     if (ret != 1)
>     > +             return -1;
>     > +
>     > +     ret = do_write(fd, &mem, sizeof(mem));
>     > +     if (ret < 0)
>     > +             return -1;
>     > +
>     > +     sprintf(filename, "/sys/devices/system/node/node%d/cpulist",
>     node);
>     > +     fp = fopen(filename, "r");
>     > +     if (!fp)
>     > +             return -1;
>     > +
>     > +     c = fgets(buf, sizeof(buf), fp);
>     > +     fclose(fp);
>     > +     if (!c)
>     > +             return -1;
>     > +
>     > +     p = strchr(buf, '\n');
>     > +     if (p)
>     > +             *p = '\0';
>     > +
>     > +     return do_write_string(fd, c);
>     > +}
>     > +
>     > +static int write_numa_topology(int fd, struct perf_header *h __used,
>     > +                       struct perf_evlist *evlist __used)
>     > +{
>     > +     char buf[MAXPATHLEN];
>     > +     FILE *fp;
>     > +     struct cpu_map *node_map;
>     > +     char *c;
>     > +     u32 nr, i, j;
>     > +     int ret;
>     > +
>     > +     fp = fopen("/sys/devices/system/node/online", "r");
>     > +     if (!fp)
>     > +             return -1;
>     > +
>     > +     c = fgets(buf, sizeof(buf), fp);
>     > +     fclose(fp);
>     > +     if (!c)
>     > +             return -1;
>     > +
>     > +     c = strchr(buf, '\n');
>     > +     if (c)
>     > +             *c = '\0';
>     > +
>     > +     node_map = cpu_map__new(buf);
>     > +     if (!node_map)
>     > +             return -1;
>     > +
>     > +     nr = (u32)node_map->nr;
>     > +     ret = do_write(fd, &nr, sizeof(nr));
>     > +     if (ret < 0)
>     > +             return -1;
>     > +
>     > +     for (i = 0; i < nr; i++) {
>     > +             j = (u32)node_map->map[i];
>     > +             ret = do_write(fd, &j, sizeof(j));
>     > +             if (ret < 0)
>     > +                     goto error;
>     > +
>     > +             ret = write_topo_node(fd, i);
>     > +             if (ret < 0)
>     > +                     goto error;
>     > +     }
>     > +     ret = 0;
>     > +error:
>     > +     free(node_map);
>     > +     return ret;
>     > +}
>     > +
>     > +static void print_hostname(struct perf_header *ph, int fd, FILE *fp)
>     > +{
>     > +     char *str = do_read_string(fd, ph);
>     > +     fprintf(fp, "# hostname : %s\n", str);
>     > +     free(str);
>     > +}
>     > +
>     > +static void print_osrelease(struct perf_header *ph, int fd, FILE *fp)
>     > +{
>     > +     char *str = do_read_string(fd, ph);
>     > +     fprintf(fp, "# os release : %s\n", str);
>     > +     free(str);
>     > +}
>     > +
>     > +static void print_arch(struct perf_header *ph, int fd, FILE *fp)
>     > +{
>     > +     char *str = do_read_string(fd, ph);
>     > +     fprintf(fp, "# arch : %s\n", str);
>     > +     free(str);
>     > +}
>     > +
>     > +static void print_cpuid(struct perf_header *ph, int fd, FILE *fp)
>     > +{
>     > +     char *str = do_read_string(fd, ph);
>     > +     fprintf(fp, "# cpuid : %s\n", str);
>     > +     free(str);
>     > +}
>     > +
>     > +static void print_nrcpus(struct perf_header *ph, int fd, FILE *fp)
>     > +{
>     > +     ssize_t ret;
>     > +     u32 nr;
>     > +
>     > +     ret = read(fd, &nr, sizeof(nr));
>     > +     if (ret != (ssize_t)sizeof(nr))
>     > +             nr = -1; /* interpreted as error */
>     > +
>     > +     if (ph->needs_swap)
>     > +             nr = bswap_32(nr);
>     > +
>     > +     fprintf(fp, "# nrcpus online : %u\n", nr);
>     > +
>     > +     ret = read(fd, &nr, sizeof(nr));
>     > +     if (ret != (ssize_t)sizeof(nr))
>     > +             nr = -1; /* interpreted as error */
>     > +
>     > +     if (ph->needs_swap)
>     > +             nr = bswap_32(nr);
>     > +
>     > +     fprintf(fp, "# nrcpus avail : %u\n", nr);
>     > +}
>     > +
>     > +static void print_version(struct perf_header *ph, int fd, FILE *fp)
>     > +{
>     > +     char *str = do_read_string(fd, ph);
>     > +     fprintf(fp, "# perf version : %s\n", str);
>     > +     free(str);
>     > +}
>     > +
>     > +static void print_cmdline(struct perf_header *ph, int fd, FILE *fp)
>     > +{
>     > +     ssize_t ret;
>     > +     char *str;
>     > +     u32 nr, i;
>     > +
>     > +     ret = read(fd, &nr, sizeof(nr));
>     > +     if (ret != (ssize_t)sizeof(nr)) {
>     > +             fprintf(fp, "# cmdline : unknown\n");
>     > +             return;
>     > +     }
>     > +
>     > +     if (ph->needs_swap)
>     > +             nr = bswap_32(nr);
>     > +
>     > +     fprintf(fp, "# cmdline : ");
>     > +
>     > +     for (i = 0; i < nr; i++) {
>     > +             str = do_read_string(fd, ph);
>     > +             fprintf(fp, "%s ", str);
>     > +             free(str);
>     > +     }
>     > +     fputc('\n', fp);
>     > +}
>     > +
>     > +static void print_cpu_topology(struct perf_header *ph, int fd,
>     FILE *fp)
>     > +{
>     > +     ssize_t ret;
>     > +     u32 nr, c, i;
>     > +     char *str;
>     > +
>     > +     ret = read(fd, &nr, sizeof(nr));
>     > +     if (ret != (ssize_t)sizeof(nr)) {
>     > +             fprintf(fp, "# cpu topology  : not available\n");
>     > +             return;
>     > +     }
>     > +
>     > +     if (ph->needs_swap)
>     > +             nr = bswap_32(nr);
>     > +
>     > +     for (i = 0; i < nr; i++) {
>     > +
>     > +             ret = read(fd, &c, sizeof(c));
>     > +             if (ret != (ssize_t)sizeof(c))
>     > +                     c = -1; /* interpreted as error */
>     > +
>     > +             if (ph->needs_swap)
>     > +                     c = bswap_32(c);
>     > +
>     > +             str = do_read_string(fd, ph);
>     > +             fprintf(fp, "# CPU%u sibling cores  : %s\n", c, str);
>     > +             free(str);
>     > +
>     > +             str = do_read_string(fd, ph);
>     > +             fprintf(fp, "# CPU%u sibling threads: %s\n", c, str);
>     > +             free(str);
>     > +     }
>     > +}
>     > +
>     > +static void print_event_desc(struct perf_header *ph, int fd, FILE
>     *fp)
>     > +{
>     > +     struct perf_event_attr attr;
>     > +     uint64_t id;
>     > +     void *buf = NULL;
>     > +     char *str;
>     > +     u32 nre, sz, nr, i, j, msz;
>     > +     int ret;
>     > +
>     > +     /* number of events */
>     > +     ret = read(fd, &nre, sizeof(nre));
>     > +     if (ret != (ssize_t)sizeof(nre))
>     > +             goto error;
>     > +
>     > +     if (ph->needs_swap)
>     > +             nre = bswap_32(nre);
>     > +
>     > +     ret = read(fd, &sz, sizeof(sz));
>     > +     if (ret != (ssize_t)sizeof(sz))
>     > +             goto error;
>     > +
>     > +     if (ph->needs_swap)
>     > +             sz = bswap_32(sz);
>     > +
>     > +     /*
>     > +      * ensure it is at least to our ABI rev
>     > +      */
>     > +     if (sz < (u32)sizeof(attr))
>     > +             goto error;
>     > +
>     > +     memset(&attr, 0, sizeof(attr));
>     > +
>     > +     /* read entire region to sync up to next field */
>     > +     buf = malloc(sz);
>     > +     if (!buf)
>     > +             goto error;
>     > +
>     > +     msz = sizeof(attr);
>     > +     if (sz < msz)
>     > +             msz = sz;
>     > +
>     > +     for (i = 0 ; i < nre; i++) {
>     > +
>     > +             ret = read(fd, buf, sz);
>     > +             if (ret != (ssize_t)sz)
>     > +                     goto error;
>     > +
>     > +             if (ph->needs_swap)
>     > +                     perf_event__attr_swap(buf);
>     > +
>     > +             memcpy(&attr, buf, msz);
>     > +
>     > +             ret = read(fd, &nr, sizeof(nr));
>     > +             if (ret != (ssize_t)sizeof(nr))
>     > +                     goto error;
>     > +
>     > +             if (ph->needs_swap)
>     > +                     nr = bswap_32(nr);
>     > +
>     > +             str = do_read_string(fd, ph);
>     > +             fprintf(fp, "# event = %s, ", str);
>     > +             free(str);
>     > +
>     > +             fprintf(fp, "type = %d, config = 0x%"PRIx64
>     > +                         ", config1 = 0x%"PRIx64", config2 =
>     0x%"PRIx64,
>     > +                             attr.type,
>     > +                             (u64)attr.config,
>     > +                             (u64)attr.config1,
>     > +                             (u64)attr.config2);
>     > +
>     > +             fprintf(fp, ", excl_usr = %d, excl_kern = %d",
>     > +                             attr.exclude_user,
>     > +                             attr.exclude_kernel);
>     > +
>     > +             if (nr)
>     > +                     fprintf(fp, ", id = {");
>     > +
>     > +             for (j = 0 ; j < nr; j++) {
>     > +                     ret = read(fd, &id, sizeof(id));
>     > +                     if (ret != (ssize_t)sizeof(id))
>     > +                             goto error;
>     > +
>     > +                     if (ph->needs_swap)
>     > +                             id = bswap_64(id);
>     > +
>     > +                     if (j)
>     > +                             fputc(',', fp);
>     > +
>     > +                     fprintf(fp, " %"PRIu64, id);
>     > +             }
>     > +             if (nr && j == nr)
>     > +                     fprintf(fp, " }");
>     > +             fputc('\n', fp);
>     > +     }
>     > +     free(buf);
>     > +     return;
>     > +error:
>     > +     fprintf(fp, "# event desc: not available or unable to read\n");
>     > +}
>     > +
>     > +static void print_total_mem(struct perf_header *h __used, int fd,
>     FILE *fp)
>     > +{
>     > +     uint64_t mem;
>     > +     ssize_t ret;
>     > +
>     > +     ret = read(fd, &mem, sizeof(mem));
>     > +     if (ret != sizeof(mem))
>     > +             goto error;
>     > +
>     > +     if (h->needs_swap)
>     > +             mem = bswap_64(mem);
>     > +
>     > +     fprintf(fp, "# total memory: %"PRIu64" kB\n", mem);
>     > +     return;
>     > +error:
>     > +     fprintf(fp, "# total memory: unknown\n");
>     > +}
>     > +
>     > +static void print_numa_topology(struct perf_header *h __used, int
>     fd, FILE *fp)
>     > +{
>     > +     ssize_t ret;
>     > +     u32 nr, c, i;
>     > +     char *str;
>     > +     uint64_t mem;
>     > +
>     > +     /* nr nodes */
>     > +     ret = read(fd, &nr, sizeof(nr));
>     > +     if (ret != (ssize_t)sizeof(nr))
>     > +             goto error;
>     > +
>     > +     if (h->needs_swap)
>     > +             nr = bswap_32(nr);
>     > +
>     > +     for (i = 0; i < nr; i++) {
>     > +
>     > +             /* node number */
>     > +             ret = read(fd, &c, sizeof(c));
>     > +             if (ret != (ssize_t)sizeof(c))
>     > +                     goto error;
>     > +
>     > +             if (h->needs_swap)
>     > +                     c = bswap_32(c);
>     > +
>     > +             ret = read(fd, &mem, sizeof(mem));
>     > +             if (ret != sizeof(mem))
>     > +                     goto error;
>     > +
>     > +             if (h->needs_swap)
>     > +                     mem = bswap_64(mem);
>     > +
>     > +             fprintf(fp, "# node%u meminfo : %"PRIu64" kB\n", c,
>     mem);
>     > +
>     > +             str = do_read_string(fd, h);
>     > +             fprintf(fp, "# node%u cpu list: %s\n", c, str);
>     > +             free(str);
>     > +     }
>     > +     return;
>     > +error:
>     > +     fprintf(fp, "# numa topology : not available\n");
>     > +}
>     > +
>     > +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);
>     > +};
>     > +
>     > +#define FEAT_OP(n, w, p) [n] = { .write = w, .print = p }
>     > +
>     > +static struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
>     > +     FEAT_OP(HEADER_TRACE_INFO, write_trace_info, NULL),
>     > +     FEAT_OP(HEADER_BUILD_ID, write_build_id, NULL),
>     > +     FEAT_OP(HEADER_HOSTNAME, write_hostname, print_hostname),
>     > +     FEAT_OP(HEADER_OSRELEASE, write_osrelease, print_osrelease),
>     > +     FEAT_OP(HEADER_ARCH, write_arch, print_arch),
>     > +     FEAT_OP(HEADER_CPUID, write_cpuid, print_cpuid),
>     > +     FEAT_OP(HEADER_NRCPUS, write_nrcpus, print_nrcpus),
>     > +     FEAT_OP(HEADER_EVENT_DESC, write_event_desc, print_event_desc),
>     > +     FEAT_OP(HEADER_CMDLINE, write_cmdline, print_cmdline),
>     > +     FEAT_OP(HEADER_VERSION, write_version, print_version),
>     > +     FEAT_OP(HEADER_CPU_TOPOLOGY, write_cpu_topology,
>     print_cpu_topology),
>     > +     FEAT_OP(HEADER_TOTAL_MEM, write_total_mem, print_total_mem),
>     > +     FEAT_OP(HEADER_NUMA_TOPOLOGY, write_numa_topology,
>     print_numa_topology),
>     > +};
>     > +
>     > +static int perf_header_fprintf_info(struct perf_file_section
>     *section,
>     > +                                 struct perf_header *ph,
>     > +                                 int feat, int fd, void *data)
>     > +{
>     > +     FILE *fp = data;
>     > +
>     > +     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_TRACE_INFO || feat >= HEADER_LAST_FEATURE) {
>     > +             pr_warning("unknown feature %d\n", feat);
>     > +             return -1;
>     > +     }
>     > +
>     > +     if (feat_ops[feat].print)
>     > +             feat_ops[feat].print(ph, fd, fp);
>     > +
>     > +     return 0;
>     > +}
>     > +
>     > +int perf_header__fprintf_info(struct perf_session *session, FILE
>     *fp __used)
>     > +{
>     > +     int fd = session->fd;
>     > +     struct perf_header *header = &session->header;
>     > +     perf_header__process_sections(header, fd, fp,
>     perf_header_fprintf_info);
>     > +     return 0;
>     > +}
>     > +
>     >  #define dsos__for_each_with_build_id(pos, head)      \
>     >       list_for_each_entry(pos, head, node)    \
>     >               if (!pos->has_build_id)         \
>     > @@ -356,15 +1201,41 @@ static bool
>     perf_session__read_build_ids(struct perf_session *session, bool with
>     >       return ret;
>     >  }
>     >
>     > +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_header__has_feat(h, type)) {
>     > +
>     > +             (*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;
>     > +}
>     > +
>     >  static int perf_header__adds_write(struct perf_header *header,
>     >                                  struct perf_evlist *evlist, int fd)
>     >  {
>     >       int nr_sections;
>     >       struct perf_session *session;
>     > -     struct perf_file_section *feat_sec;
>     > +     struct perf_file_section *feat_sec, *p;
>     >       int sec_size;
>     >       u64 sec_start;
>     > -     int idx = 0, err;
>     > +     int err;
>     >
>     >       session = container_of(header, struct perf_session, header);
>     >
>     > @@ -376,7 +1247,7 @@ static int perf_header__adds_write(struct
>     perf_header *header,
>     >       if (!nr_sections)
>     >               return 0;
>     >
>     > -     feat_sec = calloc(sizeof(*feat_sec), nr_sections);
>     > +     feat_sec = p = calloc(sizeof(*feat_sec), nr_sections);
>     >       if (feat_sec == NULL)
>     >               return -ENOMEM;
>     >
>     > @@ -385,36 +1256,65 @@ static int perf_header__adds_write(struct
>     perf_header *header,
>     >       sec_start = header->data_offset + header->data_size;
>     >       lseek(fd, sec_start + sec_size, SEEK_SET);
>     >
>     > -     if (perf_header__has_feat(header, HEADER_TRACE_INFO)) {
>     > -             struct perf_file_section *trace_sec;
>     > -
>     > -             trace_sec = &feat_sec[idx++];
>     > +     err = do_write_feat(fd, header, HEADER_TRACE_INFO, &p, evlist);
>     > +     if (err)
>     > +             goto out_free;
>     >
>     > -             /* Write trace info */
>     > -             trace_sec->offset = lseek(fd, 0, SEEK_CUR);
>     > -             read_tracing_data(fd, &evlist->entries);
>     > -             trace_sec->size = lseek(fd, 0, SEEK_CUR) -
>     trace_sec->offset;
>     > +     err = do_write_feat(fd, header, HEADER_BUILD_ID, &p, evlist);
>     > +     if (err) {
>     > +             perf_header__clear_feat(header, HEADER_BUILD_ID);
>     > +             goto out_free;
>     >       }
>     >
>     > -     if (perf_header__has_feat(header, HEADER_BUILD_ID)) {
>     > -             struct perf_file_section *buildid_sec;
>     > +     err = do_write_feat(fd, header, HEADER_HOSTNAME, &p, evlist);
>     > +     if (err)
>     > +             perf_header__clear_feat(header, HEADER_HOSTNAME);
>     >
>     > -             buildid_sec = &feat_sec[idx++];
>     > +     err = do_write_feat(fd, header, HEADER_OSRELEASE, &p, evlist);
>     > +     if (err)
>     > +             perf_header__clear_feat(header, HEADER_OSRELEASE);
>     >
>     > -             /* Write build-ids */
>     > -             buildid_sec->offset = lseek(fd, 0, SEEK_CUR);
>     > -             err = dsos__write_buildid_table(header, 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;
>     > -             if (!no_buildid_cache)
>     > -                     perf_session__cache_build_ids(session);
>     > -     }
>     > +     err = do_write_feat(fd, header, HEADER_ARCH, &p, evlist);
>     > +     if (err)
>     > +             perf_header__clear_feat(header, HEADER_ARCH);
>     > +
>     > +     err = do_write_feat(fd, header, HEADER_CPUID, &p, evlist);
>     > +     if (err)
>     > +             perf_header__clear_feat(header, HEADER_CPUID);
>     > +
>     > +     err = do_write_feat(fd, header, HEADER_NRCPUS, &p, evlist);
>     > +     if (err)
>     > +             perf_header__clear_feat(header, HEADER_NRCPUS);
>     > +
>     > +     err = do_write_feat(fd, header, HEADER_EVENT_DESC, &p, evlist);
>     > +     if (err)
>     > +             perf_header__clear_feat(header, HEADER_EVENT_DESC);
>     > +
>     > +     err = do_write_feat(fd, header, HEADER_CMDLINE, &p, evlist);
>     > +     if (err)
>     > +             perf_header__clear_feat(header, HEADER_CMDLINE);
>     > +
>     > +     err = do_write_feat(fd, header, HEADER_VERSION, &p, evlist);
>     > +     if (err)
>     > +             perf_header__clear_feat(header, HEADER_VERSION);
>     > +
>     > +     err = do_write_feat(fd, header, HEADER_CPU_TOPOLOGY, &p,
>     evlist);
>     > +     if (err)
>     > +             perf_header__clear_feat(header, HEADER_CPU_TOPOLOGY);
>     > +
>     > +     err = do_write_feat(fd, header, HEADER_TOTAL_MEM, &p, evlist);
>     > +     if (err)
>     > +             perf_header__clear_feat(header, HEADER_TOTAL_MEM);
>     > +
>     > +     err = do_write_feat(fd, header, HEADER_NUMA_TOPOLOGY, &p,
>     evlist);
>     > +     if (err)
>     > +             perf_header__clear_feat(header, HEADER_NUMA_TOPOLOGY);
>     >
>     >       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");
>     > @@ -554,9 +1454,10 @@ static int perf_header__getbuffer64(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))
>     > +                               struct perf_header *ph,
>     > +                               int feat, int fd, void *data))
>     >  {
>     >       struct perf_file_section *feat_sec;
>     >       int nr_sections;
>     > @@ -584,7 +1485,7 @@ int perf_header__process_sections(struct
>     perf_header *header, int fd,
>     >               if (perf_header__has_feat(header, feat)) {
>     >                       struct perf_file_section *sec =
>     &feat_sec[idx++];
>     >
>     > -                     err = process(sec, header, feat, fd);
>     > +                     err = process(sec, header, feat, fd, data);
>     >                       if (err < 0)
>     >                               break;
>     >               }
>     > @@ -796,7 +1697,7 @@ out:
>     >
>     >  static int perf_file_section__process(struct perf_file_section
>     *section,
>     >                                     struct perf_header *ph,
>     > -                                   int feat, int fd)
>     > +                                   int feat, int fd, void *data
>     __used)
>     >  {
>     >       if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) {
>     >               pr_debug("Failed to lseek to %" PRIu64 " offset for
>     feature "
>     > @@ -935,7 +1836,8 @@ int perf_session__read_header(struct
>     perf_session *session, int fd)
>     >               event_count =  f_header.event_types.size /
>     sizeof(struct perf_trace_event_type);
>     >       }
>     >
>     > -     perf_header__process_sections(header, fd,
>     perf_file_section__process);
>     > +     perf_header__process_sections(header, fd, NULL,
>     > +                                   perf_file_section__process);
>     >
>     >       lseek(fd, header->data_offset, SEEK_SET);
>     >
>     > diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
>     > index 1886256..455480b 100644
>     > --- a/tools/perf/util/header.h
>     > +++ b/tools/perf/util/header.h
>     > @@ -12,6 +12,19 @@
>     >  enum {
>     >       HEADER_TRACE_INFO = 1,
>     >       HEADER_BUILD_ID,
>     > +
>     > +     HEADER_HOSTNAME,
>     > +     HEADER_OSRELEASE,
>     > +     HEADER_ARCH,
>     > +     HEADER_CPUID,
>     > +     HEADER_NRCPUS,
>     > +     HEADER_EVENT_DESC,
>     > +     HEADER_CMDLINE,
>     > +     HEADER_VERSION,
>     > +     HEADER_CPU_TOPOLOGY,
>     > +     HEADER_TOTAL_MEM,
>     > +     HEADER_NUMA_TOPOLOGY,
>     > +
>     >       HEADER_LAST_FEATURE,
>     >  };
>     >
>     > @@ -68,10 +81,15 @@ void perf_header__set_feat(struct perf_header
>     *header, int feat);
>     >  void perf_header__clear_feat(struct perf_header *header, int feat);
>     >  bool perf_header__has_feat(const struct perf_header *header, int
>     feat);
>     >
>     > +int perf_header__set_cmdline(int argc, const char **argv);
>     > +
>     >  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));
>     > +                               struct perf_header *ph,
>     > +                               int feat, int fd, void *data));
>     > +
>     > +int perf_header__fprintf_info(struct perf_session *session, FILE
>     *fp);
>     >
>     >  int build_id_cache__add_s(const char *sbuild_id, const char
>     *debugdir,
>     >                         const char *name, bool is_kallsyms);
>     > diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
>     > index 72458d9..54613a2 100644
>     > --- a/tools/perf/util/session.c
>     > +++ b/tools/perf/util/session.c
>     > @@ -1326,3 +1326,21 @@ int perf_session__cpu_bitmap(struct
>     perf_session *session,
>     >
>     >       return 0;
>     >  }
>     > +
>     > +void perf_session__fprintf_info(struct perf_session *session,
>     FILE *fp)
>     > +{
>     > +     struct stat st;
>     > +     int ret;
>     > +
>     > +     if (session == NULL || fp == NULL)
>     > +             return;
>     > +
>     > +     ret = fstat(session->fd, &st);
>     > +     if (ret == -1)
>     > +             return;
>     > +
>     > +     fprintf(fp, "#\n# captured on: %s", ctime(&st.st_ctime));
>     > +     ret = perf_header__fprintf_info(session, fp);
>     > +     if (ret == 0)
>     > +             fprintf(fp, "#\n");
>     > +}
>     > diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
>     > index 170601e..33a3a46 100644
>     > --- a/tools/perf/util/session.h
>     > +++ b/tools/perf/util/session.h
>     > @@ -176,4 +176,5 @@ void perf_session__print_ip(union perf_event
>     *event,
>     >  int perf_session__cpu_bitmap(struct perf_session *session,
>     >                            const char *cpu_list, unsigned long
>     *cpu_bitmap);
>     >
>     > +void perf_session__fprintf_info(struct perf_session *session,
>     FILE *fp);
>     >  #endif /* __PERF_SESSION_H */
> 
> 

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] perf: make perf.data more self-descriptive (v4)
  2011-09-08 15:27 ` David Ahern
       [not found]   ` <CABPqkBRKcxeTDMyVYPXU6AQEY-Hp2bnE03ebEtvjYWde2JKqoQ@mail.gmail.com>
@ 2011-09-08 15:42   ` Stephane Eranian
  1 sibling, 0 replies; 21+ messages in thread
From: Stephane Eranian @ 2011-09-08 15:42 UTC (permalink / raw)
  To: David Ahern; +Cc: linux-kernel, acme, peterz, mingo

David,

Something I don't quite understand with your issues with endianess.

How does endianess change anything to a int32 (aka int) vs. uint32?

The byte swapping you need to do is regardless of the sign of
the data or am I missing something here?


On Thu, Sep 8, 2011 at 5:27 PM, David Ahern <dsahern@gmail.com> wrote:
>
>
> On 09/07/2011 01:10 PM, Stephane Eranian wrote:
> >
> > The goal of this patch is to include more information
> > about the host environment into the perf.data so it is
> > more self-descriptive. Overtime, profiles are captured
> > on various machines and it becomes hard to track what
> > was recorded, on what machine and when.
> >
> > This patch provides a way to solve this by extending
> > the perf.data file with basic information about the
> > host machine. To add those extensions, we leverage
> > the feature bits capabilities of the perf.data format.
> > The change is backward compatible with existing perf.data
> > files.
> >
> > We define the following useful new extensions:
> >  - HEADER_HOSTNAME: the hostname
> >  - HEADER_OSRELEASE: the kernel release number
> >  - HEADER_ARCH: the hw architecture
> >  - HEADER_CPUID: the cpu model description
> >  - HEADER_NRCPUS: number of online/avail cpus
> >  - HEADER_CMDLINE: perf command line
> >  - HEADER_VERSION: perf version
> >  - HEADER_TOPOLOGY: cpu topology
> >  - HEADER_EVENT_DESC: full event description (attrs)
> >
> > The small granularity for the entries is to make it easier
> > to extend without breaking backward compatiblity. All entries
> > are provided as ASCII strings, easy to parse, except for the
> > event description.
> >
> > In the second version, we dropped the -I option for the
> > perf record command. The extra information is systematically
> > captured. But it's still displayed optionally by perf report.
> > There are also a couple of cleanups of function prototypes
> > and global variables.
> >
> > In the third version, we:
> > - fixed missing MIPS support for CPUID
> > - used u32 instead of int when writing integers to file
> > - fixed couple of bswap() bugs
> > - refactorize all write/print functions using function pointers
> > - improved write_cmdline() to include actual path to perf binary
> >
> > In the fourth version, we:
> > - fixed a couple of int vs. u32 issues
> > - factorized HEADER_BUILD_ID, HEADER_TRACE_INFO
> > - added -I option to perf script report
> > - reverted change to evsel.c (unrelated to meta-data)
> > - modified write_*() to always write despite errors (adds_feature bit present)
> > - added HEADER_TOTAL_MEM: total memory installed on this system
> > - added HEADER_NUMA_TOPOLOGY: shows NUMA nodes and for each memory, cpu list
> > - renamed HEADER_TOPOLOGY to HEADER_CPU_TOPOLOGY
>
> Still some endianness problems; cross-endian analysis is causing perf to
> abort. I need some time to investigate. I can send you a ppc data file
> if you have the time.
>
> David
>
> >
> >
> > $ perf record -ecycles,instructions noploop 1
> > noploop for 1 seconds
> > [ perf record: Woken up 1 times to write data ]
> > [ perf record: Captured and wrote 0.098 MB perf.data (~4260 samples) ]
> > $ perf report -I --stdio
> > #
> > # captured on: Wed Sep  7 21:00:10 2011
> > # hostname : quad
> > # os release : 3.1.0-rc4-tip
> > # arch : x86_64
> > # cpuid : Intel(R) Core(TM)2 Quad CPU Q6600 @ 2.40GHz
> > # nrcpus online : 4
> > # nrcpus avail : 4
> > # event = cycles, type = 0, config = 0x0, config1 = 0x0, config2 = 0x0, excl_usr = 0, excl_kern = 0, id = { 536, 537, 538, 539 }
> > # event = instructions, type = 0, config = 0x1, config1 = 0x0, config2 = 0x0, excl_usr = 0, excl_kern = 0, id = { 540, 541, 542,
> > # cmdline : /home/eranian/perfmon/official/tip/build/tools/perf/perf record -ecycles,instructions noploop 1
> > # perf version : 3.1.0-rc4
> > # CPU0   sibling cores  : 0-3
> > # CPU0   sibling threads: 0
> > # CPU1   sibling cores  : 0-3
> > # CPU1   sibling threads: 1
> > # CPU2   sibling cores  : 0-3
> > # CPU2   sibling threads: 2
> > # CPU3   sibling cores  : 0-3
> > # CPU3   sibling threads: 3
> > # total memory: 8105344 kB
> > # node0   meminfo : 8320608 kB
> > # node0   cpu list: 0-3
> > #
> > # Events: 995  cycles
> > #
> > # Overhead  Command      Shared Object                Symbol
> > # ........  .......  .................  ....................
> > #
> >     99.90%  noploop  noploop            [.] noploop
> >      0.10%  noploop  [kernel.kallsyms]  [k] trace_hardirqs_on
> >      0.00%  noploop  [kernel.kallsyms]  [k] lock_release
> >      0.00%  noploop  [kernel.kallsyms]  [k] intel_pmu_enable_all
> > ...
> >
> >
> > Signed-off-by: Stephane Eranian <eranian@google.com>
> > ---
> >
> > diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt
> > index 04253c0..191e10e 100644
> > --- a/tools/perf/Documentation/perf-report.txt
> > +++ b/tools/perf/Documentation/perf-report.txt
> > @@ -134,6 +134,11 @@ OPTIONS
> >       CPUs are specified with -: 0-2. Default is to report samples on all
> >       CPUs.
> >
> > +-I::
> > +--show-info::
> > +     Display information about the perf.data file, incl. hostname,
> > +     os release, perf version, machine desc.
> > +
> >  SEE ALSO
> >  --------
> >  linkperf:perf-stat[1]
> > diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt
> > index db01786..ebf8a23 100644
> > --- a/tools/perf/Documentation/perf-script.txt
> > +++ b/tools/perf/Documentation/perf-script.txt
> > @@ -188,6 +188,13 @@ OPTIONS
> >       CPUs are specified with -: 0-2. Default is to report samples on all
> >       CPUs.
> >
> > +-I::
> > +--show-info::
> > +     Display information about the perf.data file, incl. hostname,
> > +     os release, perf version, machine desc. It can only be used with the
> > +     perf script report mode and it must be specified after the script name
> > +     like for instance: perf script report syscall-counts -I.
> > +
> >  SEE ALSO
> >  --------
> >  linkperf:perf-record[1], linkperf:perf-script-perl[1],
> > diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
> > index 6b0519f..524ebbf 100644
> > --- a/tools/perf/builtin-record.c
> > +++ b/tools/perf/builtin-record.c
> > @@ -513,6 +513,18 @@ static int __cmd_record(int argc, const char **argv)
> >       if (have_tracepoints(&evsel_list->entries))
> >               perf_header__set_feat(&session->header, HEADER_TRACE_INFO);
> >
> > +     perf_header__set_feat(&session->header, HEADER_HOSTNAME);
> > +     perf_header__set_feat(&session->header, HEADER_OSRELEASE);
> > +     perf_header__set_feat(&session->header, HEADER_ARCH);
> > +     perf_header__set_feat(&session->header, HEADER_CPUID);
> > +     perf_header__set_feat(&session->header, HEADER_NRCPUS);
> > +     perf_header__set_feat(&session->header, HEADER_EVENT_DESC);
> > +     perf_header__set_feat(&session->header, HEADER_CMDLINE);
> > +     perf_header__set_feat(&session->header, HEADER_VERSION);
> > +     perf_header__set_feat(&session->header, HEADER_CPU_TOPOLOGY);
> > +     perf_header__set_feat(&session->header, HEADER_TOTAL_MEM);
> > +     perf_header__set_feat(&session->header, HEADER_NUMA_TOPOLOGY);
> > +
> >       /* 512 kiB: default amount of unprivileged mlocked memory */
> >       if (mmap_pages == UINT_MAX)
> >               mmap_pages = (512 * 1024) / page_size;
> > @@ -782,6 +794,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
> >       int err = -ENOMEM;
> >       struct perf_evsel *pos;
> >
> > +     perf_header__set_cmdline(argc, argv);
> > +
> >       evsel_list = perf_evlist__new(NULL, NULL);
> >       if (evsel_list == NULL)
> >               return -ENOMEM;
> > diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
> > index d7ff277..7721030 100644
> > --- a/tools/perf/builtin-report.c
> > +++ b/tools/perf/builtin-report.c
> > @@ -40,6 +40,7 @@ static char         const *input_name = "perf.data";
> >  static bool          force, use_tui, use_stdio;
> >  static bool          hide_unresolved;
> >  static bool          dont_use_callchains;
> > +static bool          show_info;
> >
> >  static bool          show_threads;
> >  static struct perf_read_values       show_threads_values;
> > @@ -276,6 +277,9 @@ static int __cmd_report(void)
> >                       goto out_delete;
> >       }
> >
> > +     if (show_info)
> > +             perf_session__fprintf_info(session, stdout);
> > +
> >       if (show_threads)
> >               perf_read_values_init(&show_threads_values);
> >
> > @@ -487,6 +491,8 @@ static const struct option options[] = {
> >       OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
> >                   "Look for files with symbols relative to this directory"),
> >       OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"),
> > +     OPT_BOOLEAN('I', "show-info", &show_info,
> > +                     "display information about perf.data file"),
> >       OPT_END()
> >  };
> >
> > diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
> > index 09024ec..dac86ab 100644
> > --- a/tools/perf/builtin-script.c
> > +++ b/tools/perf/builtin-script.c
> > @@ -22,6 +22,7 @@ static u64                  last_timestamp;
> >  static u64                   nr_unordered;
> >  extern const struct option   record_options[];
> >  static bool                  no_callchain;
> > +static bool                  show_info = false;
> >  static const char            *cpu_list;
> >  static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
> >
> > @@ -1083,7 +1084,8 @@ static const struct option options[] = {
> >                    "comma separated output fields prepend with 'type:'. Valid types: hw,sw,trace,raw. Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso,addr",
> >                    parse_output_fields),
> >       OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"),
> > -
> > +     OPT_BOOLEAN('I', "show-info", &show_info,
> > +                     "display host information from perf.data file"),
> >       OPT_END()
> >  };
> >
> > @@ -1268,6 +1270,9 @@ int cmd_script(int argc, const char **argv, const char *prefix __used)
> >                       return -1;
> >       }
> >
> > +     if (show_info)
> > +             perf_session__fprintf_info(session, stdout);
> > +
> >       if (!no_callchain)
> >               symbol_conf.use_callchain = true;
> >       else
> > diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h
> > index 4702e24..b382bd5 100644
> > --- a/tools/perf/builtin.h
> > +++ b/tools/perf/builtin.h
> > @@ -4,7 +4,6 @@
> >  #include "util/util.h"
> >  #include "util/strbuf.h"
> >
> > -extern const char perf_version_string[];
> >  extern const char perf_usage_string[];
> >  extern const char perf_more_info_string[];
> >
> > diff --git a/tools/perf/perf.h b/tools/perf/perf.h
> > index a5fc660..08b0b5e 100644
> > --- a/tools/perf/perf.h
> > +++ b/tools/perf/perf.h
> > @@ -9,18 +9,21 @@ void get_term_dimensions(struct winsize *ws);
> >  #include "../../arch/x86/include/asm/unistd.h"
> >  #define rmb()                asm volatile("lock; addl $0,0(%%esp)" ::: "memory")
> >  #define cpu_relax()  asm volatile("rep; nop" ::: "memory");
> > +#define CPUINFO_PROC "model name"
> >  #endif
> >
> >  #if defined(__x86_64__)
> >  #include "../../arch/x86/include/asm/unistd.h"
> >  #define rmb()                asm volatile("lfence" ::: "memory")
> >  #define cpu_relax()  asm volatile("rep; nop" ::: "memory");
> > +#define CPUINFO_PROC "model name"
> >  #endif
> >
> >  #ifdef __powerpc__
> >  #include "../../arch/powerpc/include/asm/unistd.h"
> >  #define rmb()                asm volatile ("sync" ::: "memory")
> >  #define cpu_relax()  asm volatile ("" ::: "memory");
> > +#define CPUINFO_PROC "cpu"
> >  #endif
> >
> >  #ifdef __s390__
> > @@ -37,30 +40,35 @@ void get_term_dimensions(struct winsize *ws);
> >  # define rmb()               asm volatile("" ::: "memory")
> >  #endif
> >  #define cpu_relax()  asm volatile("" ::: "memory")
> > +#define CPUINFO_PROC "cpu type"
> >  #endif
> >
> >  #ifdef __hppa__
> >  #include "../../arch/parisc/include/asm/unistd.h"
> >  #define rmb()                asm volatile("" ::: "memory")
> >  #define cpu_relax()  asm volatile("" ::: "memory");
> > +#define CPUINFO_PROC "cpu"
> >  #endif
> >
> >  #ifdef __sparc__
> >  #include "../../arch/sparc/include/asm/unistd.h"
> >  #define rmb()                asm volatile("":::"memory")
> >  #define cpu_relax()  asm volatile("":::"memory")
> > +#define CPUINFO_PROC "cpu"
> >  #endif
> >
> >  #ifdef __alpha__
> >  #include "../../arch/alpha/include/asm/unistd.h"
> >  #define rmb()                asm volatile("mb" ::: "memory")
> >  #define cpu_relax()  asm volatile("" ::: "memory")
> > +#define CPUINFO_PROC "cpu model"
> >  #endif
> >
> >  #ifdef __ia64__
> >  #include "../../arch/ia64/include/asm/unistd.h"
> >  #define rmb()                asm volatile ("mf" ::: "memory")
> >  #define cpu_relax()  asm volatile ("hint @pause" ::: "memory")
> > +#define CPUINFO_PROC "model name"
> >  #endif
> >
> >  #ifdef __arm__
> > @@ -71,6 +79,7 @@ void get_term_dimensions(struct winsize *ws);
> >   */
> >  #define rmb()                ((void(*)(void))0xffff0fa0)()
> >  #define cpu_relax()  asm volatile("":::"memory")
> > +#define CPUINFO_PROC "Processor"
> >  #endif
> >
> >  #ifdef __mips__
> > @@ -83,6 +92,7 @@ void get_term_dimensions(struct winsize *ws);
> >                               : /* no input */                        \
> >                               : "memory")
> >  #define cpu_relax()  asm volatile("" ::: "memory")
> > +#define CPUINFO_PROC "cpu model"
> >  #endif
> >
> >  #include <time.h>
> > @@ -171,5 +181,6 @@ struct ip_callchain {
> >  };
> >
> >  extern bool perf_host, perf_guest;
> > +extern const char perf_version_string[];
> >
> >  #endif
> > diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
> > index b6c1ad1..73fa59e 100644
> > --- a/tools/perf/util/header.c
> > +++ b/tools/perf/util/header.c
> > @@ -7,6 +7,7 @@
> >  #include <stdlib.h>
> >  #include <linux/list.h>
> >  #include <linux/kernel.h>
> > +#include <sys/utsname.h>
> >
> >  #include "evlist.h"
> >  #include "evsel.h"
> > @@ -17,12 +18,19 @@
> >  #include "session.h"
> >  #include "symbol.h"
> >  #include "debug.h"
> > +#include "cpumap.h"
> >
> >  static bool no_buildid_cache = false;
> >
> >  static int event_count;
> >  static struct perf_trace_event_type *events;
> >
> > +static u32 header_argc;
> > +static const char **header_argv;
> > +
> > +static int dsos__write_buildid_table(struct perf_header *header, int fd);
> > +static int perf_session__cache_build_ids(struct perf_session *session);
> > +
> >  int perf_header__push_event(u64 id, const char *name)
> >  {
> >       if (strlen(name) > MAX_EVENT_NAME)
> > @@ -110,6 +118,843 @@ static int write_padded(int fd, const void *bf, size_t count,
> >       return err;
> >  }
> >
> > +static int do_write_string(int fd, const char *str)
> > +{
> > +     u32 len, olen;
> > +     int ret;
> > +
> > +     olen = strlen(str) + 1;
> > +     len = ALIGN(olen, NAME_ALIGN);
> > +
> > +     /* write len, incl. \0 */
> > +     ret = do_write(fd, &len, sizeof(len));
> > +     if (ret < 0)
> > +             return ret;
> > +
> > +     return write_padded(fd, str, olen, len);
> > +}
> > +
> > +static char *do_read_string(int fd, struct perf_header *ph)
> > +{
> > +     ssize_t sz, ret;
> > +     u32 len;
> > +     char *buf;
> > +
> > +     sz = read(fd, &len, sizeof(len));
> > +     if (sz < (ssize_t)sizeof(len))
> > +             return NULL;
> > +
> > +     if (ph->needs_swap)
> > +             len = bswap_32(len);
> > +
> > +     buf = malloc(len);
> > +     if (!buf)
> > +             return NULL;
> > +
> > +     ret = read(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;
> > +     }
> > +
> > +     free(buf);
> > +     return NULL;
> > +}
> > +
> > +int
> > +perf_header__set_cmdline(int argc, const char **argv)
> > +{
> > +     int i;
> > +
> > +     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;
> > +}
> > +
> > +static int write_trace_info(int fd, struct perf_header *h __used,
> > +                         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 __used)
> > +{
> > +     struct perf_session *session;
> > +     int err;
> > +
> > +     session = container_of(h, struct perf_session, header);
> > +
> > +     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);
> > +
> > +     return 0;
> > +}
> > +
> > +static int write_hostname(int fd, struct perf_header *h __used,
> > +                       struct perf_evlist *evlist __used)
> > +{
> > +     struct utsname uts;
> > +     int ret;
> > +
> > +     ret = uname(&uts);
> > +     if (ret < 0)
> > +             return -1;
> > +
> > +     return do_write_string(fd, uts.nodename);
> > +}
> > +
> > +static int write_osrelease(int fd, struct perf_header *h __used,
> > +                        struct perf_evlist *evlist __used)
> > +{
> > +     struct utsname uts;
> > +     int ret;
> > +
> > +     ret = uname(&uts);
> > +     if (ret < 0)
> > +             return -1;
> > +
> > +     return do_write_string(fd, uts.release);
> > +}
> > +
> > +static int write_arch(int fd, struct perf_header *h __used,
> > +                   struct perf_evlist *evlist __used)
> > +{
> > +     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 __used,
> > +                      struct perf_evlist *evlist __used)
> > +{
> > +     return do_write_string(fd, perf_version_string);
> > +}
> > +
> > +static int write_cpuid(int fd, struct perf_header *h __used,
> > +                    struct perf_evlist *evlist __used)
> > +{
> > +#ifndef CPUINFO_PROC
> > +#define CPUINFO_PROC NULL
> > +#endif
> > +     FILE *file;
> > +     char buf[256];
> > +     char *s, *p;
> > +     const char *search = CPUINFO_PROC;
> > +
> > +     file = fopen("/proc/cpuinfo", "r");
> > +     if (!file)
> > +             return -1;
> > +
> > +     if (search) {
> > +             while (fgets(buf, sizeof(buf), file)) {
> > +                     if (strstr(buf, search))
> > +                             goto found;
> > +             }
> > +     }
> > +     strcpy(buf, "unknown type");
> > +found:
> > +     fclose(file);
> > +
> > +     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++));
> > +             }
> > +             p++;
> > +     }
> > +     return do_write_string(fd, s);
> > +}
> > +
> > +static int write_nrcpus(int fd, struct perf_header *h __used,
> > +                     struct perf_evlist *evlist __used)
> > +{
> > +     long nr;
> > +     u32 nrc, nra;
> > +     int ret;
> > +
> > +     nr = sysconf(_SC_NPROCESSORS_CONF);
> > +     if (nr < 0)
> > +             return -1;
> > +
> > +     nrc = (u32)(nr & UINT_MAX);
> > +
> > +     ret = sysconf(_SC_NPROCESSORS_ONLN);
> > +     if (ret < 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));
> > +}
> > +
> > +static int write_event_desc(int fd, struct perf_header *h __used,
> > +                         struct perf_evlist *evlist)
> > +{
> > +     struct perf_evsel *attr;
> > +     u32 nre = 0, nri, sz;
> > +     int ret;
> > +
> > +     list_for_each_entry(attr, &evlist->entries, node)
> > +             nre++;
> > +
> > +     /*
> > +      * 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(attr->attr);
> > +     ret = do_write(fd, &sz, sizeof(sz));
> > +     if (ret < 0)
> > +             return ret;
> > +
> > +     list_for_each_entry(attr, &evlist->entries, node) {
> > +
> > +             ret = do_write(fd, &attr->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 = attr->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, attr->name);
> > +             if (ret < 0)
> > +                     return ret;
> > +             /*
> > +              * write unique ids for this event
> > +              */
> > +             ret = do_write(fd, attr->id, attr->ids * sizeof(u64));
> > +             if (ret < 0)
> > +                     return ret;
> > +     }
> > +     return 0;
> > +}
> > +
> > +static int write_cmdline(int fd, struct perf_header *h __used,
> > +                      struct perf_evlist *evlist __used)
> > +{
> > +     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;
> > +}
> > +
> > +static const char *topo_fmt[] = {
> > +     "/sys/devices/system/cpu/cpu%d/topology/core_siblings_list",
> > +     "/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list",
> > +     NULL
> > +};
> > +
> > +static int write_topo_cpu(int fd, int cpu)
> > +{
> > +     FILE *fp;
> > +     char filename[MAXPATHLEN];
> > +     char buf[MAXPATHLEN], *p, *c;
> > +     const char **q = topo_fmt;
> > +     int ret;
> > +
> > +     while (*q) {
> > +
> > +             sprintf(filename, *q, cpu);
> > +
> > +             fp = fopen(filename, "r");
> > +             if (!fp)
> > +                     return -1;
> > +
> > +             c = fgets(buf, sizeof(buf), fp);
> > +             if (!c) {
> > +                     fclose(fp);
> > +                     return -1;
> > +             }
> > +
> > +             p = strchr(buf, '\n');
> > +             if (p)
> > +                     *p = '\0';
> > +
> > +             fclose(fp);
> > +
> > +             ret = do_write_string(fd, c);
> > +             if (ret < 0)
> > +                     return ret;
> > +             q++;
> > +     }
> > +     return 0;
> > +}
> > +
> > +static int write_cpu_topology(int fd, struct perf_header *h __used,
> > +                       struct perf_evlist *evlist __used)
> > +{
> > +     u32 nr, i;
> > +     long ncpus;
> > +     int ret;
> > +
> > +     ncpus = sysconf(_SC_NPROCESSORS_CONF);
> > +     if (ncpus < 0)
> > +             return -1;
> > +
> > +     /* hopefully, this will keep compilers happy about the cast */
> > +     nr = (u32)(ncpus & UINT_MAX);
> > +
> > +     ret = do_write(fd, &nr, sizeof(nr));
> > +     if (ret < 0)
> > +             return -1;
> > +
> > +     for (i = 0; i < nr; i++) {
> > +             ret = do_write(fd, &i, sizeof(i));
> > +             if (ret < 0)
> > +                     return ret;
> > +
> > +             ret = write_topo_cpu(fd, i);
> > +             if (ret < 0)
> > +                     return ret;
> > +     }
> > +     return 0;
> > +}
> > +
> > +static int write_total_mem(int fd, struct perf_header *h __used,
> > +                       struct perf_evlist *evlist __used)
> > +{
> > +     char buf[MAXPATHLEN], *c;
> > +     FILE *fp;
> > +     int ret = -1, n;
> > +     uint64_t mem;
> > +
> > +     fp = fopen("/proc/meminfo", "r");
> > +     if (!fp)
> > +             return -1;
> > +
> > +     while ((c = fgets(buf, sizeof(buf), fp))) {
> > +             c = strstr(buf, "MemTotal:");
> > +             if (c)
> > +                     break;
> > +     }
> > +     fclose(fp);
> > +
> > +     n = sscanf(buf, "%*s %"PRIu64, &mem);
> > +     if (n == 1)
> > +             ret = do_write(fd, &mem, sizeof(mem));
> > +
> > +     return ret;
> > +}
> > +
> > +static int write_topo_node(int fd, int node)
> > +{
> > +     char filename[MAXPATHLEN];
> > +     char buf[MAXPATHLEN], *p, *c;
> > +     FILE *fp;
> > +     uint64_t mem;
> > +     int ret;
> > +
> > +     sprintf(filename, "/sys/devices/system/node/node%d/meminfo", node);
> > +     fp = fopen(filename, "r");
> > +     if (!fp)
> > +             return -1;
> > +
> > +     while ((c = fgets(buf, sizeof(buf), fp))) {
> > +             c = strstr(buf, "MemTotal:");
> > +             if (c)
> > +                     break;
> > +     }
> > +     fclose(fp);
> > +     if (!c)
> > +             return -1;
> > +     ret = sscanf(buf, "%*s %*d %*s %"PRIu64, &mem);
> > +     if (ret != 1)
> > +             return -1;
> > +
> > +     ret = do_write(fd, &mem, sizeof(mem));
> > +     if (ret < 0)
> > +             return -1;
> > +
> > +     sprintf(filename, "/sys/devices/system/node/node%d/cpulist", node);
> > +     fp = fopen(filename, "r");
> > +     if (!fp)
> > +             return -1;
> > +
> > +     c = fgets(buf, sizeof(buf), fp);
> > +     fclose(fp);
> > +     if (!c)
> > +             return -1;
> > +
> > +     p = strchr(buf, '\n');
> > +     if (p)
> > +             *p = '\0';
> > +
> > +     return do_write_string(fd, c);
> > +}
> > +
> > +static int write_numa_topology(int fd, struct perf_header *h __used,
> > +                       struct perf_evlist *evlist __used)
> > +{
> > +     char buf[MAXPATHLEN];
> > +     FILE *fp;
> > +     struct cpu_map *node_map;
> > +     char *c;
> > +     u32 nr, i, j;
> > +     int ret;
> > +
> > +     fp = fopen("/sys/devices/system/node/online", "r");
> > +     if (!fp)
> > +             return -1;
> > +
> > +     c = fgets(buf, sizeof(buf), fp);
> > +     fclose(fp);
> > +     if (!c)
> > +             return -1;
> > +
> > +     c = strchr(buf, '\n');
> > +     if (c)
> > +             *c = '\0';
> > +
> > +     node_map = cpu_map__new(buf);
> > +     if (!node_map)
> > +             return -1;
> > +
> > +     nr = (u32)node_map->nr;
> > +     ret = do_write(fd, &nr, sizeof(nr));
> > +     if (ret < 0)
> > +             return -1;
> > +
> > +     for (i = 0; i < nr; i++) {
> > +             j = (u32)node_map->map[i];
> > +             ret = do_write(fd, &j, sizeof(j));
> > +             if (ret < 0)
> > +                     goto error;
> > +
> > +             ret = write_topo_node(fd, i);
> > +             if (ret < 0)
> > +                     goto error;
> > +     }
> > +     ret = 0;
> > +error:
> > +     free(node_map);
> > +     return ret;
> > +}
> > +
> > +static void print_hostname(struct perf_header *ph, int fd, FILE *fp)
> > +{
> > +     char *str = do_read_string(fd, ph);
> > +     fprintf(fp, "# hostname : %s\n", str);
> > +     free(str);
> > +}
> > +
> > +static void print_osrelease(struct perf_header *ph, int fd, FILE *fp)
> > +{
> > +     char *str = do_read_string(fd, ph);
> > +     fprintf(fp, "# os release : %s\n", str);
> > +     free(str);
> > +}
> > +
> > +static void print_arch(struct perf_header *ph, int fd, FILE *fp)
> > +{
> > +     char *str = do_read_string(fd, ph);
> > +     fprintf(fp, "# arch : %s\n", str);
> > +     free(str);
> > +}
> > +
> > +static void print_cpuid(struct perf_header *ph, int fd, FILE *fp)
> > +{
> > +     char *str = do_read_string(fd, ph);
> > +     fprintf(fp, "# cpuid : %s\n", str);
> > +     free(str);
> > +}
> > +
> > +static void print_nrcpus(struct perf_header *ph, int fd, FILE *fp)
> > +{
> > +     ssize_t ret;
> > +     u32 nr;
> > +
> > +     ret = read(fd, &nr, sizeof(nr));
> > +     if (ret != (ssize_t)sizeof(nr))
> > +             nr = -1; /* interpreted as error */
> > +
> > +     if (ph->needs_swap)
> > +             nr = bswap_32(nr);
> > +
> > +     fprintf(fp, "# nrcpus online : %u\n", nr);
> > +
> > +     ret = read(fd, &nr, sizeof(nr));
> > +     if (ret != (ssize_t)sizeof(nr))
> > +             nr = -1; /* interpreted as error */
> > +
> > +     if (ph->needs_swap)
> > +             nr = bswap_32(nr);
> > +
> > +     fprintf(fp, "# nrcpus avail : %u\n", nr);
> > +}
> > +
> > +static void print_version(struct perf_header *ph, int fd, FILE *fp)
> > +{
> > +     char *str = do_read_string(fd, ph);
> > +     fprintf(fp, "# perf version : %s\n", str);
> > +     free(str);
> > +}
> > +
> > +static void print_cmdline(struct perf_header *ph, int fd, FILE *fp)
> > +{
> > +     ssize_t ret;
> > +     char *str;
> > +     u32 nr, i;
> > +
> > +     ret = read(fd, &nr, sizeof(nr));
> > +     if (ret != (ssize_t)sizeof(nr)) {
> > +             fprintf(fp, "# cmdline : unknown\n");
> > +             return;
> > +     }
> > +
> > +     if (ph->needs_swap)
> > +             nr = bswap_32(nr);
> > +
> > +     fprintf(fp, "# cmdline : ");
> > +
> > +     for (i = 0; i < nr; i++) {
> > +             str = do_read_string(fd, ph);
> > +             fprintf(fp, "%s ", str);
> > +             free(str);
> > +     }
> > +     fputc('\n', fp);
> > +}
> > +
> > +static void print_cpu_topology(struct perf_header *ph, int fd, FILE *fp)
> > +{
> > +     ssize_t ret;
> > +     u32 nr, c, i;
> > +     char *str;
> > +
> > +     ret = read(fd, &nr, sizeof(nr));
> > +     if (ret != (ssize_t)sizeof(nr)) {
> > +             fprintf(fp, "# cpu topology  : not available\n");
> > +             return;
> > +     }
> > +
> > +     if (ph->needs_swap)
> > +             nr = bswap_32(nr);
> > +
> > +     for (i = 0; i < nr; i++) {
> > +
> > +             ret = read(fd, &c, sizeof(c));
> > +             if (ret != (ssize_t)sizeof(c))
> > +                     c = -1; /* interpreted as error */
> > +
> > +             if (ph->needs_swap)
> > +                     c = bswap_32(c);
> > +
> > +             str = do_read_string(fd, ph);
> > +             fprintf(fp, "# CPU%u sibling cores  : %s\n", c, str);
> > +             free(str);
> > +
> > +             str = do_read_string(fd, ph);
> > +             fprintf(fp, "# CPU%u sibling threads: %s\n", c, str);
> > +             free(str);
> > +     }
> > +}
> > +
> > +static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
> > +{
> > +     struct perf_event_attr attr;
> > +     uint64_t id;
> > +     void *buf = NULL;
> > +     char *str;
> > +     u32 nre, sz, nr, i, j, msz;
> > +     int ret;
> > +
> > +     /* number of events */
> > +     ret = read(fd, &nre, sizeof(nre));
> > +     if (ret != (ssize_t)sizeof(nre))
> > +             goto error;
> > +
> > +     if (ph->needs_swap)
> > +             nre = bswap_32(nre);
> > +
> > +     ret = read(fd, &sz, sizeof(sz));
> > +     if (ret != (ssize_t)sizeof(sz))
> > +             goto error;
> > +
> > +     if (ph->needs_swap)
> > +             sz = bswap_32(sz);
> > +
> > +     /*
> > +      * ensure it is at least to our ABI rev
> > +      */
> > +     if (sz < (u32)sizeof(attr))
> > +             goto error;
> > +
> > +     memset(&attr, 0, sizeof(attr));
> > +
> > +     /* read entire region to sync up to next field */
> > +     buf = malloc(sz);
> > +     if (!buf)
> > +             goto error;
> > +
> > +     msz = sizeof(attr);
> > +     if (sz < msz)
> > +             msz = sz;
> > +
> > +     for (i = 0 ; i < nre; i++) {
> > +
> > +             ret = read(fd, buf, sz);
> > +             if (ret != (ssize_t)sz)
> > +                     goto error;
> > +
> > +             if (ph->needs_swap)
> > +                     perf_event__attr_swap(buf);
> > +
> > +             memcpy(&attr, buf, msz);
> > +
> > +             ret = read(fd, &nr, sizeof(nr));
> > +             if (ret != (ssize_t)sizeof(nr))
> > +                     goto error;
> > +
> > +             if (ph->needs_swap)
> > +                     nr = bswap_32(nr);
> > +
> > +             str = do_read_string(fd, ph);
> > +             fprintf(fp, "# event = %s, ", str);
> > +             free(str);
> > +
> > +             fprintf(fp, "type = %d, config = 0x%"PRIx64
> > +                         ", config1 = 0x%"PRIx64", config2 = 0x%"PRIx64,
> > +                             attr.type,
> > +                             (u64)attr.config,
> > +                             (u64)attr.config1,
> > +                             (u64)attr.config2);
> > +
> > +             fprintf(fp, ", excl_usr = %d, excl_kern = %d",
> > +                             attr.exclude_user,
> > +                             attr.exclude_kernel);
> > +
> > +             if (nr)
> > +                     fprintf(fp, ", id = {");
> > +
> > +             for (j = 0 ; j < nr; j++) {
> > +                     ret = read(fd, &id, sizeof(id));
> > +                     if (ret != (ssize_t)sizeof(id))
> > +                             goto error;
> > +
> > +                     if (ph->needs_swap)
> > +                             id = bswap_64(id);
> > +
> > +                     if (j)
> > +                             fputc(',', fp);
> > +
> > +                     fprintf(fp, " %"PRIu64, id);
> > +             }
> > +             if (nr && j == nr)
> > +                     fprintf(fp, " }");
> > +             fputc('\n', fp);
> > +     }
> > +     free(buf);
> > +     return;
> > +error:
> > +     fprintf(fp, "# event desc: not available or unable to read\n");
> > +}
> > +
> > +static void print_total_mem(struct perf_header *h __used, int fd, FILE *fp)
> > +{
> > +     uint64_t mem;
> > +     ssize_t ret;
> > +
> > +     ret = read(fd, &mem, sizeof(mem));
> > +     if (ret != sizeof(mem))
> > +             goto error;
> > +
> > +     if (h->needs_swap)
> > +             mem = bswap_64(mem);
> > +
> > +     fprintf(fp, "# total memory: %"PRIu64" kB\n", mem);
> > +     return;
> > +error:
> > +     fprintf(fp, "# total memory: unknown\n");
> > +}
> > +
> > +static void print_numa_topology(struct perf_header *h __used, int fd, FILE *fp)
> > +{
> > +     ssize_t ret;
> > +     u32 nr, c, i;
> > +     char *str;
> > +     uint64_t mem;
> > +
> > +     /* nr nodes */
> > +     ret = read(fd, &nr, sizeof(nr));
> > +     if (ret != (ssize_t)sizeof(nr))
> > +             goto error;
> > +
> > +     if (h->needs_swap)
> > +             nr = bswap_32(nr);
> > +
> > +     for (i = 0; i < nr; i++) {
> > +
> > +             /* node number */
> > +             ret = read(fd, &c, sizeof(c));
> > +             if (ret != (ssize_t)sizeof(c))
> > +                     goto error;
> > +
> > +             if (h->needs_swap)
> > +                     c = bswap_32(c);
> > +
> > +             ret = read(fd, &mem, sizeof(mem));
> > +             if (ret != sizeof(mem))
> > +                     goto error;
> > +
> > +             if (h->needs_swap)
> > +                     mem = bswap_64(mem);
> > +
> > +             fprintf(fp, "# node%u meminfo : %"PRIu64" kB\n", c, mem);
> > +
> > +             str = do_read_string(fd, h);
> > +             fprintf(fp, "# node%u cpu list: %s\n", c, str);
> > +             free(str);
> > +     }
> > +     return;
> > +error:
> > +     fprintf(fp, "# numa topology : not available\n");
> > +}
> > +
> > +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);
> > +};
> > +
> > +#define FEAT_OP(n, w, p) [n] = { .write = w, .print = p }
> > +
> > +static struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
> > +     FEAT_OP(HEADER_TRACE_INFO, write_trace_info, NULL),
> > +     FEAT_OP(HEADER_BUILD_ID, write_build_id, NULL),
> > +     FEAT_OP(HEADER_HOSTNAME, write_hostname, print_hostname),
> > +     FEAT_OP(HEADER_OSRELEASE, write_osrelease, print_osrelease),
> > +     FEAT_OP(HEADER_ARCH, write_arch, print_arch),
> > +     FEAT_OP(HEADER_CPUID, write_cpuid, print_cpuid),
> > +     FEAT_OP(HEADER_NRCPUS, write_nrcpus, print_nrcpus),
> > +     FEAT_OP(HEADER_EVENT_DESC, write_event_desc, print_event_desc),
> > +     FEAT_OP(HEADER_CMDLINE, write_cmdline, print_cmdline),
> > +     FEAT_OP(HEADER_VERSION, write_version, print_version),
> > +     FEAT_OP(HEADER_CPU_TOPOLOGY, write_cpu_topology, print_cpu_topology),
> > +     FEAT_OP(HEADER_TOTAL_MEM, write_total_mem, print_total_mem),
> > +     FEAT_OP(HEADER_NUMA_TOPOLOGY, write_numa_topology, print_numa_topology),
> > +};
> > +
> > +static int perf_header_fprintf_info(struct perf_file_section *section,
> > +                                 struct perf_header *ph,
> > +                                 int feat, int fd, void *data)
> > +{
> > +     FILE *fp = data;
> > +
> > +     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_TRACE_INFO || feat >= HEADER_LAST_FEATURE) {
> > +             pr_warning("unknown feature %d\n", feat);
> > +             return -1;
> > +     }
> > +
> > +     if (feat_ops[feat].print)
> > +             feat_ops[feat].print(ph, fd, fp);
> > +
> > +     return 0;
> > +}
> > +
> > +int perf_header__fprintf_info(struct perf_session *session, FILE *fp __used)
> > +{
> > +     int fd = session->fd;
> > +     struct perf_header *header = &session->header;
> > +     perf_header__process_sections(header, fd, fp, perf_header_fprintf_info);
> > +     return 0;
> > +}
> > +
> >  #define dsos__for_each_with_build_id(pos, head)      \
> >       list_for_each_entry(pos, head, node)    \
> >               if (!pos->has_build_id)         \
> > @@ -356,15 +1201,41 @@ static bool perf_session__read_build_ids(struct perf_session *session, bool with
> >       return ret;
> >  }
> >
> > +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_header__has_feat(h, type)) {
> > +
> > +             (*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;
> > +}
> > +
> >  static int perf_header__adds_write(struct perf_header *header,
> >                                  struct perf_evlist *evlist, int fd)
> >  {
> >       int nr_sections;
> >       struct perf_session *session;
> > -     struct perf_file_section *feat_sec;
> > +     struct perf_file_section *feat_sec, *p;
> >       int sec_size;
> >       u64 sec_start;
> > -     int idx = 0, err;
> > +     int err;
> >
> >       session = container_of(header, struct perf_session, header);
> >
> > @@ -376,7 +1247,7 @@ static int perf_header__adds_write(struct perf_header *header,
> >       if (!nr_sections)
> >               return 0;
> >
> > -     feat_sec = calloc(sizeof(*feat_sec), nr_sections);
> > +     feat_sec = p = calloc(sizeof(*feat_sec), nr_sections);
> >       if (feat_sec == NULL)
> >               return -ENOMEM;
> >
> > @@ -385,36 +1256,65 @@ static int perf_header__adds_write(struct perf_header *header,
> >       sec_start = header->data_offset + header->data_size;
> >       lseek(fd, sec_start + sec_size, SEEK_SET);
> >
> > -     if (perf_header__has_feat(header, HEADER_TRACE_INFO)) {
> > -             struct perf_file_section *trace_sec;
> > -
> > -             trace_sec = &feat_sec[idx++];
> > +     err = do_write_feat(fd, header, HEADER_TRACE_INFO, &p, evlist);
> > +     if (err)
> > +             goto out_free;
> >
> > -             /* Write trace info */
> > -             trace_sec->offset = lseek(fd, 0, SEEK_CUR);
> > -             read_tracing_data(fd, &evlist->entries);
> > -             trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset;
> > +     err = do_write_feat(fd, header, HEADER_BUILD_ID, &p, evlist);
> > +     if (err) {
> > +             perf_header__clear_feat(header, HEADER_BUILD_ID);
> > +             goto out_free;
> >       }
> >
> > -     if (perf_header__has_feat(header, HEADER_BUILD_ID)) {
> > -             struct perf_file_section *buildid_sec;
> > +     err = do_write_feat(fd, header, HEADER_HOSTNAME, &p, evlist);
> > +     if (err)
> > +             perf_header__clear_feat(header, HEADER_HOSTNAME);
> >
> > -             buildid_sec = &feat_sec[idx++];
> > +     err = do_write_feat(fd, header, HEADER_OSRELEASE, &p, evlist);
> > +     if (err)
> > +             perf_header__clear_feat(header, HEADER_OSRELEASE);
> >
> > -             /* Write build-ids */
> > -             buildid_sec->offset = lseek(fd, 0, SEEK_CUR);
> > -             err = dsos__write_buildid_table(header, 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;
> > -             if (!no_buildid_cache)
> > -                     perf_session__cache_build_ids(session);
> > -     }
> > +     err = do_write_feat(fd, header, HEADER_ARCH, &p, evlist);
> > +     if (err)
> > +             perf_header__clear_feat(header, HEADER_ARCH);
> > +
> > +     err = do_write_feat(fd, header, HEADER_CPUID, &p, evlist);
> > +     if (err)
> > +             perf_header__clear_feat(header, HEADER_CPUID);
> > +
> > +     err = do_write_feat(fd, header, HEADER_NRCPUS, &p, evlist);
> > +     if (err)
> > +             perf_header__clear_feat(header, HEADER_NRCPUS);
> > +
> > +     err = do_write_feat(fd, header, HEADER_EVENT_DESC, &p, evlist);
> > +     if (err)
> > +             perf_header__clear_feat(header, HEADER_EVENT_DESC);
> > +
> > +     err = do_write_feat(fd, header, HEADER_CMDLINE, &p, evlist);
> > +     if (err)
> > +             perf_header__clear_feat(header, HEADER_CMDLINE);
> > +
> > +     err = do_write_feat(fd, header, HEADER_VERSION, &p, evlist);
> > +     if (err)
> > +             perf_header__clear_feat(header, HEADER_VERSION);
> > +
> > +     err = do_write_feat(fd, header, HEADER_CPU_TOPOLOGY, &p, evlist);
> > +     if (err)
> > +             perf_header__clear_feat(header, HEADER_CPU_TOPOLOGY);
> > +
> > +     err = do_write_feat(fd, header, HEADER_TOTAL_MEM, &p, evlist);
> > +     if (err)
> > +             perf_header__clear_feat(header, HEADER_TOTAL_MEM);
> > +
> > +     err = do_write_feat(fd, header, HEADER_NUMA_TOPOLOGY, &p, evlist);
> > +     if (err)
> > +             perf_header__clear_feat(header, HEADER_NUMA_TOPOLOGY);
> >
> >       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");
> > @@ -554,9 +1454,10 @@ static int perf_header__getbuffer64(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))
> > +                               struct perf_header *ph,
> > +                               int feat, int fd, void *data))
> >  {
> >       struct perf_file_section *feat_sec;
> >       int nr_sections;
> > @@ -584,7 +1485,7 @@ int perf_header__process_sections(struct perf_header *header, int fd,
> >               if (perf_header__has_feat(header, feat)) {
> >                       struct perf_file_section *sec = &feat_sec[idx++];
> >
> > -                     err = process(sec, header, feat, fd);
> > +                     err = process(sec, header, feat, fd, data);
> >                       if (err < 0)
> >                               break;
> >               }
> > @@ -796,7 +1697,7 @@ out:
> >
> >  static int perf_file_section__process(struct perf_file_section *section,
> >                                     struct perf_header *ph,
> > -                                   int feat, int fd)
> > +                                   int feat, int fd, void *data __used)
> >  {
> >       if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) {
> >               pr_debug("Failed to lseek to %" PRIu64 " offset for feature "
> > @@ -935,7 +1836,8 @@ int perf_session__read_header(struct perf_session *session, int fd)
> >               event_count =  f_header.event_types.size / sizeof(struct perf_trace_event_type);
> >       }
> >
> > -     perf_header__process_sections(header, fd, perf_file_section__process);
> > +     perf_header__process_sections(header, fd, NULL,
> > +                                   perf_file_section__process);
> >
> >       lseek(fd, header->data_offset, SEEK_SET);
> >
> > diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
> > index 1886256..455480b 100644
> > --- a/tools/perf/util/header.h
> > +++ b/tools/perf/util/header.h
> > @@ -12,6 +12,19 @@
> >  enum {
> >       HEADER_TRACE_INFO = 1,
> >       HEADER_BUILD_ID,
> > +
> > +     HEADER_HOSTNAME,
> > +     HEADER_OSRELEASE,
> > +     HEADER_ARCH,
> > +     HEADER_CPUID,
> > +     HEADER_NRCPUS,
> > +     HEADER_EVENT_DESC,
> > +     HEADER_CMDLINE,
> > +     HEADER_VERSION,
> > +     HEADER_CPU_TOPOLOGY,
> > +     HEADER_TOTAL_MEM,
> > +     HEADER_NUMA_TOPOLOGY,
> > +
> >       HEADER_LAST_FEATURE,
> >  };
> >
> > @@ -68,10 +81,15 @@ void perf_header__set_feat(struct perf_header *header, int feat);
> >  void perf_header__clear_feat(struct perf_header *header, int feat);
> >  bool perf_header__has_feat(const struct perf_header *header, int feat);
> >
> > +int perf_header__set_cmdline(int argc, const char **argv);
> > +
> >  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));
> > +                               struct perf_header *ph,
> > +                               int feat, int fd, void *data));
> > +
> > +int perf_header__fprintf_info(struct perf_session *session, FILE *fp);
> >
> >  int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
> >                         const char *name, bool is_kallsyms);
> > diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
> > index 72458d9..54613a2 100644
> > --- a/tools/perf/util/session.c
> > +++ b/tools/perf/util/session.c
> > @@ -1326,3 +1326,21 @@ int perf_session__cpu_bitmap(struct perf_session *session,
> >
> >       return 0;
> >  }
> > +
> > +void perf_session__fprintf_info(struct perf_session *session, FILE *fp)
> > +{
> > +     struct stat st;
> > +     int ret;
> > +
> > +     if (session == NULL || fp == NULL)
> > +             return;
> > +
> > +     ret = fstat(session->fd, &st);
> > +     if (ret == -1)
> > +             return;
> > +
> > +     fprintf(fp, "#\n# captured on: %s", ctime(&st.st_ctime));
> > +     ret = perf_header__fprintf_info(session, fp);
> > +     if (ret == 0)
> > +             fprintf(fp, "#\n");
> > +}
> > diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
> > index 170601e..33a3a46 100644
> > --- a/tools/perf/util/session.h
> > +++ b/tools/perf/util/session.h
> > @@ -176,4 +176,5 @@ void perf_session__print_ip(union perf_event *event,
> >  int perf_session__cpu_bitmap(struct perf_session *session,
> >                            const char *cpu_list, unsigned long *cpu_bitmap);
> >
> > +void perf_session__fprintf_info(struct perf_session *session, FILE *fp);
> >  #endif /* __PERF_SESSION_H */

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] perf: make perf.data more self-descriptive (v4)
  2011-09-07 19:10 [PATCH] perf: make perf.data more self-descriptive (v4) Stephane Eranian
  2011-09-08 15:27 ` David Ahern
@ 2011-09-12 13:45 ` David Ahern
  2011-09-12 13:54   ` Stephane Eranian
  1 sibling, 1 reply; 21+ messages in thread
From: David Ahern @ 2011-09-12 13:45 UTC (permalink / raw)
  To: Stephane Eranian; +Cc: linux-kernel, acme, peterz, mingo

On 09/07/2011 01:10 PM, Stephane Eranian wrote:
> 
> The goal of this patch is to include more information
> about the host environment into the perf.data so it is
> more self-descriptive. Overtime, profiles are captured
> on various machines and it becomes hard to track what
> was recorded, on what machine and when.
> 
> This patch provides a way to solve this by extending
> the perf.data file with basic information about the
> host machine. To add those extensions, we leverage
> the feature bits capabilities of the perf.data format.
> The change is backward compatible with existing perf.data
> files.
> 
> We define the following useful new extensions:
>  - HEADER_HOSTNAME: the hostname
>  - HEADER_OSRELEASE: the kernel release number
>  - HEADER_ARCH: the hw architecture
>  - HEADER_CPUID: the cpu model description
>  - HEADER_NRCPUS: number of online/avail cpus
>  - HEADER_CMDLINE: perf command line
>  - HEADER_VERSION: perf version
>  - HEADER_TOPOLOGY: cpu topology
>  - HEADER_EVENT_DESC: full event description (attrs)
> 
> The small granularity for the entries is to make it easier
> to extend without breaking backward compatiblity. All entries
> are provided as ASCII strings, easy to parse, except for the
> event description.
> 
> In the second version, we dropped the -I option for the
> perf record command. The extra information is systematically
> captured. But it's still displayed optionally by perf report.
> There are also a couple of cleanups of function prototypes
> and global variables.
> 
> In the third version, we:
> - fixed missing MIPS support for CPUID
> - used u32 instead of int when writing integers to file
> - fixed couple of bswap() bugs
> - refactorize all write/print functions using function pointers
> - improved write_cmdline() to include actual path to perf binary
> 
> In the fourth version, we:
> - fixed a couple of int vs. u32 issues
> - factorized HEADER_BUILD_ID, HEADER_TRACE_INFO
> - added -I option to perf script report
> - reverted change to evsel.c (unrelated to meta-data)
> - modified write_*() to always write despite errors (adds_feature bit present)
> - added HEADER_TOTAL_MEM: total memory installed on this system
> - added HEADER_NUMA_TOPOLOGY: shows NUMA nodes and for each memory, cpu list
> - renamed HEADER_TOPOLOGY to HEADER_CPU_TOPOLOGY
> 
> 
> $ perf record -ecycles,instructions noploop 1
> noploop for 1 seconds
> [ perf record: Woken up 1 times to write data ]
> [ perf record: Captured and wrote 0.098 MB perf.data (~4260 samples) ]
> $ perf report -I --stdio
> #
> # captured on: Wed Sep  7 21:00:10 2011
> # hostname : quad
> # os release : 3.1.0-rc4-tip
> # arch : x86_64
> # cpuid : Intel(R) Core(TM)2 Quad CPU Q6600 @ 2.40GHz
> # nrcpus online : 4
> # nrcpus avail : 4
> # event = cycles, type = 0, config = 0x0, config1 = 0x0, config2 = 0x0, excl_usr = 0, excl_kern = 0, id = { 536, 537, 538, 539 }
> # event = instructions, type = 0, config = 0x1, config1 = 0x0, config2 = 0x0, excl_usr = 0, excl_kern = 0, id = { 540, 541, 542,
> # cmdline : /home/eranian/perfmon/official/tip/build/tools/perf/perf record -ecycles,instructions noploop 1 
> # perf version : 3.1.0-rc4
> # CPU0   sibling cores  : 0-3
> # CPU0   sibling threads: 0
> # CPU1   sibling cores  : 0-3
> # CPU1   sibling threads: 1
> # CPU2   sibling cores  : 0-3
> # CPU2   sibling threads: 2
> # CPU3   sibling cores  : 0-3
> # CPU3   sibling threads: 3
> # total memory: 8105344 kB
> # node0   meminfo : 8320608 kB
> # node0   cpu list: 0-3
> #
> # Events: 995  cycles
> #
> # Overhead  Command      Shared Object                Symbol
> # ........  .......  .................  ....................
> #
>     99.90%  noploop  noploop            [.] noploop
>      0.10%  noploop  [kernel.kallsyms]  [k] trace_hardirqs_on
>      0.00%  noploop  [kernel.kallsyms]  [k] lock_release
>      0.00%  noploop  [kernel.kallsyms]  [k] intel_pmu_enable_all
> ...
> 
> 
> Signed-off-by: Stephane Eranian <eranian@google.com>
> ---
> 
> diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt
> index 04253c0..191e10e 100644
> --- a/tools/perf/Documentation/perf-report.txt
> +++ b/tools/perf/Documentation/perf-report.txt
> @@ -134,6 +134,11 @@ OPTIONS
>  	CPUs are specified with -: 0-2. Default is to report samples on all
>  	CPUs.
>  
> +-I::
> +--show-info::
> +	Display information about the perf.data file, incl. hostname,
> +	os release, perf version, machine desc.
> +
>  SEE ALSO
>  --------
>  linkperf:perf-stat[1]
> diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt
> index db01786..ebf8a23 100644
> --- a/tools/perf/Documentation/perf-script.txt
> +++ b/tools/perf/Documentation/perf-script.txt
> @@ -188,6 +188,13 @@ OPTIONS
>  	CPUs are specified with -: 0-2. Default is to report samples on all
>  	CPUs.
>  
> +-I::
> +--show-info::
> +	Display information about the perf.data file, incl. hostname,
> +	os release, perf version, machine desc. It can only be used with the
> +	perf script report mode and it must be specified after the script name
> +	like for instance: perf script report syscall-counts -I.
> +
>  SEE ALSO
>  --------
>  linkperf:perf-record[1], linkperf:perf-script-perl[1],
> diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
> index 6b0519f..524ebbf 100644
> --- a/tools/perf/builtin-record.c
> +++ b/tools/perf/builtin-record.c
> @@ -513,6 +513,18 @@ static int __cmd_record(int argc, const char **argv)
>  	if (have_tracepoints(&evsel_list->entries))
>  		perf_header__set_feat(&session->header, HEADER_TRACE_INFO);
>  
> +	perf_header__set_feat(&session->header, HEADER_HOSTNAME);
> +	perf_header__set_feat(&session->header, HEADER_OSRELEASE);
> +	perf_header__set_feat(&session->header, HEADER_ARCH);
> +	perf_header__set_feat(&session->header, HEADER_CPUID);
> +	perf_header__set_feat(&session->header, HEADER_NRCPUS);
> +	perf_header__set_feat(&session->header, HEADER_EVENT_DESC);
> +	perf_header__set_feat(&session->header, HEADER_CMDLINE);
> +	perf_header__set_feat(&session->header, HEADER_VERSION);
> +	perf_header__set_feat(&session->header, HEADER_CPU_TOPOLOGY);
> +	perf_header__set_feat(&session->header, HEADER_TOTAL_MEM);
> +	perf_header__set_feat(&session->header, HEADER_NUMA_TOPOLOGY);
> +
>  	/* 512 kiB: default amount of unprivileged mlocked memory */
>  	if (mmap_pages == UINT_MAX)
>  		mmap_pages = (512 * 1024) / page_size;
> @@ -782,6 +794,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
>  	int err = -ENOMEM;
>  	struct perf_evsel *pos;
>  
> +	perf_header__set_cmdline(argc, argv);
> +
>  	evsel_list = perf_evlist__new(NULL, NULL);
>  	if (evsel_list == NULL)
>  		return -ENOMEM;
> diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
> index d7ff277..7721030 100644
> --- a/tools/perf/builtin-report.c
> +++ b/tools/perf/builtin-report.c
> @@ -40,6 +40,7 @@ static char		const *input_name = "perf.data";
>  static bool		force, use_tui, use_stdio;
>  static bool		hide_unresolved;
>  static bool		dont_use_callchains;
> +static bool		show_info;
>  
>  static bool		show_threads;
>  static struct perf_read_values	show_threads_values;
> @@ -276,6 +277,9 @@ static int __cmd_report(void)
>  			goto out_delete;
>  	}
>  
> +	if (show_info)
> +		perf_session__fprintf_info(session, stdout);
> +
>  	if (show_threads)
>  		perf_read_values_init(&show_threads_values);
>  
> @@ -487,6 +491,8 @@ static const struct option options[] = {
>  	OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
>  		    "Look for files with symbols relative to this directory"),
>  	OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"),
> +	OPT_BOOLEAN('I', "show-info", &show_info,
> +			"display information about perf.data file"),
>  	OPT_END()
>  };
>  
> diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
> index 09024ec..dac86ab 100644
> --- a/tools/perf/builtin-script.c
> +++ b/tools/perf/builtin-script.c
> @@ -22,6 +22,7 @@ static u64			last_timestamp;
>  static u64			nr_unordered;
>  extern const struct option	record_options[];
>  static bool			no_callchain;
> +static bool			show_info = false;
>  static const char		*cpu_list;
>  static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
>  
> @@ -1083,7 +1084,8 @@ static const struct option options[] = {
>  		     "comma separated output fields prepend with 'type:'. Valid types: hw,sw,trace,raw. Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso,addr",
>  		     parse_output_fields),
>  	OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"),
> -
> +	OPT_BOOLEAN('I', "show-info", &show_info,
> +			"display host information from perf.data file"),
>  	OPT_END()
>  };
>  
> @@ -1268,6 +1270,9 @@ int cmd_script(int argc, const char **argv, const char *prefix __used)
>  			return -1;
>  	}
>  
> +	if (show_info)
> +		perf_session__fprintf_info(session, stdout);
> +
>  	if (!no_callchain)
>  		symbol_conf.use_callchain = true;
>  	else
> diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h
> index 4702e24..b382bd5 100644
> --- a/tools/perf/builtin.h
> +++ b/tools/perf/builtin.h
> @@ -4,7 +4,6 @@
>  #include "util/util.h"
>  #include "util/strbuf.h"
>  
> -extern const char perf_version_string[];
>  extern const char perf_usage_string[];
>  extern const char perf_more_info_string[];
>  
> diff --git a/tools/perf/perf.h b/tools/perf/perf.h
> index a5fc660..08b0b5e 100644
> --- a/tools/perf/perf.h
> +++ b/tools/perf/perf.h
> @@ -9,18 +9,21 @@ void get_term_dimensions(struct winsize *ws);
>  #include "../../arch/x86/include/asm/unistd.h"
>  #define rmb()		asm volatile("lock; addl $0,0(%%esp)" ::: "memory")
>  #define cpu_relax()	asm volatile("rep; nop" ::: "memory");
> +#define CPUINFO_PROC	"model name"
>  #endif
>  
>  #if defined(__x86_64__)
>  #include "../../arch/x86/include/asm/unistd.h"
>  #define rmb()		asm volatile("lfence" ::: "memory")
>  #define cpu_relax()	asm volatile("rep; nop" ::: "memory");
> +#define CPUINFO_PROC	"model name"
>  #endif
>  
>  #ifdef __powerpc__
>  #include "../../arch/powerpc/include/asm/unistd.h"
>  #define rmb()		asm volatile ("sync" ::: "memory")
>  #define cpu_relax()	asm volatile ("" ::: "memory");
> +#define CPUINFO_PROC	"cpu"
>  #endif
>  
>  #ifdef __s390__
> @@ -37,30 +40,35 @@ void get_term_dimensions(struct winsize *ws);
>  # define rmb()		asm volatile("" ::: "memory")
>  #endif
>  #define cpu_relax()	asm volatile("" ::: "memory")
> +#define CPUINFO_PROC	"cpu type"
>  #endif
>  
>  #ifdef __hppa__
>  #include "../../arch/parisc/include/asm/unistd.h"
>  #define rmb()		asm volatile("" ::: "memory")
>  #define cpu_relax()	asm volatile("" ::: "memory");
> +#define CPUINFO_PROC	"cpu"
>  #endif
>  
>  #ifdef __sparc__
>  #include "../../arch/sparc/include/asm/unistd.h"
>  #define rmb()		asm volatile("":::"memory")
>  #define cpu_relax()	asm volatile("":::"memory")
> +#define CPUINFO_PROC	"cpu"
>  #endif
>  
>  #ifdef __alpha__
>  #include "../../arch/alpha/include/asm/unistd.h"
>  #define rmb()		asm volatile("mb" ::: "memory")
>  #define cpu_relax()	asm volatile("" ::: "memory")
> +#define CPUINFO_PROC	"cpu model"
>  #endif
>  
>  #ifdef __ia64__
>  #include "../../arch/ia64/include/asm/unistd.h"
>  #define rmb()		asm volatile ("mf" ::: "memory")
>  #define cpu_relax()	asm volatile ("hint @pause" ::: "memory")
> +#define CPUINFO_PROC	"model name"
>  #endif
>  
>  #ifdef __arm__
> @@ -71,6 +79,7 @@ void get_term_dimensions(struct winsize *ws);
>   */
>  #define rmb()		((void(*)(void))0xffff0fa0)()
>  #define cpu_relax()	asm volatile("":::"memory")
> +#define CPUINFO_PROC	"Processor"
>  #endif
>  
>  #ifdef __mips__
> @@ -83,6 +92,7 @@ void get_term_dimensions(struct winsize *ws);
>  				: /* no input */			\
>  				: "memory")
>  #define cpu_relax()	asm volatile("" ::: "memory")
> +#define CPUINFO_PROC	"cpu model"
>  #endif
>  
>  #include <time.h>
> @@ -171,5 +181,6 @@ struct ip_callchain {
>  };
>  
>  extern bool perf_host, perf_guest;
> +extern const char perf_version_string[];
>  
>  #endif
> diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
> index b6c1ad1..73fa59e 100644
> --- a/tools/perf/util/header.c
> +++ b/tools/perf/util/header.c
> @@ -7,6 +7,7 @@
>  #include <stdlib.h>
>  #include <linux/list.h>
>  #include <linux/kernel.h>
> +#include <sys/utsname.h>
>  
>  #include "evlist.h"
>  #include "evsel.h"
> @@ -17,12 +18,19 @@
>  #include "session.h"
>  #include "symbol.h"
>  #include "debug.h"
> +#include "cpumap.h"
>  
>  static bool no_buildid_cache = false;
>  
>  static int event_count;
>  static struct perf_trace_event_type *events;
>  
> +static u32 header_argc;
> +static const char **header_argv;
> +
> +static int dsos__write_buildid_table(struct perf_header *header, int fd);
> +static int perf_session__cache_build_ids(struct perf_session *session);
> +
>  int perf_header__push_event(u64 id, const char *name)
>  {
>  	if (strlen(name) > MAX_EVENT_NAME)
> @@ -110,6 +118,843 @@ static int write_padded(int fd, const void *bf, size_t count,
>  	return err;
>  }
>  
> +static int do_write_string(int fd, const char *str)
> +{
> +	u32 len, olen;
> +	int ret;
> +
> +	olen = strlen(str) + 1;
> +	len = ALIGN(olen, NAME_ALIGN);
> +
> +	/* write len, incl. \0 */
> +	ret = do_write(fd, &len, sizeof(len));
> +	if (ret < 0)
> +		return ret;
> +
> +	return write_padded(fd, str, olen, len);
> +}
> +
> +static char *do_read_string(int fd, struct perf_header *ph)
> +{
> +	ssize_t sz, ret;
> +	u32 len;
> +	char *buf;
> +
> +	sz = read(fd, &len, sizeof(len));
> +	if (sz < (ssize_t)sizeof(len))
> +		return NULL;
> +
> +	if (ph->needs_swap)
> +		len = bswap_32(len);
> +
> +	buf = malloc(len);
> +	if (!buf)
> +		return NULL;
> +
> +	ret = read(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;
> +	}
> +
> +	free(buf);
> +	return NULL;
> +}
> +
> +int
> +perf_header__set_cmdline(int argc, const char **argv)
> +{
> +	int i;
> +
> +	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;
> +}
> +
> +static int write_trace_info(int fd, struct perf_header *h __used,
> +			    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 __used)
> +{
> +	struct perf_session *session;
> +	int err;
> +
> +	session = container_of(h, struct perf_session, header);
> +
> +	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);
> +
> +	return 0;
> +}
> +
> +static int write_hostname(int fd, struct perf_header *h __used,
> +			  struct perf_evlist *evlist __used)
> +{
> +	struct utsname uts;
> +	int ret;
> +
> +	ret = uname(&uts);
> +	if (ret < 0)
> +		return -1;
> +
> +	return do_write_string(fd, uts.nodename);
> +}
> +
> +static int write_osrelease(int fd, struct perf_header *h __used,
> +			   struct perf_evlist *evlist __used)
> +{
> +	struct utsname uts;
> +	int ret;
> +
> +	ret = uname(&uts);
> +	if (ret < 0)
> +		return -1;
> +
> +	return do_write_string(fd, uts.release);
> +}
> +
> +static int write_arch(int fd, struct perf_header *h __used,
> +		      struct perf_evlist *evlist __used)
> +{
> +	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 __used,
> +			 struct perf_evlist *evlist __used)
> +{
> +	return do_write_string(fd, perf_version_string);
> +}
> +
> +static int write_cpuid(int fd, struct perf_header *h __used,
> +		       struct perf_evlist *evlist __used)
> +{
> +#ifndef CPUINFO_PROC
> +#define CPUINFO_PROC NULL
> +#endif
> +	FILE *file;
> +	char buf[256];
> +	char *s, *p;
> +	const char *search = CPUINFO_PROC;
> +
> +	file = fopen("/proc/cpuinfo", "r");
> +	if (!file)
> +		return -1;
> +
> +	if (search) {
> +		while (fgets(buf, sizeof(buf), file)) {
> +			if (strstr(buf, search))
> +				goto found;
> +		}
> +	}
> +	strcpy(buf, "unknown type");
> +found:
> +	fclose(file);
> +
> +	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++));
> +		}
> +		p++;
> +	}
> +	return do_write_string(fd, s);
> +}
> +
> +static int write_nrcpus(int fd, struct perf_header *h __used,
> +			struct perf_evlist *evlist __used)
> +{
> +	long nr;
> +	u32 nrc, nra;
> +	int ret;
> +
> +	nr = sysconf(_SC_NPROCESSORS_CONF);
> +	if (nr < 0)
> +		return -1;
> +
> +	nrc = (u32)(nr & UINT_MAX);
> +
> +	ret = sysconf(_SC_NPROCESSORS_ONLN);
> +	if (ret < 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));
> +}
> +
> +static int write_event_desc(int fd, struct perf_header *h __used,
> +			    struct perf_evlist *evlist)
> +{
> +	struct perf_evsel *attr;
> +	u32 nre = 0, nri, sz;
> +	int ret;
> +
> +	list_for_each_entry(attr, &evlist->entries, node)
> +		nre++;
> +
> +	/*
> +	 * 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(attr->attr);
> +	ret = do_write(fd, &sz, sizeof(sz));
> +	if (ret < 0)
> +		return ret;
> +
> +	list_for_each_entry(attr, &evlist->entries, node) {
> +
> +		ret = do_write(fd, &attr->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 = attr->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, attr->name);
> +		if (ret < 0)
> +			return ret;
> +		/*
> +		 * write unique ids for this event
> +		 */
> +		ret = do_write(fd, attr->id, attr->ids * sizeof(u64));
> +		if (ret < 0)
> +			return ret;
> +	}
> +	return 0;
> +}
> +
> +static int write_cmdline(int fd, struct perf_header *h __used,
> +			 struct perf_evlist *evlist __used)
> +{
> +	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;
> +}
> +
> +static const char *topo_fmt[] = {
> +	"/sys/devices/system/cpu/cpu%d/topology/core_siblings_list",
> +	"/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list",
> +	NULL
> +};
> +
> +static int write_topo_cpu(int fd, int cpu)
> +{
> +	FILE *fp;
> +	char filename[MAXPATHLEN];
> +	char buf[MAXPATHLEN], *p, *c;
> +	const char **q = topo_fmt;
> +	int ret;
> +
> +	while (*q) {
> +
> +		sprintf(filename, *q, cpu);
> +
> +		fp = fopen(filename, "r");
> +		if (!fp)
> +			return -1;
> +
> +		c = fgets(buf, sizeof(buf), fp);
> +		if (!c) {
> +			fclose(fp);
> +			return -1;
> +		}
> +
> +		p = strchr(buf, '\n');
> +		if (p)
> +			*p = '\0';
> +
> +		fclose(fp);
> +
> +		ret = do_write_string(fd, c);
> +		if (ret < 0)
> +			return ret;
> +		q++;
> +	}
> +	return 0;
> +}
> +
> +static int write_cpu_topology(int fd, struct perf_header *h __used,
> +			  struct perf_evlist *evlist __used)
> +{
> +	u32 nr, i;
> +	long ncpus;
> +	int ret;
> +
> +	ncpus = sysconf(_SC_NPROCESSORS_CONF);
> +	if (ncpus < 0)
> +		return -1;
> +
> +	/* hopefully, this will keep compilers happy about the cast */
> +	nr = (u32)(ncpus & UINT_MAX);
> +
> +	ret = do_write(fd, &nr, sizeof(nr));
> +	if (ret < 0)
> +		return -1;
> +
> +	for (i = 0; i < nr; i++) {
> +		ret = do_write(fd, &i, sizeof(i));
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = write_topo_cpu(fd, i);
> +		if (ret < 0)
> +			return ret;
> +	}
> +	return 0;
> +}
> +
> +static int write_total_mem(int fd, struct perf_header *h __used,
> +			  struct perf_evlist *evlist __used)
> +{
> +	char buf[MAXPATHLEN], *c;
> +	FILE *fp;
> +	int ret = -1, n;
> +	uint64_t mem;
> +
> +	fp = fopen("/proc/meminfo", "r");
> +	if (!fp)
> +		return -1;
> +
> +	while ((c = fgets(buf, sizeof(buf), fp))) {
> +		c = strstr(buf, "MemTotal:");
> +		if (c)
> +			break;
> +	}
> +	fclose(fp);
> +
> +	n = sscanf(buf, "%*s %"PRIu64, &mem);
> +	if (n == 1)
> +		ret = do_write(fd, &mem, sizeof(mem));
> +
> +	return ret;
> +}
> +
> +static int write_topo_node(int fd, int node)
> +{
> +	char filename[MAXPATHLEN];
> +	char buf[MAXPATHLEN], *p, *c;
> +	FILE *fp;
> +	uint64_t mem;
> +	int ret;
> +
> +	sprintf(filename, "/sys/devices/system/node/node%d/meminfo", node);
> +	fp = fopen(filename, "r");
> +	if (!fp)
> +		return -1;
> +
> +	while ((c = fgets(buf, sizeof(buf), fp))) {
> +		c = strstr(buf, "MemTotal:");
> +		if (c)
> +			break;
> +	}
> +	fclose(fp);
> +	if (!c)
> +		return -1;
> +	ret = sscanf(buf, "%*s %*d %*s %"PRIu64, &mem);
> +	if (ret != 1)
> +		return -1;
> +
> +	ret = do_write(fd, &mem, sizeof(mem));
> +	if (ret < 0)
> +		return -1;
> +
> +	sprintf(filename, "/sys/devices/system/node/node%d/cpulist", node);
> +	fp = fopen(filename, "r");
> +	if (!fp)
> +		return -1;
> +
> +	c = fgets(buf, sizeof(buf), fp);
> +	fclose(fp);
> +	if (!c)
> +		return -1;
> +
> +	p = strchr(buf, '\n');
> +	if (p)
> +		*p = '\0';
> +
> +	return do_write_string(fd, c);
> +}
> +
> +static int write_numa_topology(int fd, struct perf_header *h __used,
> +			  struct perf_evlist *evlist __used)
> +{
> +	char buf[MAXPATHLEN];
> +	FILE *fp;
> +	struct cpu_map *node_map;
> +	char *c;
> +	u32 nr, i, j;
> +	int ret;
> +
> +	fp = fopen("/sys/devices/system/node/online", "r");
> +	if (!fp)
> +		return -1;
> +
> +	c = fgets(buf, sizeof(buf), fp);
> +	fclose(fp);
> +	if (!c)
> +		return -1;
> +
> +	c = strchr(buf, '\n');
> +	if (c)
> +		*c = '\0';
> +
> +	node_map = cpu_map__new(buf);
> +	if (!node_map)
> +		return -1;
> +
> +	nr = (u32)node_map->nr;
> +	ret = do_write(fd, &nr, sizeof(nr));
> +	if (ret < 0)
> +		return -1;
> +
> +	for (i = 0; i < nr; i++) {
> +		j = (u32)node_map->map[i];
> +		ret = do_write(fd, &j, sizeof(j));
> +		if (ret < 0)
> +			goto error;
> +
> +		ret = write_topo_node(fd, i);
> +		if (ret < 0)
> +			goto error;
> +	}
> +	ret = 0;
> +error:
> +	free(node_map);
> +	return ret;
> +}
> +
> +static void print_hostname(struct perf_header *ph, int fd, FILE *fp)
> +{
> +	char *str = do_read_string(fd, ph);
> +	fprintf(fp, "# hostname : %s\n", str);
> +	free(str);
> +}
> +
> +static void print_osrelease(struct perf_header *ph, int fd, FILE *fp)
> +{
> +	char *str = do_read_string(fd, ph);
> +	fprintf(fp, "# os release : %s\n", str);
> +	free(str);
> +}
> +
> +static void print_arch(struct perf_header *ph, int fd, FILE *fp)
> +{
> +	char *str = do_read_string(fd, ph);
> +	fprintf(fp, "# arch : %s\n", str);
> +	free(str);
> +}
> +
> +static void print_cpuid(struct perf_header *ph, int fd, FILE *fp)
> +{
> +	char *str = do_read_string(fd, ph);
> +	fprintf(fp, "# cpuid : %s\n", str);
> +	free(str);
> +}
> +
> +static void print_nrcpus(struct perf_header *ph, int fd, FILE *fp)
> +{
> +	ssize_t ret;
> +	u32 nr;
> +
> +	ret = read(fd, &nr, sizeof(nr));
> +	if (ret != (ssize_t)sizeof(nr))
> +		nr = -1; /* interpreted as error */
> +
> +	if (ph->needs_swap)
> +		nr = bswap_32(nr);
> +
> +	fprintf(fp, "# nrcpus online : %u\n", nr);
> +
> +	ret = read(fd, &nr, sizeof(nr));
> +	if (ret != (ssize_t)sizeof(nr))
> +		nr = -1; /* interpreted as error */
> +
> +	if (ph->needs_swap)
> +		nr = bswap_32(nr);
> +
> +	fprintf(fp, "# nrcpus avail : %u\n", nr);
> +}
> +
> +static void print_version(struct perf_header *ph, int fd, FILE *fp)
> +{
> +	char *str = do_read_string(fd, ph);
> +	fprintf(fp, "# perf version : %s\n", str);
> +	free(str);
> +}
> +
> +static void print_cmdline(struct perf_header *ph, int fd, FILE *fp)
> +{
> +	ssize_t ret;
> +	char *str;
> +	u32 nr, i;
> +
> +	ret = read(fd, &nr, sizeof(nr));
> +	if (ret != (ssize_t)sizeof(nr)) {
> +		fprintf(fp, "# cmdline : unknown\n");
> +		return;
> +	}
> +
> +	if (ph->needs_swap)
> +		nr = bswap_32(nr);
> +
> +	fprintf(fp, "# cmdline : ");
> +
> +	for (i = 0; i < nr; i++) {
> +		str = do_read_string(fd, ph);
> +		fprintf(fp, "%s ", str);
> +		free(str);
> +	}
> +	fputc('\n', fp);
> +}
> +
> +static void print_cpu_topology(struct perf_header *ph, int fd, FILE *fp)
> +{
> +	ssize_t ret;
> +	u32 nr, c, i;
> +	char *str;
> +
> +	ret = read(fd, &nr, sizeof(nr));
> +	if (ret != (ssize_t)sizeof(nr)) {
> +		fprintf(fp, "# cpu topology  : not available\n");
> +		return;
> +	}
> +
> +	if (ph->needs_swap)
> +		nr = bswap_32(nr);
> +
> +	for (i = 0; i < nr; i++) {
> +
> +		ret = read(fd, &c, sizeof(c));
> +		if (ret != (ssize_t)sizeof(c))
> +			c = -1; /* interpreted as error */
> +
> +		if (ph->needs_swap)
> +			c = bswap_32(c);
> +
> +		str = do_read_string(fd, ph);
> +		fprintf(fp, "# CPU%u sibling cores  : %s\n", c, str);
> +		free(str);
> +
> +		str = do_read_string(fd, ph);
> +		fprintf(fp, "# CPU%u sibling threads: %s\n", c, str);
> +		free(str);
> +	}
> +}
> +
> +static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
> +{
> +	struct perf_event_attr attr;
> +	uint64_t id;
> +	void *buf = NULL;
> +	char *str;
> +	u32 nre, sz, nr, i, j, msz;
> +	int ret;
> +
> +	/* number of events */
> +	ret = read(fd, &nre, sizeof(nre));
> +	if (ret != (ssize_t)sizeof(nre))
> +		goto error;
> +
> +	if (ph->needs_swap)
> +		nre = bswap_32(nre);
> +
> +	ret = read(fd, &sz, sizeof(sz));
> +	if (ret != (ssize_t)sizeof(sz))
> +		goto error;
> +
> +	if (ph->needs_swap)
> +		sz = bswap_32(sz);
> +
> +	/*
> +	 * ensure it is at least to our ABI rev
> +	 */
> +	if (sz < (u32)sizeof(attr))
> +		goto error;
> +
> +	memset(&attr, 0, sizeof(attr));
> +
> +	/* read entire region to sync up to next field */
> +	buf = malloc(sz);
> +	if (!buf)
> +		goto error;
> +
> +	msz = sizeof(attr);
> +	if (sz < msz)
> +		msz = sz;
> +
> +	for (i = 0 ; i < nre; i++) {
> +
> +		ret = read(fd, buf, sz);
> +		if (ret != (ssize_t)sz)
> +			goto error;
> +
> +		if (ph->needs_swap)
> +			perf_event__attr_swap(buf);
> +
> +		memcpy(&attr, buf, msz);
> +
> +		ret = read(fd, &nr, sizeof(nr));
> +		if (ret != (ssize_t)sizeof(nr))
> +			goto error;
> +
> +		if (ph->needs_swap)
> +			nr = bswap_32(nr);
> +
> +		str = do_read_string(fd, ph);
> +		fprintf(fp, "# event = %s, ", str);
> +		free(str);
> +
> +		fprintf(fp, "type = %d, config = 0x%"PRIx64
> +			    ", config1 = 0x%"PRIx64", config2 = 0x%"PRIx64,
> +				attr.type,
> +				(u64)attr.config,
> +				(u64)attr.config1,
> +				(u64)attr.config2);
> +
> +		fprintf(fp, ", excl_usr = %d, excl_kern = %d",
> +				attr.exclude_user,
> +				attr.exclude_kernel);
> +
> +		if (nr)
> +			fprintf(fp, ", id = {");
> +
> +		for (j = 0 ; j < nr; j++) {
> +			ret = read(fd, &id, sizeof(id));
> +			if (ret != (ssize_t)sizeof(id))
> +				goto error;
> +
> +			if (ph->needs_swap)
> +				id = bswap_64(id);
> +
> +			if (j)
> +				fputc(',', fp);
> +
> +			fprintf(fp, " %"PRIu64, id);
> +		}
> +		if (nr && j == nr)
> +			fprintf(fp, " }");
> +		fputc('\n', fp);
> +	}
> +	free(buf);
> +	return;
> +error:
> +	fprintf(fp, "# event desc: not available or unable to read\n");
> +}
> +
> +static void print_total_mem(struct perf_header *h __used, int fd, FILE *fp)
> +{
> +	uint64_t mem;
> +	ssize_t ret;
> +
> +	ret = read(fd, &mem, sizeof(mem));
> +	if (ret != sizeof(mem))
> +		goto error;
> +
> +	if (h->needs_swap)
> +		mem = bswap_64(mem);
> +
> +	fprintf(fp, "# total memory: %"PRIu64" kB\n", mem);
> +	return;
> +error:
> +	fprintf(fp, "# total memory: unknown\n");
> +}
> +
> +static void print_numa_topology(struct perf_header *h __used, int fd, FILE *fp)
> +{
> +	ssize_t ret;
> +	u32 nr, c, i;
> +	char *str;
> +	uint64_t mem;
> +
> +	/* nr nodes */
> +	ret = read(fd, &nr, sizeof(nr));
> +	if (ret != (ssize_t)sizeof(nr))
> +		goto error;
> +
> +	if (h->needs_swap)
> +		nr = bswap_32(nr);
> +
> +	for (i = 0; i < nr; i++) {
> +
> +		/* node number */
> +		ret = read(fd, &c, sizeof(c));
> +		if (ret != (ssize_t)sizeof(c))
> +			goto error;
> +
> +		if (h->needs_swap)
> +			c = bswap_32(c);
> +
> +		ret = read(fd, &mem, sizeof(mem));
> +		if (ret != sizeof(mem))
> +			goto error;
> +
> +		if (h->needs_swap)
> +			mem = bswap_64(mem);
> +
> +		fprintf(fp, "# node%u meminfo : %"PRIu64" kB\n", c, mem);
> +
> +		str = do_read_string(fd, h);
> +		fprintf(fp, "# node%u cpu list: %s\n", c, str);
> +		free(str);
> +	}
> +	return;
> +error:
> +	fprintf(fp, "# numa topology : not available\n");
> +}
> +
> +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);
> +};
> +
> +#define FEAT_OP(n, w, p) [n] = { .write = w, .print = p }
> +
> +static struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
> +	FEAT_OP(HEADER_TRACE_INFO, write_trace_info, NULL),
> +	FEAT_OP(HEADER_BUILD_ID, write_build_id, NULL),
> +	FEAT_OP(HEADER_HOSTNAME, write_hostname, print_hostname),
> +	FEAT_OP(HEADER_OSRELEASE, write_osrelease, print_osrelease),
> +	FEAT_OP(HEADER_ARCH, write_arch, print_arch),
> +	FEAT_OP(HEADER_CPUID, write_cpuid, print_cpuid),
> +	FEAT_OP(HEADER_NRCPUS, write_nrcpus, print_nrcpus),
> +	FEAT_OP(HEADER_EVENT_DESC, write_event_desc, print_event_desc),
> +	FEAT_OP(HEADER_CMDLINE, write_cmdline, print_cmdline),
> +	FEAT_OP(HEADER_VERSION, write_version, print_version),
> +	FEAT_OP(HEADER_CPU_TOPOLOGY, write_cpu_topology, print_cpu_topology),
> +	FEAT_OP(HEADER_TOTAL_MEM, write_total_mem, print_total_mem),
> +	FEAT_OP(HEADER_NUMA_TOPOLOGY, write_numa_topology, print_numa_topology),
> +};
> +
> +static int perf_header_fprintf_info(struct perf_file_section *section,
> +				    struct perf_header *ph,
> +				    int feat, int fd, void *data)
> +{
> +	FILE *fp = data;
> +
> +	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_TRACE_INFO || feat >= HEADER_LAST_FEATURE) {
> +		pr_warning("unknown feature %d\n", feat);
> +		return -1;
> +	}
> +
> +	if (feat_ops[feat].print)
> +		feat_ops[feat].print(ph, fd, fp);
> +
> +	return 0;
> +}
> +
> +int perf_header__fprintf_info(struct perf_session *session, FILE *fp __used)
> +{
> +	int fd = session->fd;
> +	struct perf_header *header = &session->header;
> +	perf_header__process_sections(header, fd, fp, perf_header_fprintf_info);
> +	return 0;
> +}
> +
>  #define dsos__for_each_with_build_id(pos, head)	\
>  	list_for_each_entry(pos, head, node)	\
>  		if (!pos->has_build_id)		\
> @@ -356,15 +1201,41 @@ static bool perf_session__read_build_ids(struct perf_session *session, bool with
>  	return ret;
>  }
>  
> +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_header__has_feat(h, type)) {
> +
> +		(*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;
> +}
> +
>  static int perf_header__adds_write(struct perf_header *header,
>  				   struct perf_evlist *evlist, int fd)
>  {
>  	int nr_sections;
>  	struct perf_session *session;
> -	struct perf_file_section *feat_sec;
> +	struct perf_file_section *feat_sec, *p;
>  	int sec_size;
>  	u64 sec_start;
> -	int idx = 0, err;
> +	int err;
>  
>  	session = container_of(header, struct perf_session, header);
>  
> @@ -376,7 +1247,7 @@ static int perf_header__adds_write(struct perf_header *header,
>  	if (!nr_sections)
>  		return 0;
>  
> -	feat_sec = calloc(sizeof(*feat_sec), nr_sections);
> +	feat_sec = p = calloc(sizeof(*feat_sec), nr_sections);
>  	if (feat_sec == NULL)
>  		return -ENOMEM;
>  
> @@ -385,36 +1256,65 @@ static int perf_header__adds_write(struct perf_header *header,
>  	sec_start = header->data_offset + header->data_size;
>  	lseek(fd, sec_start + sec_size, SEEK_SET);
>  
> -	if (perf_header__has_feat(header, HEADER_TRACE_INFO)) {
> -		struct perf_file_section *trace_sec;
> -
> -		trace_sec = &feat_sec[idx++];
> +	err = do_write_feat(fd, header, HEADER_TRACE_INFO, &p, evlist);
> +	if (err)
> +		goto out_free;
>  
> -		/* Write trace info */
> -		trace_sec->offset = lseek(fd, 0, SEEK_CUR);
> -		read_tracing_data(fd, &evlist->entries);
> -		trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset;
> +	err = do_write_feat(fd, header, HEADER_BUILD_ID, &p, evlist);
> +	if (err) {
> +		perf_header__clear_feat(header, HEADER_BUILD_ID);
> +		goto out_free;
>  	}
>  
> -	if (perf_header__has_feat(header, HEADER_BUILD_ID)) {
> -		struct perf_file_section *buildid_sec;
> +	err = do_write_feat(fd, header, HEADER_HOSTNAME, &p, evlist);
> +	if (err)
> +		perf_header__clear_feat(header, HEADER_HOSTNAME);
>  
> -		buildid_sec = &feat_sec[idx++];
> +	err = do_write_feat(fd, header, HEADER_OSRELEASE, &p, evlist);
> +	if (err)
> +		perf_header__clear_feat(header, HEADER_OSRELEASE);
>  
> -		/* Write build-ids */
> -		buildid_sec->offset = lseek(fd, 0, SEEK_CUR);
> -		err = dsos__write_buildid_table(header, 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;
> -		if (!no_buildid_cache)
> -			perf_session__cache_build_ids(session);
> -	}
> +	err = do_write_feat(fd, header, HEADER_ARCH, &p, evlist);
> +	if (err)
> +		perf_header__clear_feat(header, HEADER_ARCH);
> +
> +	err = do_write_feat(fd, header, HEADER_CPUID, &p, evlist);
> +	if (err)
> +		perf_header__clear_feat(header, HEADER_CPUID);
> +
> +	err = do_write_feat(fd, header, HEADER_NRCPUS, &p, evlist);
> +	if (err)
> +		perf_header__clear_feat(header, HEADER_NRCPUS);
> +
> +	err = do_write_feat(fd, header, HEADER_EVENT_DESC, &p, evlist);
> +	if (err)
> +		perf_header__clear_feat(header, HEADER_EVENT_DESC);
> +
> +	err = do_write_feat(fd, header, HEADER_CMDLINE, &p, evlist);
> +	if (err)
> +		perf_header__clear_feat(header, HEADER_CMDLINE);
> +
> +	err = do_write_feat(fd, header, HEADER_VERSION, &p, evlist);
> +	if (err)
> +		perf_header__clear_feat(header, HEADER_VERSION);
> +
> +	err = do_write_feat(fd, header, HEADER_CPU_TOPOLOGY, &p, evlist);
> +	if (err)
> +		perf_header__clear_feat(header, HEADER_CPU_TOPOLOGY);
> +
> +	err = do_write_feat(fd, header, HEADER_TOTAL_MEM, &p, evlist);
> +	if (err)
> +		perf_header__clear_feat(header, HEADER_TOTAL_MEM);
> +
> +	err = do_write_feat(fd, header, HEADER_NUMA_TOPOLOGY, &p, evlist);
> +	if (err)
> +		perf_header__clear_feat(header, HEADER_NUMA_TOPOLOGY);
>  
>  	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");
> @@ -554,9 +1454,10 @@ static int perf_header__getbuffer64(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))
> +				  struct perf_header *ph,
> +				  int feat, int fd, void *data))
>  {
>  	struct perf_file_section *feat_sec;
>  	int nr_sections;
> @@ -584,7 +1485,7 @@ int perf_header__process_sections(struct perf_header *header, int fd,
>  		if (perf_header__has_feat(header, feat)) {
>  			struct perf_file_section *sec = &feat_sec[idx++];
>  
> -			err = process(sec, header, feat, fd);
> +			err = process(sec, header, feat, fd, data);
>  			if (err < 0)
>  				break;
>  		}
> @@ -796,7 +1697,7 @@ out:
>  
>  static int perf_file_section__process(struct perf_file_section *section,
>  				      struct perf_header *ph,
> -				      int feat, int fd)
> +				      int feat, int fd, void *data __used)
>  {
>  	if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) {
>  		pr_debug("Failed to lseek to %" PRIu64 " offset for feature "
> @@ -935,7 +1836,8 @@ int perf_session__read_header(struct perf_session *session, int fd)
>  		event_count =  f_header.event_types.size / sizeof(struct perf_trace_event_type);
>  	}
>  
> -	perf_header__process_sections(header, fd, perf_file_section__process);
> +	perf_header__process_sections(header, fd, NULL,
> +				      perf_file_section__process);
>  
>  	lseek(fd, header->data_offset, SEEK_SET);
>  
> diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
> index 1886256..455480b 100644
> --- a/tools/perf/util/header.h
> +++ b/tools/perf/util/header.h
> @@ -12,6 +12,19 @@
>  enum {
>  	HEADER_TRACE_INFO = 1,
>  	HEADER_BUILD_ID,
> +
> +	HEADER_HOSTNAME,
> +	HEADER_OSRELEASE,
> +	HEADER_ARCH,
> +	HEADER_CPUID,
> +	HEADER_NRCPUS,
> +	HEADER_EVENT_DESC,
> +	HEADER_CMDLINE,
> +	HEADER_VERSION,
> +	HEADER_CPU_TOPOLOGY,
> +	HEADER_TOTAL_MEM,
> +	HEADER_NUMA_TOPOLOGY,
> +
>  	HEADER_LAST_FEATURE,
>  };
>  
> @@ -68,10 +81,15 @@ void perf_header__set_feat(struct perf_header *header, int feat);
>  void perf_header__clear_feat(struct perf_header *header, int feat);
>  bool perf_header__has_feat(const struct perf_header *header, int feat);
>  
> +int perf_header__set_cmdline(int argc, const char **argv);
> +
>  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));
> +				  struct perf_header *ph,
> +				  int feat, int fd, void *data));
> +
> +int perf_header__fprintf_info(struct perf_session *session, FILE *fp);
>  
>  int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
>  			  const char *name, bool is_kallsyms);
> diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
> index 72458d9..54613a2 100644
> --- a/tools/perf/util/session.c
> +++ b/tools/perf/util/session.c
> @@ -1326,3 +1326,21 @@ int perf_session__cpu_bitmap(struct perf_session *session,
>  
>  	return 0;
>  }
> +
> +void perf_session__fprintf_info(struct perf_session *session, FILE *fp)
> +{
> +	struct stat st;
> +	int ret;
> +
> +	if (session == NULL || fp == NULL)
> +		return;
> +
> +	ret = fstat(session->fd, &st);
> +	if (ret == -1)
> +		return;
> +
> +	fprintf(fp, "#\n# captured on: %s", ctime(&st.st_ctime));
> +	ret = perf_header__fprintf_info(session, fp);
> +	if (ret == 0)
> +		fprintf(fp, "#\n");
> +}
> diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
> index 170601e..33a3a46 100644
> --- a/tools/perf/util/session.h
> +++ b/tools/perf/util/session.h
> @@ -176,4 +176,5 @@ void perf_session__print_ip(union perf_event *event,
>  int perf_session__cpu_bitmap(struct perf_session *session,
>  			     const char *cpu_list, unsigned long *cpu_bitmap);
>  
> +void perf_session__fprintf_info(struct perf_session *session, FILE *fp);
>  #endif /* __PERF_SESSION_H */

Reviewed-by: David Ahern <dsahern@gmail.com>
Tested-by: David Ahern <dsahern@gmail.com>

The problem with endianness handling I mentioned last week is related to
the assumption that the HEADER_BUILD_ID is set. In my test case it is
not (-B flag on perf-record) and that causes the aborts. A problem for
another day which I can address it when I submit the patch to handle
swapping for the feature mask.

David

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] perf: make perf.data more self-descriptive (v4)
  2011-09-12 13:45 ` David Ahern
@ 2011-09-12 13:54   ` Stephane Eranian
  2011-09-12 14:33     ` David Ahern
  0 siblings, 1 reply; 21+ messages in thread
From: Stephane Eranian @ 2011-09-12 13:54 UTC (permalink / raw)
  To: David Ahern; +Cc: linux-kernel, acme, peterz, mingo

On Mon, Sep 12, 2011 at 3:45 PM, David Ahern <dsahern@gmail.com> wrote:
> On 09/07/2011 01:10 PM, Stephane Eranian wrote:
>>
>> The goal of this patch is to include more information
>> about the host environment into the perf.data so it is
>> more self-descriptive. Overtime, profiles are captured
>> on various machines and it becomes hard to track what
>> was recorded, on what machine and when.
>>
>> This patch provides a way to solve this by extending
>> the perf.data file with basic information about the
>> host machine. To add those extensions, we leverage
>> the feature bits capabilities of the perf.data format.
>> The change is backward compatible with existing perf.data
>> files.
>>
>> We define the following useful new extensions:
>>  - HEADER_HOSTNAME: the hostname
>>  - HEADER_OSRELEASE: the kernel release number
>>  - HEADER_ARCH: the hw architecture
>>  - HEADER_CPUID: the cpu model description
>>  - HEADER_NRCPUS: number of online/avail cpus
>>  - HEADER_CMDLINE: perf command line
>>  - HEADER_VERSION: perf version
>>  - HEADER_TOPOLOGY: cpu topology
>>  - HEADER_EVENT_DESC: full event description (attrs)
>>
>> The small granularity for the entries is to make it easier
>> to extend without breaking backward compatiblity. All entries
>> are provided as ASCII strings, easy to parse, except for the
>> event description.
>>
>> In the second version, we dropped the -I option for the
>> perf record command. The extra information is systematically
>> captured. But it's still displayed optionally by perf report.
>> There are also a couple of cleanups of function prototypes
>> and global variables.
>>
>> In the third version, we:
>> - fixed missing MIPS support for CPUID
>> - used u32 instead of int when writing integers to file
>> - fixed couple of bswap() bugs
>> - refactorize all write/print functions using function pointers
>> - improved write_cmdline() to include actual path to perf binary
>>
>> In the fourth version, we:
>> - fixed a couple of int vs. u32 issues
>> - factorized HEADER_BUILD_ID, HEADER_TRACE_INFO
>> - added -I option to perf script report
>> - reverted change to evsel.c (unrelated to meta-data)
>> - modified write_*() to always write despite errors (adds_feature bit present)
>> - added HEADER_TOTAL_MEM: total memory installed on this system
>> - added HEADER_NUMA_TOPOLOGY: shows NUMA nodes and for each memory, cpu list
>> - renamed HEADER_TOPOLOGY to HEADER_CPU_TOPOLOGY
>>
>>
>> $ perf record -ecycles,instructions noploop 1
>> noploop for 1 seconds
>> [ perf record: Woken up 1 times to write data ]
>> [ perf record: Captured and wrote 0.098 MB perf.data (~4260 samples) ]
>> $ perf report -I --stdio
>> #
>> # captured on: Wed Sep  7 21:00:10 2011
>> # hostname : quad
>> # os release : 3.1.0-rc4-tip
>> # arch : x86_64
>> # cpuid : Intel(R) Core(TM)2 Quad CPU Q6600 @ 2.40GHz
>> # nrcpus online : 4
>> # nrcpus avail : 4
>> # event = cycles, type = 0, config = 0x0, config1 = 0x0, config2 = 0x0, excl_usr = 0, excl_kern = 0, id = { 536, 537, 538, 539 }
>> # event = instructions, type = 0, config = 0x1, config1 = 0x0, config2 = 0x0, excl_usr = 0, excl_kern = 0, id = { 540, 541, 542,
>> # cmdline : /home/eranian/perfmon/official/tip/build/tools/perf/perf record -ecycles,instructions noploop 1
>> # perf version : 3.1.0-rc4
>> # CPU0   sibling cores  : 0-3
>> # CPU0   sibling threads: 0
>> # CPU1   sibling cores  : 0-3
>> # CPU1   sibling threads: 1
>> # CPU2   sibling cores  : 0-3
>> # CPU2   sibling threads: 2
>> # CPU3   sibling cores  : 0-3
>> # CPU3   sibling threads: 3
>> # total memory: 8105344 kB
>> # node0   meminfo : 8320608 kB
>> # node0   cpu list: 0-3
>> #
>> # Events: 995  cycles
>> #
>> # Overhead  Command      Shared Object                Symbol
>> # ........  .......  .................  ....................
>> #
>>     99.90%  noploop  noploop            [.] noploop
>>      0.10%  noploop  [kernel.kallsyms]  [k] trace_hardirqs_on
>>      0.00%  noploop  [kernel.kallsyms]  [k] lock_release
>>      0.00%  noploop  [kernel.kallsyms]  [k] intel_pmu_enable_all
>> ...
>>
>>
>> Signed-off-by: Stephane Eranian <eranian@google.com>
>> ---
>>
>> diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt
>> index 04253c0..191e10e 100644
>> --- a/tools/perf/Documentation/perf-report.txt
>> +++ b/tools/perf/Documentation/perf-report.txt
>> @@ -134,6 +134,11 @@ OPTIONS
>>       CPUs are specified with -: 0-2. Default is to report samples on all
>>       CPUs.
>>
>> +-I::
>> +--show-info::
>> +     Display information about the perf.data file, incl. hostname,
>> +     os release, perf version, machine desc.
>> +
>>  SEE ALSO
>>  --------
>>  linkperf:perf-stat[1]
>> diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt
>> index db01786..ebf8a23 100644
>> --- a/tools/perf/Documentation/perf-script.txt
>> +++ b/tools/perf/Documentation/perf-script.txt
>> @@ -188,6 +188,13 @@ OPTIONS
>>       CPUs are specified with -: 0-2. Default is to report samples on all
>>       CPUs.
>>
>> +-I::
>> +--show-info::
>> +     Display information about the perf.data file, incl. hostname,
>> +     os release, perf version, machine desc. It can only be used with the
>> +     perf script report mode and it must be specified after the script name
>> +     like for instance: perf script report syscall-counts -I.
>> +
>>  SEE ALSO
>>  --------
>>  linkperf:perf-record[1], linkperf:perf-script-perl[1],
>> diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
>> index 6b0519f..524ebbf 100644
>> --- a/tools/perf/builtin-record.c
>> +++ b/tools/perf/builtin-record.c
>> @@ -513,6 +513,18 @@ static int __cmd_record(int argc, const char **argv)
>>       if (have_tracepoints(&evsel_list->entries))
>>               perf_header__set_feat(&session->header, HEADER_TRACE_INFO);
>>
>> +     perf_header__set_feat(&session->header, HEADER_HOSTNAME);
>> +     perf_header__set_feat(&session->header, HEADER_OSRELEASE);
>> +     perf_header__set_feat(&session->header, HEADER_ARCH);
>> +     perf_header__set_feat(&session->header, HEADER_CPUID);
>> +     perf_header__set_feat(&session->header, HEADER_NRCPUS);
>> +     perf_header__set_feat(&session->header, HEADER_EVENT_DESC);
>> +     perf_header__set_feat(&session->header, HEADER_CMDLINE);
>> +     perf_header__set_feat(&session->header, HEADER_VERSION);
>> +     perf_header__set_feat(&session->header, HEADER_CPU_TOPOLOGY);
>> +     perf_header__set_feat(&session->header, HEADER_TOTAL_MEM);
>> +     perf_header__set_feat(&session->header, HEADER_NUMA_TOPOLOGY);
>> +
>>       /* 512 kiB: default amount of unprivileged mlocked memory */
>>       if (mmap_pages == UINT_MAX)
>>               mmap_pages = (512 * 1024) / page_size;
>> @@ -782,6 +794,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
>>       int err = -ENOMEM;
>>       struct perf_evsel *pos;
>>
>> +     perf_header__set_cmdline(argc, argv);
>> +
>>       evsel_list = perf_evlist__new(NULL, NULL);
>>       if (evsel_list == NULL)
>>               return -ENOMEM;
>> diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
>> index d7ff277..7721030 100644
>> --- a/tools/perf/builtin-report.c
>> +++ b/tools/perf/builtin-report.c
>> @@ -40,6 +40,7 @@ static char         const *input_name = "perf.data";
>>  static bool          force, use_tui, use_stdio;
>>  static bool          hide_unresolved;
>>  static bool          dont_use_callchains;
>> +static bool          show_info;
>>
>>  static bool          show_threads;
>>  static struct perf_read_values       show_threads_values;
>> @@ -276,6 +277,9 @@ static int __cmd_report(void)
>>                       goto out_delete;
>>       }
>>
>> +     if (show_info)
>> +             perf_session__fprintf_info(session, stdout);
>> +
>>       if (show_threads)
>>               perf_read_values_init(&show_threads_values);
>>
>> @@ -487,6 +491,8 @@ static const struct option options[] = {
>>       OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
>>                   "Look for files with symbols relative to this directory"),
>>       OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"),
>> +     OPT_BOOLEAN('I', "show-info", &show_info,
>> +                     "display information about perf.data file"),
>>       OPT_END()
>>  };
>>
>> diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
>> index 09024ec..dac86ab 100644
>> --- a/tools/perf/builtin-script.c
>> +++ b/tools/perf/builtin-script.c
>> @@ -22,6 +22,7 @@ static u64                  last_timestamp;
>>  static u64                   nr_unordered;
>>  extern const struct option   record_options[];
>>  static bool                  no_callchain;
>> +static bool                  show_info = false;
>>  static const char            *cpu_list;
>>  static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
>>
>> @@ -1083,7 +1084,8 @@ static const struct option options[] = {
>>                    "comma separated output fields prepend with 'type:'. Valid types: hw,sw,trace,raw. Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso,addr",
>>                    parse_output_fields),
>>       OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"),
>> -
>> +     OPT_BOOLEAN('I', "show-info", &show_info,
>> +                     "display host information from perf.data file"),
>>       OPT_END()
>>  };
>>
>> @@ -1268,6 +1270,9 @@ int cmd_script(int argc, const char **argv, const char *prefix __used)
>>                       return -1;
>>       }
>>
>> +     if (show_info)
>> +             perf_session__fprintf_info(session, stdout);
>> +
>>       if (!no_callchain)
>>               symbol_conf.use_callchain = true;
>>       else
>> diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h
>> index 4702e24..b382bd5 100644
>> --- a/tools/perf/builtin.h
>> +++ b/tools/perf/builtin.h
>> @@ -4,7 +4,6 @@
>>  #include "util/util.h"
>>  #include "util/strbuf.h"
>>
>> -extern const char perf_version_string[];
>>  extern const char perf_usage_string[];
>>  extern const char perf_more_info_string[];
>>
>> diff --git a/tools/perf/perf.h b/tools/perf/perf.h
>> index a5fc660..08b0b5e 100644
>> --- a/tools/perf/perf.h
>> +++ b/tools/perf/perf.h
>> @@ -9,18 +9,21 @@ void get_term_dimensions(struct winsize *ws);
>>  #include "../../arch/x86/include/asm/unistd.h"
>>  #define rmb()                asm volatile("lock; addl $0,0(%%esp)" ::: "memory")
>>  #define cpu_relax()  asm volatile("rep; nop" ::: "memory");
>> +#define CPUINFO_PROC "model name"
>>  #endif
>>
>>  #if defined(__x86_64__)
>>  #include "../../arch/x86/include/asm/unistd.h"
>>  #define rmb()                asm volatile("lfence" ::: "memory")
>>  #define cpu_relax()  asm volatile("rep; nop" ::: "memory");
>> +#define CPUINFO_PROC "model name"
>>  #endif
>>
>>  #ifdef __powerpc__
>>  #include "../../arch/powerpc/include/asm/unistd.h"
>>  #define rmb()                asm volatile ("sync" ::: "memory")
>>  #define cpu_relax()  asm volatile ("" ::: "memory");
>> +#define CPUINFO_PROC "cpu"
>>  #endif
>>
>>  #ifdef __s390__
>> @@ -37,30 +40,35 @@ void get_term_dimensions(struct winsize *ws);
>>  # define rmb()               asm volatile("" ::: "memory")
>>  #endif
>>  #define cpu_relax()  asm volatile("" ::: "memory")
>> +#define CPUINFO_PROC "cpu type"
>>  #endif
>>
>>  #ifdef __hppa__
>>  #include "../../arch/parisc/include/asm/unistd.h"
>>  #define rmb()                asm volatile("" ::: "memory")
>>  #define cpu_relax()  asm volatile("" ::: "memory");
>> +#define CPUINFO_PROC "cpu"
>>  #endif
>>
>>  #ifdef __sparc__
>>  #include "../../arch/sparc/include/asm/unistd.h"
>>  #define rmb()                asm volatile("":::"memory")
>>  #define cpu_relax()  asm volatile("":::"memory")
>> +#define CPUINFO_PROC "cpu"
>>  #endif
>>
>>  #ifdef __alpha__
>>  #include "../../arch/alpha/include/asm/unistd.h"
>>  #define rmb()                asm volatile("mb" ::: "memory")
>>  #define cpu_relax()  asm volatile("" ::: "memory")
>> +#define CPUINFO_PROC "cpu model"
>>  #endif
>>
>>  #ifdef __ia64__
>>  #include "../../arch/ia64/include/asm/unistd.h"
>>  #define rmb()                asm volatile ("mf" ::: "memory")
>>  #define cpu_relax()  asm volatile ("hint @pause" ::: "memory")
>> +#define CPUINFO_PROC "model name"
>>  #endif
>>
>>  #ifdef __arm__
>> @@ -71,6 +79,7 @@ void get_term_dimensions(struct winsize *ws);
>>   */
>>  #define rmb()                ((void(*)(void))0xffff0fa0)()
>>  #define cpu_relax()  asm volatile("":::"memory")
>> +#define CPUINFO_PROC "Processor"
>>  #endif
>>
>>  #ifdef __mips__
>> @@ -83,6 +92,7 @@ void get_term_dimensions(struct winsize *ws);
>>                               : /* no input */                        \
>>                               : "memory")
>>  #define cpu_relax()  asm volatile("" ::: "memory")
>> +#define CPUINFO_PROC "cpu model"
>>  #endif
>>
>>  #include <time.h>
>> @@ -171,5 +181,6 @@ struct ip_callchain {
>>  };
>>
>>  extern bool perf_host, perf_guest;
>> +extern const char perf_version_string[];
>>
>>  #endif
>> diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
>> index b6c1ad1..73fa59e 100644
>> --- a/tools/perf/util/header.c
>> +++ b/tools/perf/util/header.c
>> @@ -7,6 +7,7 @@
>>  #include <stdlib.h>
>>  #include <linux/list.h>
>>  #include <linux/kernel.h>
>> +#include <sys/utsname.h>
>>
>>  #include "evlist.h"
>>  #include "evsel.h"
>> @@ -17,12 +18,19 @@
>>  #include "session.h"
>>  #include "symbol.h"
>>  #include "debug.h"
>> +#include "cpumap.h"
>>
>>  static bool no_buildid_cache = false;
>>
>>  static int event_count;
>>  static struct perf_trace_event_type *events;
>>
>> +static u32 header_argc;
>> +static const char **header_argv;
>> +
>> +static int dsos__write_buildid_table(struct perf_header *header, int fd);
>> +static int perf_session__cache_build_ids(struct perf_session *session);
>> +
>>  int perf_header__push_event(u64 id, const char *name)
>>  {
>>       if (strlen(name) > MAX_EVENT_NAME)
>> @@ -110,6 +118,843 @@ static int write_padded(int fd, const void *bf, size_t count,
>>       return err;
>>  }
>>
>> +static int do_write_string(int fd, const char *str)
>> +{
>> +     u32 len, olen;
>> +     int ret;
>> +
>> +     olen = strlen(str) + 1;
>> +     len = ALIGN(olen, NAME_ALIGN);
>> +
>> +     /* write len, incl. \0 */
>> +     ret = do_write(fd, &len, sizeof(len));
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     return write_padded(fd, str, olen, len);
>> +}
>> +
>> +static char *do_read_string(int fd, struct perf_header *ph)
>> +{
>> +     ssize_t sz, ret;
>> +     u32 len;
>> +     char *buf;
>> +
>> +     sz = read(fd, &len, sizeof(len));
>> +     if (sz < (ssize_t)sizeof(len))
>> +             return NULL;
>> +
>> +     if (ph->needs_swap)
>> +             len = bswap_32(len);
>> +
>> +     buf = malloc(len);
>> +     if (!buf)
>> +             return NULL;
>> +
>> +     ret = read(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;
>> +     }
>> +
>> +     free(buf);
>> +     return NULL;
>> +}
>> +
>> +int
>> +perf_header__set_cmdline(int argc, const char **argv)
>> +{
>> +     int i;
>> +
>> +     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;
>> +}
>> +
>> +static int write_trace_info(int fd, struct perf_header *h __used,
>> +                         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 __used)
>> +{
>> +     struct perf_session *session;
>> +     int err;
>> +
>> +     session = container_of(h, struct perf_session, header);
>> +
>> +     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);
>> +
>> +     return 0;
>> +}
>> +
>> +static int write_hostname(int fd, struct perf_header *h __used,
>> +                       struct perf_evlist *evlist __used)
>> +{
>> +     struct utsname uts;
>> +     int ret;
>> +
>> +     ret = uname(&uts);
>> +     if (ret < 0)
>> +             return -1;
>> +
>> +     return do_write_string(fd, uts.nodename);
>> +}
>> +
>> +static int write_osrelease(int fd, struct perf_header *h __used,
>> +                        struct perf_evlist *evlist __used)
>> +{
>> +     struct utsname uts;
>> +     int ret;
>> +
>> +     ret = uname(&uts);
>> +     if (ret < 0)
>> +             return -1;
>> +
>> +     return do_write_string(fd, uts.release);
>> +}
>> +
>> +static int write_arch(int fd, struct perf_header *h __used,
>> +                   struct perf_evlist *evlist __used)
>> +{
>> +     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 __used,
>> +                      struct perf_evlist *evlist __used)
>> +{
>> +     return do_write_string(fd, perf_version_string);
>> +}
>> +
>> +static int write_cpuid(int fd, struct perf_header *h __used,
>> +                    struct perf_evlist *evlist __used)
>> +{
>> +#ifndef CPUINFO_PROC
>> +#define CPUINFO_PROC NULL
>> +#endif
>> +     FILE *file;
>> +     char buf[256];
>> +     char *s, *p;
>> +     const char *search = CPUINFO_PROC;
>> +
>> +     file = fopen("/proc/cpuinfo", "r");
>> +     if (!file)
>> +             return -1;
>> +
>> +     if (search) {
>> +             while (fgets(buf, sizeof(buf), file)) {
>> +                     if (strstr(buf, search))
>> +                             goto found;
>> +             }
>> +     }
>> +     strcpy(buf, "unknown type");
>> +found:
>> +     fclose(file);
>> +
>> +     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++));
>> +             }
>> +             p++;
>> +     }
>> +     return do_write_string(fd, s);
>> +}
>> +
>> +static int write_nrcpus(int fd, struct perf_header *h __used,
>> +                     struct perf_evlist *evlist __used)
>> +{
>> +     long nr;
>> +     u32 nrc, nra;
>> +     int ret;
>> +
>> +     nr = sysconf(_SC_NPROCESSORS_CONF);
>> +     if (nr < 0)
>> +             return -1;
>> +
>> +     nrc = (u32)(nr & UINT_MAX);
>> +
>> +     ret = sysconf(_SC_NPROCESSORS_ONLN);
>> +     if (ret < 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));
>> +}
>> +
>> +static int write_event_desc(int fd, struct perf_header *h __used,
>> +                         struct perf_evlist *evlist)
>> +{
>> +     struct perf_evsel *attr;
>> +     u32 nre = 0, nri, sz;
>> +     int ret;
>> +
>> +     list_for_each_entry(attr, &evlist->entries, node)
>> +             nre++;
>> +
>> +     /*
>> +      * 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(attr->attr);
>> +     ret = do_write(fd, &sz, sizeof(sz));
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     list_for_each_entry(attr, &evlist->entries, node) {
>> +
>> +             ret = do_write(fd, &attr->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 = attr->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, attr->name);
>> +             if (ret < 0)
>> +                     return ret;
>> +             /*
>> +              * write unique ids for this event
>> +              */
>> +             ret = do_write(fd, attr->id, attr->ids * sizeof(u64));
>> +             if (ret < 0)
>> +                     return ret;
>> +     }
>> +     return 0;
>> +}
>> +
>> +static int write_cmdline(int fd, struct perf_header *h __used,
>> +                      struct perf_evlist *evlist __used)
>> +{
>> +     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;
>> +}
>> +
>> +static const char *topo_fmt[] = {
>> +     "/sys/devices/system/cpu/cpu%d/topology/core_siblings_list",
>> +     "/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list",
>> +     NULL
>> +};
>> +
>> +static int write_topo_cpu(int fd, int cpu)
>> +{
>> +     FILE *fp;
>> +     char filename[MAXPATHLEN];
>> +     char buf[MAXPATHLEN], *p, *c;
>> +     const char **q = topo_fmt;
>> +     int ret;
>> +
>> +     while (*q) {
>> +
>> +             sprintf(filename, *q, cpu);
>> +
>> +             fp = fopen(filename, "r");
>> +             if (!fp)
>> +                     return -1;
>> +
>> +             c = fgets(buf, sizeof(buf), fp);
>> +             if (!c) {
>> +                     fclose(fp);
>> +                     return -1;
>> +             }
>> +
>> +             p = strchr(buf, '\n');
>> +             if (p)
>> +                     *p = '\0';
>> +
>> +             fclose(fp);
>> +
>> +             ret = do_write_string(fd, c);
>> +             if (ret < 0)
>> +                     return ret;
>> +             q++;
>> +     }
>> +     return 0;
>> +}
>> +
>> +static int write_cpu_topology(int fd, struct perf_header *h __used,
>> +                       struct perf_evlist *evlist __used)
>> +{
>> +     u32 nr, i;
>> +     long ncpus;
>> +     int ret;
>> +
>> +     ncpus = sysconf(_SC_NPROCESSORS_CONF);
>> +     if (ncpus < 0)
>> +             return -1;
>> +
>> +     /* hopefully, this will keep compilers happy about the cast */
>> +     nr = (u32)(ncpus & UINT_MAX);
>> +
>> +     ret = do_write(fd, &nr, sizeof(nr));
>> +     if (ret < 0)
>> +             return -1;
>> +
>> +     for (i = 0; i < nr; i++) {
>> +             ret = do_write(fd, &i, sizeof(i));
>> +             if (ret < 0)
>> +                     return ret;
>> +
>> +             ret = write_topo_cpu(fd, i);
>> +             if (ret < 0)
>> +                     return ret;
>> +     }
>> +     return 0;
>> +}
>> +
>> +static int write_total_mem(int fd, struct perf_header *h __used,
>> +                       struct perf_evlist *evlist __used)
>> +{
>> +     char buf[MAXPATHLEN], *c;
>> +     FILE *fp;
>> +     int ret = -1, n;
>> +     uint64_t mem;
>> +
>> +     fp = fopen("/proc/meminfo", "r");
>> +     if (!fp)
>> +             return -1;
>> +
>> +     while ((c = fgets(buf, sizeof(buf), fp))) {
>> +             c = strstr(buf, "MemTotal:");
>> +             if (c)
>> +                     break;
>> +     }
>> +     fclose(fp);
>> +
>> +     n = sscanf(buf, "%*s %"PRIu64, &mem);
>> +     if (n == 1)
>> +             ret = do_write(fd, &mem, sizeof(mem));
>> +
>> +     return ret;
>> +}
>> +
>> +static int write_topo_node(int fd, int node)
>> +{
>> +     char filename[MAXPATHLEN];
>> +     char buf[MAXPATHLEN], *p, *c;
>> +     FILE *fp;
>> +     uint64_t mem;
>> +     int ret;
>> +
>> +     sprintf(filename, "/sys/devices/system/node/node%d/meminfo", node);
>> +     fp = fopen(filename, "r");
>> +     if (!fp)
>> +             return -1;
>> +
>> +     while ((c = fgets(buf, sizeof(buf), fp))) {
>> +             c = strstr(buf, "MemTotal:");
>> +             if (c)
>> +                     break;
>> +     }
>> +     fclose(fp);
>> +     if (!c)
>> +             return -1;
>> +     ret = sscanf(buf, "%*s %*d %*s %"PRIu64, &mem);
>> +     if (ret != 1)
>> +             return -1;
>> +
>> +     ret = do_write(fd, &mem, sizeof(mem));
>> +     if (ret < 0)
>> +             return -1;
>> +
>> +     sprintf(filename, "/sys/devices/system/node/node%d/cpulist", node);
>> +     fp = fopen(filename, "r");
>> +     if (!fp)
>> +             return -1;
>> +
>> +     c = fgets(buf, sizeof(buf), fp);
>> +     fclose(fp);
>> +     if (!c)
>> +             return -1;
>> +
>> +     p = strchr(buf, '\n');
>> +     if (p)
>> +             *p = '\0';
>> +
>> +     return do_write_string(fd, c);
>> +}
>> +
>> +static int write_numa_topology(int fd, struct perf_header *h __used,
>> +                       struct perf_evlist *evlist __used)
>> +{
>> +     char buf[MAXPATHLEN];
>> +     FILE *fp;
>> +     struct cpu_map *node_map;
>> +     char *c;
>> +     u32 nr, i, j;
>> +     int ret;
>> +
>> +     fp = fopen("/sys/devices/system/node/online", "r");
>> +     if (!fp)
>> +             return -1;
>> +
>> +     c = fgets(buf, sizeof(buf), fp);
>> +     fclose(fp);
>> +     if (!c)
>> +             return -1;
>> +
>> +     c = strchr(buf, '\n');
>> +     if (c)
>> +             *c = '\0';
>> +
>> +     node_map = cpu_map__new(buf);
>> +     if (!node_map)
>> +             return -1;
>> +
>> +     nr = (u32)node_map->nr;
>> +     ret = do_write(fd, &nr, sizeof(nr));
>> +     if (ret < 0)
>> +             return -1;
>> +
>> +     for (i = 0; i < nr; i++) {
>> +             j = (u32)node_map->map[i];
>> +             ret = do_write(fd, &j, sizeof(j));
>> +             if (ret < 0)
>> +                     goto error;
>> +
>> +             ret = write_topo_node(fd, i);
>> +             if (ret < 0)
>> +                     goto error;
>> +     }
>> +     ret = 0;
>> +error:
>> +     free(node_map);
>> +     return ret;
>> +}
>> +
>> +static void print_hostname(struct perf_header *ph, int fd, FILE *fp)
>> +{
>> +     char *str = do_read_string(fd, ph);
>> +     fprintf(fp, "# hostname : %s\n", str);
>> +     free(str);
>> +}
>> +
>> +static void print_osrelease(struct perf_header *ph, int fd, FILE *fp)
>> +{
>> +     char *str = do_read_string(fd, ph);
>> +     fprintf(fp, "# os release : %s\n", str);
>> +     free(str);
>> +}
>> +
>> +static void print_arch(struct perf_header *ph, int fd, FILE *fp)
>> +{
>> +     char *str = do_read_string(fd, ph);
>> +     fprintf(fp, "# arch : %s\n", str);
>> +     free(str);
>> +}
>> +
>> +static void print_cpuid(struct perf_header *ph, int fd, FILE *fp)
>> +{
>> +     char *str = do_read_string(fd, ph);
>> +     fprintf(fp, "# cpuid : %s\n", str);
>> +     free(str);
>> +}
>> +
>> +static void print_nrcpus(struct perf_header *ph, int fd, FILE *fp)
>> +{
>> +     ssize_t ret;
>> +     u32 nr;
>> +
>> +     ret = read(fd, &nr, sizeof(nr));
>> +     if (ret != (ssize_t)sizeof(nr))
>> +             nr = -1; /* interpreted as error */
>> +
>> +     if (ph->needs_swap)
>> +             nr = bswap_32(nr);
>> +
>> +     fprintf(fp, "# nrcpus online : %u\n", nr);
>> +
>> +     ret = read(fd, &nr, sizeof(nr));
>> +     if (ret != (ssize_t)sizeof(nr))
>> +             nr = -1; /* interpreted as error */
>> +
>> +     if (ph->needs_swap)
>> +             nr = bswap_32(nr);
>> +
>> +     fprintf(fp, "# nrcpus avail : %u\n", nr);
>> +}
>> +
>> +static void print_version(struct perf_header *ph, int fd, FILE *fp)
>> +{
>> +     char *str = do_read_string(fd, ph);
>> +     fprintf(fp, "# perf version : %s\n", str);
>> +     free(str);
>> +}
>> +
>> +static void print_cmdline(struct perf_header *ph, int fd, FILE *fp)
>> +{
>> +     ssize_t ret;
>> +     char *str;
>> +     u32 nr, i;
>> +
>> +     ret = read(fd, &nr, sizeof(nr));
>> +     if (ret != (ssize_t)sizeof(nr)) {
>> +             fprintf(fp, "# cmdline : unknown\n");
>> +             return;
>> +     }
>> +
>> +     if (ph->needs_swap)
>> +             nr = bswap_32(nr);
>> +
>> +     fprintf(fp, "# cmdline : ");
>> +
>> +     for (i = 0; i < nr; i++) {
>> +             str = do_read_string(fd, ph);
>> +             fprintf(fp, "%s ", str);
>> +             free(str);
>> +     }
>> +     fputc('\n', fp);
>> +}
>> +
>> +static void print_cpu_topology(struct perf_header *ph, int fd, FILE *fp)
>> +{
>> +     ssize_t ret;
>> +     u32 nr, c, i;
>> +     char *str;
>> +
>> +     ret = read(fd, &nr, sizeof(nr));
>> +     if (ret != (ssize_t)sizeof(nr)) {
>> +             fprintf(fp, "# cpu topology  : not available\n");
>> +             return;
>> +     }
>> +
>> +     if (ph->needs_swap)
>> +             nr = bswap_32(nr);
>> +
>> +     for (i = 0; i < nr; i++) {
>> +
>> +             ret = read(fd, &c, sizeof(c));
>> +             if (ret != (ssize_t)sizeof(c))
>> +                     c = -1; /* interpreted as error */
>> +
>> +             if (ph->needs_swap)
>> +                     c = bswap_32(c);
>> +
>> +             str = do_read_string(fd, ph);
>> +             fprintf(fp, "# CPU%u sibling cores  : %s\n", c, str);
>> +             free(str);
>> +
>> +             str = do_read_string(fd, ph);
>> +             fprintf(fp, "# CPU%u sibling threads: %s\n", c, str);
>> +             free(str);
>> +     }
>> +}
>> +
>> +static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
>> +{
>> +     struct perf_event_attr attr;
>> +     uint64_t id;
>> +     void *buf = NULL;
>> +     char *str;
>> +     u32 nre, sz, nr, i, j, msz;
>> +     int ret;
>> +
>> +     /* number of events */
>> +     ret = read(fd, &nre, sizeof(nre));
>> +     if (ret != (ssize_t)sizeof(nre))
>> +             goto error;
>> +
>> +     if (ph->needs_swap)
>> +             nre = bswap_32(nre);
>> +
>> +     ret = read(fd, &sz, sizeof(sz));
>> +     if (ret != (ssize_t)sizeof(sz))
>> +             goto error;
>> +
>> +     if (ph->needs_swap)
>> +             sz = bswap_32(sz);
>> +
>> +     /*
>> +      * ensure it is at least to our ABI rev
>> +      */
>> +     if (sz < (u32)sizeof(attr))
>> +             goto error;
>> +
>> +     memset(&attr, 0, sizeof(attr));
>> +
>> +     /* read entire region to sync up to next field */
>> +     buf = malloc(sz);
>> +     if (!buf)
>> +             goto error;
>> +
>> +     msz = sizeof(attr);
>> +     if (sz < msz)
>> +             msz = sz;
>> +
>> +     for (i = 0 ; i < nre; i++) {
>> +
>> +             ret = read(fd, buf, sz);
>> +             if (ret != (ssize_t)sz)
>> +                     goto error;
>> +
>> +             if (ph->needs_swap)
>> +                     perf_event__attr_swap(buf);
>> +
>> +             memcpy(&attr, buf, msz);
>> +
>> +             ret = read(fd, &nr, sizeof(nr));
>> +             if (ret != (ssize_t)sizeof(nr))
>> +                     goto error;
>> +
>> +             if (ph->needs_swap)
>> +                     nr = bswap_32(nr);
>> +
>> +             str = do_read_string(fd, ph);
>> +             fprintf(fp, "# event = %s, ", str);
>> +             free(str);
>> +
>> +             fprintf(fp, "type = %d, config = 0x%"PRIx64
>> +                         ", config1 = 0x%"PRIx64", config2 = 0x%"PRIx64,
>> +                             attr.type,
>> +                             (u64)attr.config,
>> +                             (u64)attr.config1,
>> +                             (u64)attr.config2);
>> +
>> +             fprintf(fp, ", excl_usr = %d, excl_kern = %d",
>> +                             attr.exclude_user,
>> +                             attr.exclude_kernel);
>> +
>> +             if (nr)
>> +                     fprintf(fp, ", id = {");
>> +
>> +             for (j = 0 ; j < nr; j++) {
>> +                     ret = read(fd, &id, sizeof(id));
>> +                     if (ret != (ssize_t)sizeof(id))
>> +                             goto error;
>> +
>> +                     if (ph->needs_swap)
>> +                             id = bswap_64(id);
>> +
>> +                     if (j)
>> +                             fputc(',', fp);
>> +
>> +                     fprintf(fp, " %"PRIu64, id);
>> +             }
>> +             if (nr && j == nr)
>> +                     fprintf(fp, " }");
>> +             fputc('\n', fp);
>> +     }
>> +     free(buf);
>> +     return;
>> +error:
>> +     fprintf(fp, "# event desc: not available or unable to read\n");
>> +}
>> +
>> +static void print_total_mem(struct perf_header *h __used, int fd, FILE *fp)
>> +{
>> +     uint64_t mem;
>> +     ssize_t ret;
>> +
>> +     ret = read(fd, &mem, sizeof(mem));
>> +     if (ret != sizeof(mem))
>> +             goto error;
>> +
>> +     if (h->needs_swap)
>> +             mem = bswap_64(mem);
>> +
>> +     fprintf(fp, "# total memory: %"PRIu64" kB\n", mem);
>> +     return;
>> +error:
>> +     fprintf(fp, "# total memory: unknown\n");
>> +}
>> +
>> +static void print_numa_topology(struct perf_header *h __used, int fd, FILE *fp)
>> +{
>> +     ssize_t ret;
>> +     u32 nr, c, i;
>> +     char *str;
>> +     uint64_t mem;
>> +
>> +     /* nr nodes */
>> +     ret = read(fd, &nr, sizeof(nr));
>> +     if (ret != (ssize_t)sizeof(nr))
>> +             goto error;
>> +
>> +     if (h->needs_swap)
>> +             nr = bswap_32(nr);
>> +
>> +     for (i = 0; i < nr; i++) {
>> +
>> +             /* node number */
>> +             ret = read(fd, &c, sizeof(c));
>> +             if (ret != (ssize_t)sizeof(c))
>> +                     goto error;
>> +
>> +             if (h->needs_swap)
>> +                     c = bswap_32(c);
>> +
>> +             ret = read(fd, &mem, sizeof(mem));
>> +             if (ret != sizeof(mem))
>> +                     goto error;
>> +
>> +             if (h->needs_swap)
>> +                     mem = bswap_64(mem);
>> +
>> +             fprintf(fp, "# node%u meminfo : %"PRIu64" kB\n", c, mem);
>> +
>> +             str = do_read_string(fd, h);
>> +             fprintf(fp, "# node%u cpu list: %s\n", c, str);
>> +             free(str);
>> +     }
>> +     return;
>> +error:
>> +     fprintf(fp, "# numa topology : not available\n");
>> +}
>> +
>> +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);
>> +};
>> +
>> +#define FEAT_OP(n, w, p) [n] = { .write = w, .print = p }
>> +
>> +static struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
>> +     FEAT_OP(HEADER_TRACE_INFO, write_trace_info, NULL),
>> +     FEAT_OP(HEADER_BUILD_ID, write_build_id, NULL),
>> +     FEAT_OP(HEADER_HOSTNAME, write_hostname, print_hostname),
>> +     FEAT_OP(HEADER_OSRELEASE, write_osrelease, print_osrelease),
>> +     FEAT_OP(HEADER_ARCH, write_arch, print_arch),
>> +     FEAT_OP(HEADER_CPUID, write_cpuid, print_cpuid),
>> +     FEAT_OP(HEADER_NRCPUS, write_nrcpus, print_nrcpus),
>> +     FEAT_OP(HEADER_EVENT_DESC, write_event_desc, print_event_desc),
>> +     FEAT_OP(HEADER_CMDLINE, write_cmdline, print_cmdline),
>> +     FEAT_OP(HEADER_VERSION, write_version, print_version),
>> +     FEAT_OP(HEADER_CPU_TOPOLOGY, write_cpu_topology, print_cpu_topology),
>> +     FEAT_OP(HEADER_TOTAL_MEM, write_total_mem, print_total_mem),
>> +     FEAT_OP(HEADER_NUMA_TOPOLOGY, write_numa_topology, print_numa_topology),
>> +};
>> +
>> +static int perf_header_fprintf_info(struct perf_file_section *section,
>> +                                 struct perf_header *ph,
>> +                                 int feat, int fd, void *data)
>> +{
>> +     FILE *fp = data;
>> +
>> +     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_TRACE_INFO || feat >= HEADER_LAST_FEATURE) {
>> +             pr_warning("unknown feature %d\n", feat);
>> +             return -1;
>> +     }
>> +
>> +     if (feat_ops[feat].print)
>> +             feat_ops[feat].print(ph, fd, fp);
>> +
>> +     return 0;
>> +}
>> +
>> +int perf_header__fprintf_info(struct perf_session *session, FILE *fp __used)
>> +{
>> +     int fd = session->fd;
>> +     struct perf_header *header = &session->header;
>> +     perf_header__process_sections(header, fd, fp, perf_header_fprintf_info);
>> +     return 0;
>> +}
>> +
>>  #define dsos__for_each_with_build_id(pos, head)      \
>>       list_for_each_entry(pos, head, node)    \
>>               if (!pos->has_build_id)         \
>> @@ -356,15 +1201,41 @@ static bool perf_session__read_build_ids(struct perf_session *session, bool with
>>       return ret;
>>  }
>>
>> +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_header__has_feat(h, type)) {
>> +
>> +             (*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;
>> +}
>> +
>>  static int perf_header__adds_write(struct perf_header *header,
>>                                  struct perf_evlist *evlist, int fd)
>>  {
>>       int nr_sections;
>>       struct perf_session *session;
>> -     struct perf_file_section *feat_sec;
>> +     struct perf_file_section *feat_sec, *p;
>>       int sec_size;
>>       u64 sec_start;
>> -     int idx = 0, err;
>> +     int err;
>>
>>       session = container_of(header, struct perf_session, header);
>>
>> @@ -376,7 +1247,7 @@ static int perf_header__adds_write(struct perf_header *header,
>>       if (!nr_sections)
>>               return 0;
>>
>> -     feat_sec = calloc(sizeof(*feat_sec), nr_sections);
>> +     feat_sec = p = calloc(sizeof(*feat_sec), nr_sections);
>>       if (feat_sec == NULL)
>>               return -ENOMEM;
>>
>> @@ -385,36 +1256,65 @@ static int perf_header__adds_write(struct perf_header *header,
>>       sec_start = header->data_offset + header->data_size;
>>       lseek(fd, sec_start + sec_size, SEEK_SET);
>>
>> -     if (perf_header__has_feat(header, HEADER_TRACE_INFO)) {
>> -             struct perf_file_section *trace_sec;
>> -
>> -             trace_sec = &feat_sec[idx++];
>> +     err = do_write_feat(fd, header, HEADER_TRACE_INFO, &p, evlist);
>> +     if (err)
>> +             goto out_free;
>>
>> -             /* Write trace info */
>> -             trace_sec->offset = lseek(fd, 0, SEEK_CUR);
>> -             read_tracing_data(fd, &evlist->entries);
>> -             trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset;
>> +     err = do_write_feat(fd, header, HEADER_BUILD_ID, &p, evlist);
>> +     if (err) {
>> +             perf_header__clear_feat(header, HEADER_BUILD_ID);
>> +             goto out_free;
>>       }
>>
>> -     if (perf_header__has_feat(header, HEADER_BUILD_ID)) {
>> -             struct perf_file_section *buildid_sec;
>> +     err = do_write_feat(fd, header, HEADER_HOSTNAME, &p, evlist);
>> +     if (err)
>> +             perf_header__clear_feat(header, HEADER_HOSTNAME);
>>
>> -             buildid_sec = &feat_sec[idx++];
>> +     err = do_write_feat(fd, header, HEADER_OSRELEASE, &p, evlist);
>> +     if (err)
>> +             perf_header__clear_feat(header, HEADER_OSRELEASE);
>>
>> -             /* Write build-ids */
>> -             buildid_sec->offset = lseek(fd, 0, SEEK_CUR);
>> -             err = dsos__write_buildid_table(header, 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;
>> -             if (!no_buildid_cache)
>> -                     perf_session__cache_build_ids(session);
>> -     }
>> +     err = do_write_feat(fd, header, HEADER_ARCH, &p, evlist);
>> +     if (err)
>> +             perf_header__clear_feat(header, HEADER_ARCH);
>> +
>> +     err = do_write_feat(fd, header, HEADER_CPUID, &p, evlist);
>> +     if (err)
>> +             perf_header__clear_feat(header, HEADER_CPUID);
>> +
>> +     err = do_write_feat(fd, header, HEADER_NRCPUS, &p, evlist);
>> +     if (err)
>> +             perf_header__clear_feat(header, HEADER_NRCPUS);
>> +
>> +     err = do_write_feat(fd, header, HEADER_EVENT_DESC, &p, evlist);
>> +     if (err)
>> +             perf_header__clear_feat(header, HEADER_EVENT_DESC);
>> +
>> +     err = do_write_feat(fd, header, HEADER_CMDLINE, &p, evlist);
>> +     if (err)
>> +             perf_header__clear_feat(header, HEADER_CMDLINE);
>> +
>> +     err = do_write_feat(fd, header, HEADER_VERSION, &p, evlist);
>> +     if (err)
>> +             perf_header__clear_feat(header, HEADER_VERSION);
>> +
>> +     err = do_write_feat(fd, header, HEADER_CPU_TOPOLOGY, &p, evlist);
>> +     if (err)
>> +             perf_header__clear_feat(header, HEADER_CPU_TOPOLOGY);
>> +
>> +     err = do_write_feat(fd, header, HEADER_TOTAL_MEM, &p, evlist);
>> +     if (err)
>> +             perf_header__clear_feat(header, HEADER_TOTAL_MEM);
>> +
>> +     err = do_write_feat(fd, header, HEADER_NUMA_TOPOLOGY, &p, evlist);
>> +     if (err)
>> +             perf_header__clear_feat(header, HEADER_NUMA_TOPOLOGY);
>>
>>       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");
>> @@ -554,9 +1454,10 @@ static int perf_header__getbuffer64(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))
>> +                               struct perf_header *ph,
>> +                               int feat, int fd, void *data))
>>  {
>>       struct perf_file_section *feat_sec;
>>       int nr_sections;
>> @@ -584,7 +1485,7 @@ int perf_header__process_sections(struct perf_header *header, int fd,
>>               if (perf_header__has_feat(header, feat)) {
>>                       struct perf_file_section *sec = &feat_sec[idx++];
>>
>> -                     err = process(sec, header, feat, fd);
>> +                     err = process(sec, header, feat, fd, data);
>>                       if (err < 0)
>>                               break;
>>               }
>> @@ -796,7 +1697,7 @@ out:
>>
>>  static int perf_file_section__process(struct perf_file_section *section,
>>                                     struct perf_header *ph,
>> -                                   int feat, int fd)
>> +                                   int feat, int fd, void *data __used)
>>  {
>>       if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) {
>>               pr_debug("Failed to lseek to %" PRIu64 " offset for feature "
>> @@ -935,7 +1836,8 @@ int perf_session__read_header(struct perf_session *session, int fd)
>>               event_count =  f_header.event_types.size / sizeof(struct perf_trace_event_type);
>>       }
>>
>> -     perf_header__process_sections(header, fd, perf_file_section__process);
>> +     perf_header__process_sections(header, fd, NULL,
>> +                                   perf_file_section__process);
>>
>>       lseek(fd, header->data_offset, SEEK_SET);
>>
>> diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
>> index 1886256..455480b 100644
>> --- a/tools/perf/util/header.h
>> +++ b/tools/perf/util/header.h
>> @@ -12,6 +12,19 @@
>>  enum {
>>       HEADER_TRACE_INFO = 1,
>>       HEADER_BUILD_ID,
>> +
>> +     HEADER_HOSTNAME,
>> +     HEADER_OSRELEASE,
>> +     HEADER_ARCH,
>> +     HEADER_CPUID,
>> +     HEADER_NRCPUS,
>> +     HEADER_EVENT_DESC,
>> +     HEADER_CMDLINE,
>> +     HEADER_VERSION,
>> +     HEADER_CPU_TOPOLOGY,
>> +     HEADER_TOTAL_MEM,
>> +     HEADER_NUMA_TOPOLOGY,
>> +
>>       HEADER_LAST_FEATURE,
>>  };
>>
>> @@ -68,10 +81,15 @@ void perf_header__set_feat(struct perf_header *header, int feat);
>>  void perf_header__clear_feat(struct perf_header *header, int feat);
>>  bool perf_header__has_feat(const struct perf_header *header, int feat);
>>
>> +int perf_header__set_cmdline(int argc, const char **argv);
>> +
>>  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));
>> +                               struct perf_header *ph,
>> +                               int feat, int fd, void *data));
>> +
>> +int perf_header__fprintf_info(struct perf_session *session, FILE *fp);
>>
>>  int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
>>                         const char *name, bool is_kallsyms);
>> diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
>> index 72458d9..54613a2 100644
>> --- a/tools/perf/util/session.c
>> +++ b/tools/perf/util/session.c
>> @@ -1326,3 +1326,21 @@ int perf_session__cpu_bitmap(struct perf_session *session,
>>
>>       return 0;
>>  }
>> +
>> +void perf_session__fprintf_info(struct perf_session *session, FILE *fp)
>> +{
>> +     struct stat st;
>> +     int ret;
>> +
>> +     if (session == NULL || fp == NULL)
>> +             return;
>> +
>> +     ret = fstat(session->fd, &st);
>> +     if (ret == -1)
>> +             return;
>> +
>> +     fprintf(fp, "#\n# captured on: %s", ctime(&st.st_ctime));
>> +     ret = perf_header__fprintf_info(session, fp);
>> +     if (ret == 0)
>> +             fprintf(fp, "#\n");
>> +}
>> diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
>> index 170601e..33a3a46 100644
>> --- a/tools/perf/util/session.h
>> +++ b/tools/perf/util/session.h
>> @@ -176,4 +176,5 @@ void perf_session__print_ip(union perf_event *event,
>>  int perf_session__cpu_bitmap(struct perf_session *session,
>>                            const char *cpu_list, unsigned long *cpu_bitmap);
>>
>> +void perf_session__fprintf_info(struct perf_session *session, FILE *fp);
>>  #endif /* __PERF_SESSION_H */
>
> Reviewed-by: David Ahern <dsahern@gmail.com>
> Tested-by: David Ahern <dsahern@gmail.com>
>
> The problem with endianness handling I mentioned last week is related to
> the assumption that the HEADER_BUILD_ID is set. In my test case it is
> not (-B flag on perf-record) and that causes the aborts. A problem for
> another day which I can address it when I submit the patch to handle
> swapping for the feature mask.
>
Ok, now I understand your point better.

The thing is that the endianess detection done by perf is currently broken.
It assumes that if the size of perf_event_attr saved in the file (attr_size)
does not match sizeof(perf_event_attr) then it must be because of endianess
issues. The reality is that it they can be different because the the ABI
has been extended (and I am sure it will), and this a perf tool may
have a larger struct perf_event_attr, than an old perf.data file, yet it
should be able to read it. We need to fix this issue now and not once
we hit the problem with extended ABI. I have a couple of ideas on this
and they revolve around using the magic prefix as the indicator of endianess
(and also detect older perf.data files).

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] perf: make perf.data more self-descriptive (v4)
  2011-09-12 13:54   ` Stephane Eranian
@ 2011-09-12 14:33     ` David Ahern
  2011-09-12 14:40       ` Stephane Eranian
  0 siblings, 1 reply; 21+ messages in thread
From: David Ahern @ 2011-09-12 14:33 UTC (permalink / raw)
  To: Stephane Eranian; +Cc: linux-kernel, acme, peterz, mingo


On 09/12/2011 07:54 AM, Stephane Eranian wrote:
>> The problem with endianness handling I mentioned last week is related to
>> the assumption that the HEADER_BUILD_ID is set. In my test case it is
>> not (-B flag on perf-record) and that causes the aborts. A problem for
>> another day which I can address it when I submit the patch to handle
>> swapping for the feature mask.
>>
> Ok, now I understand your point better.
> 
> The thing is that the endianess detection done by perf is currently broken.
> It assumes that if the size of perf_event_attr saved in the file (attr_size)
> does not match sizeof(perf_event_attr) then it must be because of endianess
> issues. The reality is that it they can be different because the the ABI
> has been extended (and I am sure it will), and this a perf tool may
> have a larger struct perf_event_attr, than an old perf.data file, yet it
> should be able to read it. We need to fix this issue now and not once
> we hit the problem with extended ABI. I have a couple of ideas on this
> and they revolve around using the magic prefix as the indicator of endianess
> (and also detect older perf.data files).

There is some handling of older perf data files. For example, data files
without the adds_features mask are handled -- see
perf_file_header__read(), if (header->size != sizeof(*header)) {}

The swapping of the adds_features mask is a mess because the mask was
declared as an array of unsigned longs (util/include/linux/types.h).

I do have it working now -- analyzing 32-bit PPC files on 32-bit and
64-bit x86 hosts.

I have a patch to take a best guess on the feature mask. Your patchset
actually helps there since the new features are on by default with no
off switch. I guess to make it more robust I should add an endian
feature mask and require it to be set rather than depending implicitly
on the existence of other flags. Once Arnaldo picks up this patch I'll
send out one for endianness handling of the features mask.

David

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] perf: make perf.data more self-descriptive (v4)
  2011-09-12 14:33     ` David Ahern
@ 2011-09-12 14:40       ` Stephane Eranian
  2011-09-12 14:43         ` Peter Zijlstra
  0 siblings, 1 reply; 21+ messages in thread
From: Stephane Eranian @ 2011-09-12 14:40 UTC (permalink / raw)
  To: David Ahern; +Cc: linux-kernel, acme, peterz, mingo

On Mon, Sep 12, 2011 at 4:33 PM, David Ahern <dsahern@gmail.com> wrote:
>
> On 09/12/2011 07:54 AM, Stephane Eranian wrote:
>>> The problem with endianness handling I mentioned last week is related to
>>> the assumption that the HEADER_BUILD_ID is set. In my test case it is
>>> not (-B flag on perf-record) and that causes the aborts. A problem for
>>> another day which I can address it when I submit the patch to handle
>>> swapping for the feature mask.
>>>
>> Ok, now I understand your point better.
>>
>> The thing is that the endianess detection done by perf is currently broken.
>> It assumes that if the size of perf_event_attr saved in the file (attr_size)
>> does not match sizeof(perf_event_attr) then it must be because of endianess
>> issues. The reality is that it they can be different because the the ABI
>> has been extended (and I am sure it will), and this a perf tool may
>> have a larger struct perf_event_attr, than an old perf.data file, yet it
>> should be able to read it. We need to fix this issue now and not once
>> we hit the problem with extended ABI. I have a couple of ideas on this
>> and they revolve around using the magic prefix as the indicator of endianess
>> (and also detect older perf.data files).
>
> There is some handling of older perf data files. For example, data files
> without the adds_features mask are handled -- see
> perf_file_header__read(), if (header->size != sizeof(*header)) {}
>
Yes, that's quite possible. I am worried of attr_size vs.
sizeof(perf_event_attr).
I don't think this should be the test to detect endianess.


> The swapping of the adds_features mask is a mess because the mask was
> declared as an array of unsigned longs (util/include/linux/types.h).
>
> I do have it working now -- analyzing 32-bit PPC files on 32-bit and
> 64-bit x86 hosts.
>
> I have a patch to take a best guess on the feature mask. Your patchset
> actually helps there since the new features are on by default with no
> off switch. I guess to make it more robust I should add an endian
> feature mask and require it to be set rather than depending implicitly
> on the existence of other flags. Once Arnaldo picks up this patch I'll
> send out one for endianness handling of the features mask.
>
okay.

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] perf: make perf.data more self-descriptive (v4)
  2011-09-12 14:40       ` Stephane Eranian
@ 2011-09-12 14:43         ` Peter Zijlstra
  2011-09-12 14:47           ` Stephane Eranian
  2011-09-16 14:35           ` Stephane Eranian
  0 siblings, 2 replies; 21+ messages in thread
From: Peter Zijlstra @ 2011-09-12 14:43 UTC (permalink / raw)
  To: Stephane Eranian; +Cc: David Ahern, linux-kernel, acme, mingo

On Mon, 2011-09-12 at 16:40 +0200, Stephane Eranian wrote:
> I don't think this should be the test to detect endianess.

You should be able to tell the endianness from the PERF_MAGIC string, it
stores the string as a u64, so depending on endianness it reads back as
either: PERFFILE or ELIFFREP or whatever the bswap64 result is.

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] perf: make perf.data more self-descriptive (v4)
  2011-09-12 14:43         ` Peter Zijlstra
@ 2011-09-12 14:47           ` Stephane Eranian
  2011-09-13 14:40             ` Arnaldo Carvalho de Melo
  2011-09-16 14:35           ` Stephane Eranian
  1 sibling, 1 reply; 21+ messages in thread
From: Stephane Eranian @ 2011-09-12 14:47 UTC (permalink / raw)
  To: Peter Zijlstra; +Cc: David Ahern, linux-kernel, acme, mingo

On Mon, Sep 12, 2011 at 4:43 PM, Peter Zijlstra <peterz@infradead.org> wrote:
> On Mon, 2011-09-12 at 16:40 +0200, Stephane Eranian wrote:
>> I don't think this should be the test to detect endianess.
>
> You should be able to tell the endianness from the PERF_MAGIC string, it
> stores the string as a u64, so depending on endianness it reads back as
> either: PERFFILE or ELIFFREP or whatever the bswap64 result is.
>
right, something like that...

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] perf: make perf.data more self-descriptive (v4)
  2011-09-12 14:47           ` Stephane Eranian
@ 2011-09-13 14:40             ` Arnaldo Carvalho de Melo
  0 siblings, 0 replies; 21+ messages in thread
From: Arnaldo Carvalho de Melo @ 2011-09-13 14:40 UTC (permalink / raw)
  To: Stephane Eranian; +Cc: Peter Zijlstra, David Ahern, linux-kernel, mingo

Em Mon, Sep 12, 2011 at 04:47:21PM +0200, Stephane Eranian escreveu:
> On Mon, Sep 12, 2011 at 4:43 PM, Peter Zijlstra <peterz@infradead.org> wrote:
> > On Mon, 2011-09-12 at 16:40 +0200, Stephane Eranian wrote:
> >> I don't think this should be the test to detect endianess.
> >
> > You should be able to tell the endianness from the PERF_MAGIC string, it
> > stores the string as a u64, so depending on endianness it reads back as
> > either: PERFFILE or ELIFFREP or whatever the bswap64 result is.
> >
> right, something like that...

Yeah, right, reading this thread I could swear it was done like that :-\

Just set needs_swap based on the magic number in perf_file_header__read().

- Arnaldo

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] perf: make perf.data more self-descriptive (v4)
  2011-09-12 14:43         ` Peter Zijlstra
  2011-09-12 14:47           ` Stephane Eranian
@ 2011-09-16 14:35           ` Stephane Eranian
  2011-09-16 14:37             ` Peter Zijlstra
  2011-09-16 14:50             ` David Ahern
  1 sibling, 2 replies; 21+ messages in thread
From: Stephane Eranian @ 2011-09-16 14:35 UTC (permalink / raw)
  To: Peter Zijlstra; +Cc: David Ahern, linux-kernel, acme, mingo

On Mon, Sep 12, 2011 at 4:43 PM, Peter Zijlstra <peterz@infradead.org> wrote:
> On Mon, 2011-09-12 at 16:40 +0200, Stephane Eranian wrote:
>> I don't think this should be the test to detect endianess.
>
> You should be able to tell the endianness from the PERF_MAGIC string, it
> stores the string as a u64, so depending on endianness it reads back as
> either: PERFFILE or ELIFFREP or whatever the bswap64 result is.
>

I believe in big endian, if you do od -c perf.data | head -1, you also see:

0000000   P   E   R   F   F   I   L   E   h  \0  \0  \0  \0  \0  \0  \0

static const char *__perf_magic = "PERFFILE";
#define PERF_MAGIC      (*(u64 *)__perf_magic)

u64 hm = PERF_MAGIC;

The layout in memory is the same for both little-endian and
big-endian. Thus the layout on the file is the same.

When you look at the memory as u64, then things are different:
In little-endian, hm=0x454c494646524550
in big-endian,   hm=0x5045524646494c45

In big-endian, the MSB 0x50 ('P') ends up at the lowest memory address.
In little-endian, the LSB 0x50 ('P') ends up at the lowest memory address.

Thus, I suspect we need to write in the file a different MAGIC for big vs.
little endian.

David, can you confirm this?

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] perf: make perf.data more self-descriptive (v4)
  2011-09-16 14:35           ` Stephane Eranian
@ 2011-09-16 14:37             ` Peter Zijlstra
  2011-09-16 14:44               ` Stephane Eranian
  2011-09-16 14:50             ` David Ahern
  1 sibling, 1 reply; 21+ messages in thread
From: Peter Zijlstra @ 2011-09-16 14:37 UTC (permalink / raw)
  To: Stephane Eranian; +Cc: David Ahern, linux-kernel, acme, mingo

On Fri, 2011-09-16 at 16:35 +0200, Stephane Eranian wrote:
> On Mon, Sep 12, 2011 at 4:43 PM, Peter Zijlstra <peterz@infradead.org> wrote:
> > On Mon, 2011-09-12 at 16:40 +0200, Stephane Eranian wrote:
> >> I don't think this should be the test to detect endianess.
> >
> > You should be able to tell the endianness from the PERF_MAGIC string, it
> > stores the string as a u64, so depending on endianness it reads back as
> > either: PERFFILE or ELIFFREP or whatever the bswap64 result is.
> >
> 
> I believe in big endian, if you do od -c perf.data | head -1, you also see:
> 
> 0000000   P   E   R   F   F   I   L   E   h  \0  \0  \0  \0  \0  \0  \0

Sure, but all you need to know is if the file you're reading is a
different endian than your box, if it is swap stuff, if its not, you're
good.



^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] perf: make perf.data more self-descriptive (v4)
  2011-09-16 14:37             ` Peter Zijlstra
@ 2011-09-16 14:44               ` Stephane Eranian
  2011-09-16 14:52                 ` David Ahern
  0 siblings, 1 reply; 21+ messages in thread
From: Stephane Eranian @ 2011-09-16 14:44 UTC (permalink / raw)
  To: Peter Zijlstra; +Cc: David Ahern, linux-kernel, acme, mingo

On Fri, Sep 16, 2011 at 4:37 PM, Peter Zijlstra <peterz@infradead.org> wrote:
> On Fri, 2011-09-16 at 16:35 +0200, Stephane Eranian wrote:
>> On Mon, Sep 12, 2011 at 4:43 PM, Peter Zijlstra <peterz@infradead.org> wrote:
>> > On Mon, 2011-09-12 at 16:40 +0200, Stephane Eranian wrote:
>> >> I don't think this should be the test to detect endianess.
>> >
>> > You should be able to tell the endianness from the PERF_MAGIC string, it
>> > stores the string as a u64, so depending on endianness it reads back as
>> > either: PERFFILE or ELIFFREP or whatever the bswap64 result is.
>> >
>>
>> I believe in big endian, if you do od -c perf.data | head -1, you also see:
>>
>> 0000000   P   E   R   F   F   I   L   E   h  \0  \0  \0  \0  \0  \0  \0
>
> Sure, but all you need to know is if the file you're reading is a
> different endian than your box, if it is swap stuff, if its not, you're
> good.

We agree on that. I am just saying the MAGIC string as it is today
may not be enough to tell us that.

>
>
>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] perf: make perf.data more self-descriptive (v4)
  2011-09-16 14:35           ` Stephane Eranian
  2011-09-16 14:37             ` Peter Zijlstra
@ 2011-09-16 14:50             ` David Ahern
  2011-09-16 15:19               ` Peter Zijlstra
  1 sibling, 1 reply; 21+ messages in thread
From: David Ahern @ 2011-09-16 14:50 UTC (permalink / raw)
  To: Stephane Eranian; +Cc: Peter Zijlstra, linux-kernel, acme, mingo

On 09/16/2011 08:35 AM, Stephane Eranian wrote:
> On Mon, Sep 12, 2011 at 4:43 PM, Peter Zijlstra <peterz@infradead.org> wrote:
>> On Mon, 2011-09-12 at 16:40 +0200, Stephane Eranian wrote:
>>> I don't think this should be the test to detect endianess.
>>
>> You should be able to tell the endianness from the PERF_MAGIC string, it
>> stores the string as a u64, so depending on endianness it reads back as
>> either: PERFFILE or ELIFFREP or whatever the bswap64 result is.
>>
> 
> I believe in big endian, if you do od -c perf.data | head -1, you also see:
> 
> 0000000   P   E   R   F   F   I   L   E   h  \0  \0  \0  \0  \0  \0  \0
> 
> static const char *__perf_magic = "PERFFILE";
> #define PERF_MAGIC      (*(u64 *)__perf_magic)
> 
> u64 hm = PERF_MAGIC;
> 
> The layout in memory is the same for both little-endian and
> big-endian. Thus the layout on the file is the same.
> 
> When you look at the memory as u64, then things are different:
> In little-endian, hm=0x454c494646524550
> in big-endian,   hm=0x5045524646494c45
> 
> In big-endian, the MSB 0x50 ('P') ends up at the lowest memory address.
> In little-endian, the LSB 0x50 ('P') ends up at the lowest memory address.
> 
> Thus, I suspect we need to write in the file a different MAGIC for big vs.
> little endian.
> 
> David, can you confirm this?

on x86:
$  od -c perf.data | head -1
0000000   P   E   R   F   F   I   L   E   h  \0  \0  \0  \0  \0  \0  \0

on PPC:
# od -c perf.data | head -1
0000000   P   E   R   F   F   I   L   E  \0  \0  \0  \0  \0  \0  \0   h

And then PPC file on x86:
$ od -c perf-ppc.data | head -1
0000000   P   E   R   F   F   I   L   E  \0  \0  \0  \0  \0  \0  \0   h

So it is the same and that explains why the current code is:

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

versus a u64 comparison. Printing magic as a PRIx64 running the command
on x86:

ppc data file: header->magic 454c494646524550
x86 data file: header->magic 454c494646524550

Which is expected given the od -c output above.

David

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] perf: make perf.data more self-descriptive (v4)
  2011-09-16 14:44               ` Stephane Eranian
@ 2011-09-16 14:52                 ` David Ahern
  2011-09-16 14:56                   ` Stephane Eranian
  0 siblings, 1 reply; 21+ messages in thread
From: David Ahern @ 2011-09-16 14:52 UTC (permalink / raw)
  To: Stephane Eranian; +Cc: Peter Zijlstra, linux-kernel, acme, mingo


On 09/16/2011 08:44 AM, Stephane Eranian wrote:
>> Sure, but all you need to know is if the file you're reading is a
>> different endian than your box, if it is swap stuff, if its not, you're
>> good.
> 
> We agree on that. I am just saying the MAGIC string as it is today
> may not be enough to tell us that.

Right. I think that's why the attribute struct is used to detect
endianness and not the magic.

David

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] perf: make perf.data more self-descriptive (v4)
  2011-09-16 14:52                 ` David Ahern
@ 2011-09-16 14:56                   ` Stephane Eranian
  0 siblings, 0 replies; 21+ messages in thread
From: Stephane Eranian @ 2011-09-16 14:56 UTC (permalink / raw)
  To: David Ahern; +Cc: Peter Zijlstra, linux-kernel, acme, mingo

On Fri, Sep 16, 2011 at 4:52 PM, David Ahern <dsahern@gmail.com> wrote:
>
> On 09/16/2011 08:44 AM, Stephane Eranian wrote:
>>> Sure, but all you need to know is if the file you're reading is a
>>> different endian than your box, if it is swap stuff, if its not, you're
>>> good.
>>
>> We agree on that. I am just saying the MAGIC string as it is today
>> may not be enough to tell us that.
>
> Right. I think that's why the attribute struct is used to detect
> endianness and not the magic.
>
Yes, but using attr_size was the wrong choice. I suspect we may be
able to use size (sizeof(f_header)) if we know:
- it is not going to change.
- require perfect match with sizeof(struct perf_file_header)

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] perf: make perf.data more self-descriptive (v4)
  2011-09-16 14:50             ` David Ahern
@ 2011-09-16 15:19               ` Peter Zijlstra
  2011-09-16 15:33                 ` David Ahern
  0 siblings, 1 reply; 21+ messages in thread
From: Peter Zijlstra @ 2011-09-16 15:19 UTC (permalink / raw)
  To: David Ahern; +Cc: Stephane Eranian, linux-kernel, acme, mingo

On Fri, 2011-09-16 at 08:50 -0600, David Ahern wrote:
> 
> ppc data file: header->magic 454c494646524550
> x86 data file: header->magic 454c494646524550
> 
Ok then we need to fix that magic thing..

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] perf: make perf.data more self-descriptive (v4)
  2011-09-16 15:19               ` Peter Zijlstra
@ 2011-09-16 15:33                 ` David Ahern
  2011-09-16 15:42                   ` Peter Zijlstra
  2011-09-16 16:34                   ` Stephane Eranian
  0 siblings, 2 replies; 21+ messages in thread
From: David Ahern @ 2011-09-16 15:33 UTC (permalink / raw)
  To: Peter Zijlstra, Stephane Eranian; +Cc: linux-kernel, acme, mingo



On 09/16/2011 09:19 AM, Peter Zijlstra wrote:
> On Fri, 2011-09-16 at 08:50 -0600, David Ahern wrote:
>>
>> ppc data file: header->magic 454c494646524550
>> x86 data file: header->magic 454c494646524550
>>
> Ok then we need to fix that magic thing..

Right, but it breaks backwards/forwards compatibility. e.g.,

diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 73fa59e..603a8bb9 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -65,9 +65,7 @@ char *perf_header__find_event(u64 id)
    return NULL;
 }

-static const char *__perf_magic = "PERFFILE";
-
-#define PERF_MAGIC (*(u64 *)__perf_magic)
+#define PERF_MAGIC 0x5045524646494c45ULL  // "PERFFILE"

 struct perf_file_attr {
    struct perf_event_attr  attr;

That puts the magic in the file based on endianness of the recording
system. e.g., on x86:

$ od -c perf-magic.data | head -1
0000000   E   L   I   F   F   R   E   P   h  \0  \0  \0  \0  \0  \0  \0

Which means older perf binaries can't analyze new files (created with
new magic order) and newer perf binaries can't analyze older perf files
-- the new command will incorrectly want to do endian swapping on the
old file.

David

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [PATCH] perf: make perf.data more self-descriptive (v4)
  2011-09-16 15:33                 ` David Ahern
@ 2011-09-16 15:42                   ` Peter Zijlstra
  2011-09-16 16:34                   ` Stephane Eranian
  1 sibling, 0 replies; 21+ messages in thread
From: Peter Zijlstra @ 2011-09-16 15:42 UTC (permalink / raw)
  To: David Ahern; +Cc: Stephane Eranian, linux-kernel, acme, mingo

On Fri, 2011-09-16 at 09:33 -0600, David Ahern wrote:
> 
> On 09/16/2011 09:19 AM, Peter Zijlstra wrote:
> > On Fri, 2011-09-16 at 08:50 -0600, David Ahern wrote:
> >>
> >> ppc data file: header->magic 454c494646524550
> >> x86 data file: header->magic 454c494646524550
> >>
> > Ok then we need to fix that magic thing..
> 
> Right, but it breaks backwards/forwards compatibility. e.g.,
> 
> diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
> index 73fa59e..603a8bb9 100644
> --- a/tools/perf/util/header.c
> +++ b/tools/perf/util/header.c
> @@ -65,9 +65,7 @@ char *perf_header__find_event(u64 id)
>     return NULL;
>  }
> 
> -static const char *__perf_magic = "PERFFILE";
> -
> -#define PERF_MAGIC (*(u64 *)__perf_magic)
> +#define PERF_MAGIC 0x5045524646494c45ULL  // "PERFFILE"
> 
>  struct perf_file_attr {
>     struct perf_event_attr  attr;
> 
> That puts the magic in the file based on endianness of the recording
> system. e.g., on x86:
> 
> $ od -c perf-magic.data | head -1
> 0000000   E   L   I   F   F   R   E   P   h  \0  \0  \0  \0  \0  \0  \0
> 
> Which means older perf binaries can't analyze new files (created with
> new magic order) and newer perf binaries can't analyze older perf files
> -- the new command will incorrectly want to do endian swapping on the
> old file.

crap.. sounds like a nice moment to ditch the current format and go for
the multi-file setup.. :-)

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] perf: make perf.data more self-descriptive (v4)
  2011-09-16 15:33                 ` David Ahern
  2011-09-16 15:42                   ` Peter Zijlstra
@ 2011-09-16 16:34                   ` Stephane Eranian
  1 sibling, 0 replies; 21+ messages in thread
From: Stephane Eranian @ 2011-09-16 16:34 UTC (permalink / raw)
  To: David Ahern; +Cc: Peter Zijlstra, linux-kernel, acme, mingo

On Fri, Sep 16, 2011 at 5:33 PM, David Ahern <dsahern@gmail.com> wrote:
>
>
> On 09/16/2011 09:19 AM, Peter Zijlstra wrote:
>> On Fri, 2011-09-16 at 08:50 -0600, David Ahern wrote:
>>>
>>> ppc data file: header->magic 454c494646524550
>>> x86 data file: header->magic 454c494646524550
>>>
>> Ok then we need to fix that magic thing..
>
> Right, but it breaks backwards/forwards compatibility. e.g.,
>
Not necessarily, if you keep the current MAGIC, then you detect
a file with the old format, i.e., no flexibility on attr_size.

Then, you pick two new MAGIC numbers. They must, of course,
reflect the endianness. Those new perf.data files won't work with
older perf for sure, but at least you can maintain backward
compatibility. The new MAGIC must, of course, reflect the
endianness.



> diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
> index 73fa59e..603a8bb9 100644
> --- a/tools/perf/util/header.c
> +++ b/tools/perf/util/header.c
> @@ -65,9 +65,7 @@ char *perf_header__find_event(u64 id)
>    return NULL;
>  }
>
> -static const char *__perf_magic = "PERFFILE";
> -
> -#define PERF_MAGIC (*(u64 *)__perf_magic)
> +#define PERF_MAGIC 0x5045524646494c45ULL  // "PERFFILE"
>
>  struct perf_file_attr {
>    struct perf_event_attr  attr;
>
> That puts the magic in the file based on endianness of the recording
> system. e.g., on x86:
>
> $ od -c perf-magic.data | head -1
> 0000000   E   L   I   F   F   R   E   P   h  \0  \0  \0  \0  \0  \0  \0
>
> Which means older perf binaries can't analyze new files (created with
> new magic order) and newer perf binaries can't analyze older perf files
> -- the new command will incorrectly want to do endian swapping on the
> old file.
>
> David
>

^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2011-09-16 16:34 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-09-07 19:10 [PATCH] perf: make perf.data more self-descriptive (v4) Stephane Eranian
2011-09-08 15:27 ` David Ahern
     [not found]   ` <CABPqkBRKcxeTDMyVYPXU6AQEY-Hp2bnE03ebEtvjYWde2JKqoQ@mail.gmail.com>
2011-09-08 15:37     ` David Ahern
2011-09-08 15:42   ` Stephane Eranian
2011-09-12 13:45 ` David Ahern
2011-09-12 13:54   ` Stephane Eranian
2011-09-12 14:33     ` David Ahern
2011-09-12 14:40       ` Stephane Eranian
2011-09-12 14:43         ` Peter Zijlstra
2011-09-12 14:47           ` Stephane Eranian
2011-09-13 14:40             ` Arnaldo Carvalho de Melo
2011-09-16 14:35           ` Stephane Eranian
2011-09-16 14:37             ` Peter Zijlstra
2011-09-16 14:44               ` Stephane Eranian
2011-09-16 14:52                 ` David Ahern
2011-09-16 14:56                   ` Stephane Eranian
2011-09-16 14:50             ` David Ahern
2011-09-16 15:19               ` Peter Zijlstra
2011-09-16 15:33                 ` David Ahern
2011-09-16 15:42                   ` Peter Zijlstra
2011-09-16 16:34                   ` Stephane Eranian

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox