All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/5] perf tools: add meta-data header support in pipe mode
@ 2012-04-11  9:01 Stephane Eranian
  2012-04-11  9:01 ` [PATCH 1/5] perf inject: fix broken perf inject -b Stephane Eranian
                   ` (5 more replies)
  0 siblings, 6 replies; 9+ messages in thread
From: Stephane Eranian @ 2012-04-11  9:01 UTC (permalink / raw)
  To: linux-kernel; +Cc: peterz, mingo, acme, dsahern, namhyung.kim

This patch series adds meta-data support to perf record/report in pipe
mode:
	perf record -o - foo | perf inject -b | perf report -i -

We had meta-data (information about host configuration, perf tool version,...)
in regular (file) mode but it was lacking in pipe mode. This patch series fixes
this.

In pipe mode, there is no meta-data header structures at the beginning of the
streamed data given we cannot seek in a pipe. Instead, we need to create pseudo
record types for each of the possible features, e.g., hostname, cpuid, event_desc,
and so on. Those pseudo records are guaranteed to be before any actual sample
records, therefore perf report/annotate are guaranteed to get the information
they need before processing the first sample.

The series also fixes perf inject to actually inject build-ids and buildid-list
to work better with pipe mode, i.e., print the build-ids.

With this series:

 $ perf record -o - noploop 2 | perf inject -b | perf report -i -
      # ========
      # captured on: Fri Jan 20 18:13:55 2012
      # ========
      #
      # hostname : quad
      # os release : 3.2.0-rc7-tip
      # perf version : 3.2.0
      # arch : x86_64
      # nrcpus online : 4
      # nrcpus avail : 4
      # cpudesc : Intel(R) Core(TM)2 Quad CPU Q6600 @ 2.40GHz
      # cpuid : GenuineIntel,6,15,11
      # total memory : 8092884 kB
      ...
      # HEADER_CPU_TOPOLOGY info available, use -I to display
      noploop for 2 seconds
      [ perf record: Woken up 1 times to write data ]
      [ perf record: Captured and wrote 0.084 MB - (~3677 samples) ]
        99.80%  noploop  noploop            [.] noploop
         0.19%  noploop  [kernel.kallsyms]  [k] radix_tree_gang_lookup

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

Stephane Eranian (5):
  perf inject: fix broken perf inject -b
  perf tools: fix piped mode read code
  perf tools: rename HEADER_TRACE_INFO to HEADER_TRACING_DATA
  perf record: add meta-data support for pipe-mode
  perf: make perf buildid-list work better with pipe mode

 tools/perf/builtin-annotate.c     |    5 +
 tools/perf/builtin-buildid-list.c |    6 +-
 tools/perf/builtin-inject.c       |    6 +
 tools/perf/builtin-record.c       |   11 +-
 tools/perf/builtin-report.c       |    7 +-
 tools/perf/util/build-id.c        |    2 +
 tools/perf/util/event.c           |   12 +
 tools/perf/util/event.h           |   18 ++
 tools/perf/util/header.c          |  538 ++++++++++++++++++++++++++-----------
 tools/perf/util/header.h          |   11 +-
 tools/perf/util/session.c         |   52 +++-
 tools/perf/util/tool.h            |    4 +-
 12 files changed, 494 insertions(+), 178 deletions(-)

-- 
1.7.4.1


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

* [PATCH 1/5] perf inject: fix broken perf inject -b
  2012-04-11  9:01 [PATCH 0/5] perf tools: add meta-data header support in pipe mode Stephane Eranian
@ 2012-04-11  9:01 ` Stephane Eranian
  2012-04-11  9:01 ` [PATCH 2/5] perf tools: fix piped mode read code Stephane Eranian
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 9+ messages in thread
From: Stephane Eranian @ 2012-04-11  9:01 UTC (permalink / raw)
  To: linux-kernel; +Cc: peterz, mingo, acme, dsahern, namhyung.kim

perf inject -b was broken. It would not inject any build_id
into the stream. Furthermore, it would strip samples from the
stream.

The reason was a missing initialization of the event attribute
structure. The perf_tool.tool.attr() callback was pointing to
a simple repipe. But there was no initialization of the internal
data structures to keep track of events and event ids. That later
caused event id lookups to fail, and sample would get removed.

The patch simply adds back the call to perf_event__process_attr()
to initialize the evlist structure and now build_ids are again
injected.

Signed-off-by: Stephane Eranian <eranian@google.com>
---
 tools/perf/builtin-inject.c |    5 +++++
 1 files changed, 5 insertions(+), 0 deletions(-)

diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c
index 09c1061..3beab48 100644
--- a/tools/perf/builtin-inject.c
+++ b/tools/perf/builtin-inject.c
@@ -60,6 +60,11 @@ static int perf_event__repipe_tracing_data_synth(union perf_event *event,
 static int perf_event__repipe_attr(union perf_event *event,
 				   struct perf_evlist **pevlist __used)
 {
+	int ret;
+	ret = perf_event__process_attr(event, pevlist);
+	if (ret)
+		return ret;
+
 	return perf_event__repipe_synth(NULL, event, NULL);
 }
 
-- 
1.7.4.1


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

* [PATCH 2/5] perf tools: fix piped mode read code
  2012-04-11  9:01 [PATCH 0/5] perf tools: add meta-data header support in pipe mode Stephane Eranian
  2012-04-11  9:01 ` [PATCH 1/5] perf inject: fix broken perf inject -b Stephane Eranian
@ 2012-04-11  9:01 ` Stephane Eranian
  2012-05-11  0:11   ` David Ahern
  2012-04-11  9:01 ` [PATCH 3/5] perf tools: rename HEADER_TRACE_INFO to HEADER_TRACING_DATA Stephane Eranian
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 9+ messages in thread
From: Stephane Eranian @ 2012-04-11  9:01 UTC (permalink / raw)
  To: linux-kernel; +Cc: peterz, mingo, acme, dsahern, namhyung.kim

In __perf_session__process_pipe_events(), there was a risk
we would read more than what a union perf_event struct can
hold. this could happen in case, perf is reading a file which
contains new record types it does not know about and which are
larger than anything it knows about.

In general, perf is supposed to skip records it does not
understand, but in pipe mode, those have to be read and ignored.
The fixed size header contains the size of the record, but that
size may be larger than union perf_event, yet it was used as
the backing to the read in:

  union perf_event event;
  void *p;

  size = event->header.size;

  p = &event;
  p += sizeof(struct perf_event_header);
  if (size - sizeof(struct perf_event_header)) {
    err = readn(self->fd, p, size - sizeof(struct perf_event_header));

We fix this by allocating a buffer based on the size reported in
the header. We reuse the buffer as much as we can. We realloc in
case it becomes too small. In the  common case, the performance
impact is negligible.

Signed-off-by: Stephane Eranian <eranian@google.com>
---
 tools/perf/util/session.c |   35 +++++++++++++++++++++++++++--------
 1 files changed, 27 insertions(+), 8 deletions(-)

diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 9412e3b..d13e915 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -1056,8 +1056,9 @@ volatile int session_done;
 static int __perf_session__process_pipe_events(struct perf_session *self,
 					       struct perf_tool *tool)
 {
-	union perf_event event;
-	uint32_t size;
+	union perf_event *event;
+	uint32_t size, cur_size = 0;
+	void *buf = NULL;
 	int skip = 0;
 	u64 head;
 	int err;
@@ -1066,8 +1067,14 @@ static int __perf_session__process_pipe_events(struct perf_session *self,
 	perf_tool__fill_defaults(tool);
 
 	head = 0;
+	cur_size = sizeof(union perf_event);
+
+	buf = malloc(cur_size);
+	if (!buf)
+		return -errno;
 more:
-	err = readn(self->fd, &event, sizeof(struct perf_event_header));
+	event = buf;
+	err = readn(self->fd, event, sizeof(struct perf_event_header));
 	if (err <= 0) {
 		if (err == 0)
 			goto done;
@@ -1077,13 +1084,23 @@ static int __perf_session__process_pipe_events(struct perf_session *self,
 	}
 
 	if (self->header.needs_swap)
-		perf_event_header__bswap(&event.header);
+		perf_event_header__bswap(&event->header);
 
-	size = event.header.size;
+	size = event->header.size;
 	if (size == 0)
 		size = 8;
 
-	p = &event;
+	if (size > cur_size) {
+		void *new = realloc(buf, size);
+		if (!new) {
+			pr_err("failed to allocate memory to read event\n");
+			goto out_err;
+		}
+		buf = new;
+		cur_size = size;
+		event = buf;
+	}
+	p = event;
 	p += sizeof(struct perf_event_header);
 
 	if (size - sizeof(struct perf_event_header)) {
@@ -1099,9 +1116,10 @@ static int __perf_session__process_pipe_events(struct perf_session *self,
 		}
 	}
 
-	if ((skip = perf_session__process_event(self, &event, tool, head)) < 0) {
+	skip = perf_session__process_event(self, event, tool, head);
+	if (skip < 0) {
 		dump_printf("%#" PRIx64 " [%#x]: skipping unknown header type: %d\n",
-			    head, event.header.size, event.header.type);
+			    head, event->header.size, event->header.type);
 		/*
 		 * assume we lost track of the stream, check alignment, and
 		 * increment a single u64 in the hope to catch on again 'soon'.
@@ -1122,6 +1140,7 @@ static int __perf_session__process_pipe_events(struct perf_session *self,
 done:
 	err = 0;
 out_err:
+	free(buf);
 	perf_session__warn_about_errors(self, tool);
 	perf_session_free_sample_buffers(self);
 	return err;
-- 
1.7.4.1


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

* [PATCH 3/5] perf tools: rename HEADER_TRACE_INFO to HEADER_TRACING_DATA
  2012-04-11  9:01 [PATCH 0/5] perf tools: add meta-data header support in pipe mode Stephane Eranian
  2012-04-11  9:01 ` [PATCH 1/5] perf inject: fix broken perf inject -b Stephane Eranian
  2012-04-11  9:01 ` [PATCH 2/5] perf tools: fix piped mode read code Stephane Eranian
@ 2012-04-11  9:01 ` Stephane Eranian
  2012-04-11  9:01 ` [PATCH 4/5] perf record: add meta-data support for pipe-mode Stephane Eranian
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 9+ messages in thread
From: Stephane Eranian @ 2012-04-11  9:01 UTC (permalink / raw)
  To: linux-kernel; +Cc: peterz, mingo, acme, dsahern, namhyung.kim

To match the PERF_RECORD_HEADER_TRACING_DATA record type.
This is the same info as the one used for pipe mode whereas
the other one is for regular file output. This will help in
the later patch to add meta-data infos in pipe mode.

Signed-off-by: Stephane Eranian <eranian@google.com>
---
 tools/perf/builtin-record.c |    4 ++--
 tools/perf/util/header.c    |   10 +++++-----
 tools/perf/util/header.h    |    2 +-
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index be4e1ee..2a64679 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -389,7 +389,7 @@ static void perf_record__mmap_read_all(struct perf_record *rec)
 			perf_record__mmap_read(rec, &rec->evlist->mmap[i]);
 	}
 
-	if (perf_header__has_feat(&rec->session->header, HEADER_TRACE_INFO))
+	if (perf_header__has_feat(&rec->session->header, HEADER_TRACING_DATA))
 		write_output(rec, &finished_round_event, sizeof(finished_round_event));
 }
 
@@ -471,7 +471,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
 		perf_header__clear_feat(&session->header, HEADER_BUILD_ID);
 
 	if (!have_tracepoints(&evsel_list->entries))
-		perf_header__clear_feat(&session->header, HEADER_TRACE_INFO);
+		perf_header__clear_feat(&session->header, HEADER_TRACING_DATA);
 
 	if (!rec->opts.branch_stack)
 		perf_header__clear_feat(&session->header, HEADER_BRANCH_STACK);
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 4c7c2d7..99af448 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -442,7 +442,7 @@ static bool perf_session__read_build_ids(struct perf_session *session, bool with
 	return ret;
 }
 
-static int write_trace_info(int fd, struct perf_header *h __used,
+static int write_tracing_data(int fd, struct perf_header *h __used,
 			    struct perf_evlist *evlist)
 {
 	return read_tracing_data(fd, &evlist->entries);
@@ -1477,7 +1477,7 @@ static int perf_header__read_build_ids(struct perf_header *header,
 	return err;
 }
 
-static int process_trace_info(struct perf_file_section *section __unused,
+static int process_tracing_data(struct perf_file_section *section __unused,
 			      struct perf_header *ph __unused,
 			      int feat __unused, int fd)
 {
@@ -1513,11 +1513,11 @@ struct feature_ops {
 		.full_only = true }
 
 /* feature_ops not implemented: */
-#define print_trace_info		NULL
-#define print_build_id			NULL
+#define print_tracing_data	NULL
+#define print_build_id		NULL
 
 static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
-	FEAT_OPP(HEADER_TRACE_INFO,	trace_info),
+	FEAT_OPP(HEADER_TRACING_DATA,	tracing_data),
 	FEAT_OPP(HEADER_BUILD_ID,	build_id),
 	FEAT_OPA(HEADER_HOSTNAME,	hostname),
 	FEAT_OPA(HEADER_OSRELEASE,	osrelease),
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
index 21a6be0..2d42b3e 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -12,7 +12,7 @@
 enum {
 	HEADER_RESERVED		= 0,	/* always cleared */
 	HEADER_FIRST_FEATURE	= 1,
-	HEADER_TRACE_INFO	= 1,
+	HEADER_TRACING_DATA	= 1,
 	HEADER_BUILD_ID,
 
 	HEADER_HOSTNAME,
-- 
1.7.4.1


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

* [PATCH 4/5] perf record: add meta-data support for pipe-mode
  2012-04-11  9:01 [PATCH 0/5] perf tools: add meta-data header support in pipe mode Stephane Eranian
                   ` (2 preceding siblings ...)
  2012-04-11  9:01 ` [PATCH 3/5] perf tools: rename HEADER_TRACE_INFO to HEADER_TRACING_DATA Stephane Eranian
@ 2012-04-11  9:01 ` Stephane Eranian
  2012-04-11  9:01 ` [PATCH 5/5] perf: make perf buildid-list work better with pipe mode Stephane Eranian
  2012-04-26 15:14 ` [PATCH 0/5] perf tools: add meta-data header support in " Stephane Eranian
  5 siblings, 0 replies; 9+ messages in thread
From: Stephane Eranian @ 2012-04-11  9:01 UTC (permalink / raw)
  To: linux-kernel; +Cc: peterz, mingo, acme, dsahern, namhyung.kim

This patch adds the meta-data header support for perf record
when used in pipe mode: perf record -o -

Up until now, meta-data was only available when perf record
was used in "regular" mode, i.e., generating a perf.data file.
For users depending on pipe mode, no host, event header information
was gathered. This patch addresses this limitation.

The difficulty in pipe mode is that information needs to be written
sequentially to the pipe. Meta data headers are usually generated
(and also expected) at the beginning of the file (or piped output).
To solve this problem, we introduce new synthetic record types,
one for each meta-data type. The approach is similar to what
is already used for BUILD_ID and TRACING_DATA.

We have modified util/header.c such that the same routines are used
to generate and read the meta-data information regardless of pipe-mode
vs. regular mode. To make this work, we added a new struct called
feat_fd which encapsulates all the information necessary to read or
write meta-data information to a file/pipe or from a file/pipe.

It should be noted that there is a limitation with the current
perf in terms of endianess in pipe mode. Perf assumes the records
are generated using the same endianess during collection and
analysis.  That is always the case with the example shown below.
However, one could also do:
$ perf record -o - noploop 2 | perf inject -b >perf.data
$ cat perf.data | perf report -i -

With this patch, it is possible to get:
 $ perf record -o - noploop 2 | perf inject -b | perf report -i -
      # ========
      # captured on: Fri Jan 20 18:13:55 2012
      # ========
      #
      # hostname : quad
      # os release : 3.2.0-rc7-tip
      # perf version : 3.2.0
      # arch : x86_64
      # nrcpus online : 4
      # nrcpus avail : 4
      # cpudesc : Intel(R) Core(TM)2 Quad CPU Q6600 @ 2.40GHz
      # cpuid : GenuineIntel,6,15,11
      # total memory : 8092884 kB
      ...
      # HEADER_CPU_TOPOLOGY info available, use -I to display
      noploop for 2 seconds
      [ perf record: Woken up 1 times to write data ]
      [ perf record: Captured and wrote 0.084 MB - (~3677 samples) ]
        99.80%  noploop  noploop            [.] noploop
         0.19%  noploop  [kernel.kallsyms]  [k] radix_tree_gang_lookup

Signed-off-by: Stephane Eranian <eranian@google.com>
---
 tools/perf/builtin-annotate.c |    5 +
 tools/perf/builtin-inject.c   |    1 +
 tools/perf/builtin-record.c   |    7 +
 tools/perf/builtin-report.c   |    7 +-
 tools/perf/util/event.c       |   12 +
 tools/perf/util/event.h       |   18 ++
 tools/perf/util/header.c      |  532 +++++++++++++++++++++++++++++------------
 tools/perf/util/header.h      |    9 +
 tools/perf/util/session.c     |   17 ++-
 tools/perf/util/tool.h        |    4 +-
 10 files changed, 449 insertions(+), 163 deletions(-)

diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 806e0a2..fe42298 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -247,6 +247,11 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __used)
 			.mmap	= perf_event__process_mmap,
 			.comm	= perf_event__process_comm,
 			.fork	= perf_event__process_task,
+			.attr	= perf_event__process_attr,
+			.event_type	 = perf_event__process_event_type,
+			.tracing_data	 = perf_event__process_tracing_data,
+			.build_id	 = perf_event__process_build_id,
+			.feature	 = perf_event__process_feature,
 			.ordered_samples = true,
 			.ordering_requires_timestamps = true,
 		},
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c
index 3beab48..65b0529 100644
--- a/tools/perf/builtin-inject.c
+++ b/tools/perf/builtin-inject.c
@@ -217,6 +217,7 @@ struct perf_tool perf_inject = {
 	.event_type	= perf_event__repipe_event_type_synth,
 	.tracing_data	= perf_event__repipe_tracing_data_synth,
 	.build_id	= perf_event__repipe_op2_synth,
+	.feature	= perf_event__repipe_op2_synth,
 };
 
 extern volatile int session_done;
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 2a64679..175acdf 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -524,6 +524,13 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
 	}
 
 	if (opts->pipe_output) {
+		err = perf_event__synthesize_features(tool, session, evsel_list,
+						   process_synthesized_event);
+		if (err < 0) {
+			pr_err("Couldn't synthesize features.\n");
+			return err;
+		}
+
 		err = perf_event__synthesize_attrs(tool, session,
 						   process_synthesized_event);
 		if (err < 0) {
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 2e31743..350b888 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -43,7 +43,6 @@ struct perf_report {
 	bool			force, use_tui, use_gtk, use_stdio;
 	bool			hide_unresolved;
 	bool			dont_use_callchains;
-	bool			show_full_info;
 	bool			show_threads;
 	bool			inverted_callchain;
 	struct perf_read_values	show_threads_values;
@@ -355,7 +354,8 @@ static int __cmd_report(struct perf_report *rep)
 	}
 
 	if (use_browser <= 0)
-		perf_session__fprintf_info(session, stdout, rep->show_full_info);
+		perf_session__fprintf_info(session, stdout,
+					   rep->tool.show_all_features);
 
 	if (rep->show_threads)
 		perf_read_values_init(&rep->show_threads_values);
@@ -552,6 +552,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
 			.event_type	 = perf_event__process_event_type,
 			.tracing_data	 = perf_event__process_tracing_data,
 			.build_id	 = perf_event__process_build_id,
+			.feature	 = perf_event__process_feature,
 			.ordered_samples = true,
 			.ordering_requires_timestamps = true,
 		},
@@ -615,7 +616,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
 		    "Look for files with symbols relative to this directory"),
 	OPT_STRING('C', "cpu", &report.cpu_list, "cpu",
 		   "list of cpus to profile"),
-	OPT_BOOLEAN('I', "show-info", &report.show_full_info,
+	OPT_BOOLEAN('I', "show-info", &report.tool.show_all_features,
 		    "Display extended information about perf.data file"),
 	OPT_BOOLEAN(0, "source", &symbol_conf.annotate_src,
 		    "Interleave source code with assembly code (default)"),
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index 2a6f33c..da3551f 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -23,6 +23,18 @@ static const char *perf_event__names[] = {
 	[PERF_RECORD_HEADER_TRACING_DATA]	= "TRACING_DATA",
 	[PERF_RECORD_HEADER_BUILD_ID]		= "BUILD_ID",
 	[PERF_RECORD_FINISHED_ROUND]		= "FINISHED_ROUND",
+	[PERF_RECORD_HEADER_HOSTNAME]		= "HOSTNAME",
+	[PERF_RECORD_HEADER_OSRELEASE]		= "OSRELEASE",
+	[PERF_RECORD_HEADER_VERSION]		= "VERSION",
+	[PERF_RECORD_HEADER_ARCH]		= "ARCH",
+	[PERF_RECORD_HEADER_NRCPUS]		= "NRCPUS",
+	[PERF_RECORD_HEADER_CPUDESC]		= "CPUDESC",
+	[PERF_RECORD_HEADER_CPUID]		= "CPUID",
+	[PERF_RECORD_HEADER_TOTAL_MEM]		= "TOTAL_MEM",
+	[PERF_RECORD_HEADER_CMDLINE]		= "CMDLINE",
+	[PERF_RECORD_HEADER_EVENT_DESC]		= "EVENT_DESC",
+	[PERF_RECORD_HEADER_CPU_TOPOLOGY]	= "CPU_TOPOLOGY",
+	[PERF_RECORD_HEADER_NUMA_TOPOLOGY]	= "NUMA_TOPOLOGY",
 };
 
 const char *perf_event__name(unsigned int id)
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index 1b19728..e44aaec 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -100,6 +100,18 @@ enum perf_user_event_type { /* above any possible kernel type */
 	PERF_RECORD_HEADER_TRACING_DATA		= 66,
 	PERF_RECORD_HEADER_BUILD_ID		= 67,
 	PERF_RECORD_FINISHED_ROUND		= 68,
+	PERF_RECORD_HEADER_HOSTNAME		= 69,
+	PERF_RECORD_HEADER_OSRELEASE		= 70,
+	PERF_RECORD_HEADER_VERSION		= 71,
+	PERF_RECORD_HEADER_ARCH			= 72,
+	PERF_RECORD_HEADER_NRCPUS		= 73,
+	PERF_RECORD_HEADER_CPUDESC		= 74,
+	PERF_RECORD_HEADER_CPUID		= 75,
+	PERF_RECORD_HEADER_TOTAL_MEM		= 76,
+	PERF_RECORD_HEADER_CMDLINE		= 77,
+	PERF_RECORD_HEADER_EVENT_DESC		= 78,
+	PERF_RECORD_HEADER_CPU_TOPOLOGY		= 79,
+	PERF_RECORD_HEADER_NUMA_TOPOLOGY	= 80,
 	PERF_RECORD_HEADER_MAX
 };
 
@@ -126,6 +138,11 @@ struct tracing_data_event {
 	u32 size;
 };
 
+struct feature_event {
+	struct perf_event_header header;
+	char data[]; /* size bytes of raw data specific to the feature */
+};
+
 union perf_event {
 	struct perf_event_header	header;
 	struct ip_event			ip;
@@ -139,6 +156,7 @@ union perf_event {
 	struct event_type_event		event_type;
 	struct tracing_data_event	tracing_data;
 	struct build_id_event		build_id;
+	struct feature_event		feat;
 };
 
 void perf_event__print_totals(void);
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 99af448..6072fba 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -20,6 +20,16 @@
 #include "symbol.h"
 #include "debug.h"
 #include "cpumap.h"
+#include "tool.h"
+
+struct feat_fd {
+	int fd;
+	int needs_swap;
+	void *buf;
+	struct perf_header *hdr;
+	size_t size;
+	size_t pos;
+};
 
 static bool no_buildid_cache = false;
 
@@ -98,14 +108,46 @@ bool perf_header__has_feat(const struct perf_header *header, int feat)
 	return test_bit(feat, header->adds_features);
 }
 
-static int do_write(int fd, const void *buf, size_t size)
+static ssize_t do_read(struct feat_fd *fd, void *addr, size_t size)
+{
+	if (!fd->buf)
+		return read(fd->fd, addr, size);
+
+	size = min(size, (fd->size - fd->pos));
+
+	memcpy(addr, fd->buf+fd->pos, size);
+
+	fd->pos += size;
+
+	return size;
+}
+
+static int do_write(struct feat_fd *fd, const void *buf, size_t size)
 {
+	void *addr;
+	int ret;
+
 	while (size) {
-		int ret = write(fd, buf, size);
 
-		if (ret < 0)
-			return -errno;
+		if (fd->buf == NULL) {
+			ret = write(fd->fd, buf, size);
+			if (ret < 0)
+				return -errno;
+		} else {
+retry:
+			if (size > (fd->size - fd->pos)) {
+				addr = realloc(fd->buf, fd->size << 1);
+				if (!addr)
+					return -ENOSPC;
+				fd->buf = addr;
+				fd->size <<= 1;
+				goto retry;
+			}
 
+			memcpy(fd->buf+fd->pos, buf, size);
+			fd->pos += size;
+			ret = size;
+		}
 		size -= ret;
 		buf += ret;
 	}
@@ -115,7 +157,7 @@ static int do_write(int fd, const void *buf, size_t size)
 
 #define NAME_ALIGN 64
 
-static int write_padded(int fd, const void *bf, size_t count,
+static int write_padded(struct feat_fd *fd, const void *bf, size_t count,
 			size_t count_aligned)
 {
 	static const char zero_buf[NAME_ALIGN];
@@ -127,7 +169,7 @@ static int write_padded(int fd, const void *bf, size_t count,
 	return err;
 }
 
-static int do_write_string(int fd, const char *str)
+static int do_write_string(struct feat_fd *fd, const char *str)
 {
 	u32 len, olen;
 	int ret;
@@ -143,24 +185,35 @@ static int do_write_string(int fd, const char *str)
 	return write_padded(fd, str, olen, len);
 }
 
-static char *do_read_string(int fd, struct perf_header *ph)
+static char *do_read_string(struct feat_fd *fd)
 {
 	ssize_t sz, ret;
 	u32 len;
 	char *buf;
 
-	sz = read(fd, &len, sizeof(len));
-	if (sz < (ssize_t)sizeof(len))
-		return NULL;
+	if (fd->buf) {
+		len = *(int *)(fd->buf + fd->pos);
+		fd->pos += sizeof(u32);
+	} else {
+		sz = read(fd->fd, &len, sizeof(len));
+		if (sz < (ssize_t)sizeof(len))
+			return NULL;
+	}
 
-	if (ph->needs_swap)
+	if (fd->needs_swap)
 		len = bswap_32(len);
 
 	buf = malloc(len);
 	if (!buf)
 		return NULL;
 
-	ret = read(fd, buf, len);
+	if (fd->buf) {
+		memcpy(buf, fd->buf+fd->pos, len);
+		fd->pos += len;
+		return buf;
+	}
+
+	ret = read(fd->fd, buf, len);
 	if (ret == (ssize_t)len) {
 		/*
 		 * strings are padded by zeroes
@@ -169,7 +222,6 @@ static char *do_read_string(int fd, struct perf_header *ph)
 		 */
 		return buf;
 	}
-
 	free(buf);
 	return NULL;
 }
@@ -203,7 +255,7 @@ perf_header__set_cmdline(int argc, const char **argv)
 		else
 
 static int __dsos__write_buildid_table(struct list_head *head, pid_t pid,
-				u16 misc, int fd)
+				u16 misc, struct feat_fd *fd)
 {
 	struct dso *pos;
 
@@ -233,7 +285,8 @@ static int __dsos__write_buildid_table(struct list_head *head, pid_t pid,
 	return 0;
 }
 
-static int machine__write_buildid_table(struct machine *machine, int fd)
+static int machine__write_buildid_table(struct machine *machine,
+					struct feat_fd *fd)
 {
 	int err;
 	u16 kmisc = PERF_RECORD_MISC_KERNEL,
@@ -252,7 +305,8 @@ static int machine__write_buildid_table(struct machine *machine, int fd)
 	return err;
 }
 
-static int dsos__write_buildid_table(struct perf_header *header, int fd)
+static int dsos__write_buildid_table(struct perf_header *header,
+				     struct feat_fd *fd)
 {
 	struct perf_session *session = container_of(header,
 			struct perf_session, header);
@@ -442,25 +496,24 @@ static bool perf_session__read_build_ids(struct perf_session *session, bool with
 	return ret;
 }
 
-static int write_tracing_data(int fd, struct perf_header *h __used,
-			    struct perf_evlist *evlist)
+static int write_tracing_data(struct feat_fd *fd, struct perf_evlist *evlist)
 {
-	return read_tracing_data(fd, &evlist->entries);
+	return read_tracing_data(fd->fd, &evlist->entries);
 }
 
 
-static int write_build_id(int fd, struct perf_header *h,
+static int write_build_id(struct feat_fd *fd,
 			  struct perf_evlist *evlist __used)
 {
 	struct perf_session *session;
 	int err;
 
-	session = container_of(h, struct perf_session, header);
+	session = container_of(fd->hdr, struct perf_session, header);
 
 	if (!perf_session__read_build_ids(session, true))
 		return -1;
 
-	err = dsos__write_buildid_table(h, fd);
+	err = dsos__write_buildid_table(fd->hdr, fd);
 	if (err < 0) {
 		pr_debug("failed to write buildid table\n");
 		return err;
@@ -471,8 +524,7 @@ static int write_build_id(int fd, struct perf_header *h,
 	return 0;
 }
 
-static int write_hostname(int fd, struct perf_header *h __used,
-			  struct perf_evlist *evlist __used)
+static int write_hostname(struct feat_fd *fd, struct perf_evlist *evlist __used)
 {
 	struct utsname uts;
 	int ret;
@@ -484,7 +536,7 @@ static int write_hostname(int fd, struct perf_header *h __used,
 	return do_write_string(fd, uts.nodename);
 }
 
-static int write_osrelease(int fd, struct perf_header *h __used,
+static int write_osrelease(struct feat_fd *fd,
 			   struct perf_evlist *evlist __used)
 {
 	struct utsname uts;
@@ -497,8 +549,7 @@ static int write_osrelease(int fd, struct perf_header *h __used,
 	return do_write_string(fd, uts.release);
 }
 
-static int write_arch(int fd, struct perf_header *h __used,
-		      struct perf_evlist *evlist __used)
+static int write_arch(struct feat_fd *fd, struct perf_evlist *evlist __used)
 {
 	struct utsname uts;
 	int ret;
@@ -510,14 +561,12 @@ static int write_arch(int fd, struct perf_header *h __used,
 	return do_write_string(fd, uts.machine);
 }
 
-static int write_version(int fd, struct perf_header *h __used,
-			 struct perf_evlist *evlist __used)
+static int write_version(struct feat_fd *fd, struct perf_evlist *evlist __used)
 {
 	return do_write_string(fd, perf_version_string);
 }
 
-static int write_cpudesc(int fd, struct perf_header *h __used,
-		       struct perf_evlist *evlist __used)
+static int write_cpudesc(struct feat_fd *fd, struct perf_evlist *evlist __used)
 {
 #ifndef CPUINFO_PROC
 #define CPUINFO_PROC NULL
@@ -575,8 +624,7 @@ static int write_cpudesc(int fd, struct perf_header *h __used,
 	return ret;
 }
 
-static int write_nrcpus(int fd, struct perf_header *h __used,
-			struct perf_evlist *evlist __used)
+static int write_nrcpus(struct feat_fd *fd, struct perf_evlist *evlist __used)
 {
 	long nr;
 	u32 nrc, nra;
@@ -601,8 +649,7 @@ static int write_nrcpus(int fd, struct perf_header *h __used,
 	return do_write(fd, &nra, sizeof(nra));
 }
 
-static int write_event_desc(int fd, struct perf_header *h __used,
-			    struct perf_evlist *evlist)
+static int write_event_desc(struct feat_fd *fd, struct perf_evlist *evlist)
 {
 	struct perf_evsel *attr;
 	u32 nre = 0, nri, sz;
@@ -659,8 +706,7 @@ static int write_event_desc(int fd, struct perf_header *h __used,
 	return 0;
 }
 
-static int write_cmdline(int fd, struct perf_header *h __used,
-			 struct perf_evlist *evlist __used)
+static int write_cmdline(struct feat_fd *fd, struct perf_evlist *evlist __used)
 {
 	char buf[MAXPATHLEN];
 	char proc[32];
@@ -828,8 +874,8 @@ static struct cpu_topo *build_cpu_topology(void)
 	return tp;
 }
 
-static int write_cpu_topology(int fd, struct perf_header *h __used,
-			  struct perf_evlist *evlist __used)
+static int write_cpu_topology(struct feat_fd *fd,
+			      struct perf_evlist *evlist __used)
 {
 	struct cpu_topo *tp;
 	u32 i;
@@ -862,10 +908,8 @@ static int write_cpu_topology(int fd, struct perf_header *h __used,
 	return ret;
 }
 
-
-
-static int write_total_mem(int fd, struct perf_header *h __used,
-			  struct perf_evlist *evlist __used)
+static int write_total_mem(struct feat_fd *fd,
+			   struct perf_evlist *evlist __used)
 {
 	char *buf = NULL;
 	FILE *fp;
@@ -892,7 +936,7 @@ static int write_total_mem(int fd, struct perf_header *h __used,
 	return ret;
 }
 
-static int write_topo_node(int fd, int node)
+static int write_topo_node(struct feat_fd *fd, int node)
 {
 	char str[MAXPATHLEN];
 	char field[32];
@@ -950,8 +994,8 @@ static int write_topo_node(int fd, int node)
 	return ret;
 }
 
-static int write_numa_topology(int fd, struct perf_header *h __used,
-			  struct perf_evlist *evlist __used)
+static int write_numa_topology(struct feat_fd *fd,
+			       struct perf_evlist *evlist __used)
 {
 	char *buf = NULL;
 	size_t len = 0;
@@ -1008,8 +1052,7 @@ int __attribute__((weak)) get_cpuid(char *buffer __used, size_t sz __used)
 	return -1;
 }
 
-static int write_cpuid(int fd, struct perf_header *h __used,
-		       struct perf_evlist *evlist __used)
+static int write_cpuid(struct feat_fd *fd, struct perf_evlist *evlist __used)
 {
 	char buffer[64];
 	int ret;
@@ -1023,128 +1066,128 @@ static int write_cpuid(int fd, struct perf_header *h __used,
 	return do_write_string(fd, buffer);
 }
 
-static int write_branch_stack(int fd __used, struct perf_header *h __used,
-		       struct perf_evlist *evlist __used)
+static int write_branch_stack(struct feat_fd *fd __used,
+			      struct perf_evlist *evlist __used)
 {
 	return 0;
 }
 
-static void print_hostname(struct perf_header *ph, int fd, FILE *fp)
+static void print_hostname(struct feat_fd *fd, FILE *fp)
 {
-	char *str = do_read_string(fd, ph);
+	char *str = do_read_string(fd);
 	fprintf(fp, "# hostname : %s\n", str);
 	free(str);
 }
 
-static void print_osrelease(struct perf_header *ph, int fd, FILE *fp)
+static void print_osrelease(struct feat_fd *fd, FILE *fp)
 {
-	char *str = do_read_string(fd, ph);
+	char *str = do_read_string(fd);
 	fprintf(fp, "# os release : %s\n", str);
 	free(str);
 }
 
-static void print_arch(struct perf_header *ph, int fd, FILE *fp)
+static void print_arch(struct feat_fd *fd, FILE *fp)
 {
-	char *str = do_read_string(fd, ph);
+	char *str = do_read_string(fd);
 	fprintf(fp, "# arch : %s\n", str);
 	free(str);
 }
 
-static void print_cpudesc(struct perf_header *ph, int fd, FILE *fp)
+static void print_cpudesc(struct feat_fd *fd, FILE *fp)
 {
-	char *str = do_read_string(fd, ph);
+	char *str = do_read_string(fd);
 	fprintf(fp, "# cpudesc : %s\n", str);
 	free(str);
 }
 
-static void print_nrcpus(struct perf_header *ph, int fd, FILE *fp)
+static void print_nrcpus(struct feat_fd *fd, FILE *fp)
 {
 	ssize_t ret;
 	u32 nr;
 
-	ret = read(fd, &nr, sizeof(nr));
+	ret = do_read(fd, &nr, sizeof(nr));
 	if (ret != (ssize_t)sizeof(nr))
 		nr = -1; /* interpreted as error */
 
-	if (ph->needs_swap)
+	if (fd->needs_swap)
 		nr = bswap_32(nr);
 
 	fprintf(fp, "# nrcpus online : %u\n", nr);
 
-	ret = read(fd, &nr, sizeof(nr));
+	ret = do_read(fd, &nr, sizeof(nr));
 	if (ret != (ssize_t)sizeof(nr))
 		nr = -1; /* interpreted as error */
 
-	if (ph->needs_swap)
+	if (fd->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)
+static void print_version(struct feat_fd *fd, FILE *fp)
 {
-	char *str = do_read_string(fd, ph);
+	char *str = do_read_string(fd);
 	fprintf(fp, "# perf version : %s\n", str);
 	free(str);
 }
 
-static void print_cmdline(struct perf_header *ph, int fd, FILE *fp)
+static void print_cmdline(struct feat_fd *fd, FILE *fp)
 {
 	ssize_t ret;
 	char *str;
 	u32 nr, i;
 
-	ret = read(fd, &nr, sizeof(nr));
+	ret = do_read(fd, &nr, sizeof(nr));
 	if (ret != (ssize_t)sizeof(nr))
 		return;
 
-	if (ph->needs_swap)
+	if (fd->needs_swap)
 		nr = bswap_32(nr);
 
 	fprintf(fp, "# cmdline : ");
 
 	for (i = 0; i < nr; i++) {
-		str = do_read_string(fd, ph);
+		str = do_read_string(fd);
 		fprintf(fp, "%s ", str);
 		free(str);
 	}
 	fputc('\n', fp);
 }
 
-static void print_cpu_topology(struct perf_header *ph, int fd, FILE *fp)
+static void print_cpu_topology(struct feat_fd *fd, FILE *fp)
 {
 	ssize_t ret;
 	u32 nr, i;
 	char *str;
 
-	ret = read(fd, &nr, sizeof(nr));
+	ret = do_read(fd, &nr, sizeof(nr));
 	if (ret != (ssize_t)sizeof(nr))
 		return;
 
-	if (ph->needs_swap)
+	if (fd->needs_swap)
 		nr = bswap_32(nr);
 
 	for (i = 0; i < nr; i++) {
-		str = do_read_string(fd, ph);
+		str = do_read_string(fd);
 		fprintf(fp, "# sibling cores   : %s\n", str);
 		free(str);
 	}
 
-	ret = read(fd, &nr, sizeof(nr));
+	ret = do_read(fd, &nr, sizeof(nr));
 	if (ret != (ssize_t)sizeof(nr))
 		return;
 
-	if (ph->needs_swap)
+	if (fd->needs_swap)
 		nr = bswap_32(nr);
 
 	for (i = 0; i < nr; i++) {
-		str = do_read_string(fd, ph);
+		str = do_read_string(fd);
 		fprintf(fp, "# sibling threads : %s\n", str);
 		free(str);
 	}
 }
 
-static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
+static void print_event_desc(struct feat_fd *fd, FILE *fp)
 {
 	struct perf_event_attr attr;
 	uint64_t id;
@@ -1155,18 +1198,18 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
 	size_t msz;
 
 	/* number of events */
-	ret = read(fd, &nre, sizeof(nre));
+	ret = do_read(fd, &nre, sizeof(nre));
 	if (ret != (ssize_t)sizeof(nre))
 		goto error;
 
-	if (ph->needs_swap)
+	if (fd->needs_swap)
 		nre = bswap_32(nre);
 
-	ret = read(fd, &sz, sizeof(sz));
+	ret = do_read(fd, &sz, sizeof(sz));
 	if (ret != (ssize_t)sizeof(sz))
 		goto error;
 
-	if (ph->needs_swap)
+	if (fd->needs_swap)
 		sz = bswap_32(sz);
 
 	memset(&attr, 0, sizeof(attr));
@@ -1181,28 +1224,27 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
 		msz = sz;
 
 	for (i = 0 ; i < nre; i++) {
-
 		/*
 		 * must read entire on-file attr struct to
 		 * sync up with layout.
 		 */
-		ret = read(fd, buf, sz);
+		ret = do_read(fd, buf, sz);
 		if (ret != (ssize_t)sz)
 			goto error;
 
-		if (ph->needs_swap)
+		if (fd->needs_swap)
 			perf_event__attr_swap(buf);
 
 		memcpy(&attr, buf, msz);
 
-		ret = read(fd, &nr, sizeof(nr));
+		ret = do_read(fd, &nr, sizeof(nr));
 		if (ret != (ssize_t)sizeof(nr))
 			goto error;
 
-		if (ph->needs_swap)
+		if (fd->needs_swap)
 			nr = bswap_32(nr);
 
-		str = do_read_string(fd, ph);
+		str = do_read_string(fd);
 		fprintf(fp, "# event : name = %s, ", str);
 		free(str);
 
@@ -1221,11 +1263,11 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
 			fprintf(fp, ", id = {");
 
 		for (j = 0 ; j < nr; j++) {
-			ret = read(fd, &id, sizeof(id));
+			ret = do_read(fd, &id, sizeof(id));
 			if (ret != (ssize_t)sizeof(id))
 				goto error;
 
-			if (ph->needs_swap)
+			if (fd->needs_swap)
 				id = bswap_64(id);
 
 			if (j)
@@ -1243,16 +1285,16 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
 	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)
+static void print_total_mem(struct feat_fd *fd, FILE *fp)
 {
 	uint64_t mem;
 	ssize_t ret;
 
-	ret = read(fd, &mem, sizeof(mem));
+	ret = do_read(fd, &mem, sizeof(mem));
 	if (ret != sizeof(mem))
 		goto error;
 
-	if (h->needs_swap)
+	if (fd->needs_swap)
 		mem = bswap_64(mem);
 
 	fprintf(fp, "# total memory : %"PRIu64" kB\n", mem);
@@ -1261,7 +1303,7 @@ static void print_total_mem(struct perf_header *h __used, int fd, FILE *fp)
 	fprintf(fp, "# total memory : unknown\n");
 }
 
-static void print_numa_topology(struct perf_header *h __used, int fd, FILE *fp)
+static void print_numa_topology(struct feat_fd *fd, FILE *fp)
 {
 	ssize_t ret;
 	u32 nr, c, i;
@@ -1269,32 +1311,32 @@ static void print_numa_topology(struct perf_header *h __used, int fd, FILE *fp)
 	uint64_t mem_total, mem_free;
 
 	/* nr nodes */
-	ret = read(fd, &nr, sizeof(nr));
+	ret = do_read(fd, &nr, sizeof(nr));
 	if (ret != (ssize_t)sizeof(nr))
 		goto error;
 
-	if (h->needs_swap)
+	if (fd->needs_swap)
 		nr = bswap_32(nr);
 
 	for (i = 0; i < nr; i++) {
 
 		/* node number */
-		ret = read(fd, &c, sizeof(c));
+		ret = do_read(fd, &c, sizeof(c));
 		if (ret != (ssize_t)sizeof(c))
 			goto error;
 
-		if (h->needs_swap)
+		if (fd->needs_swap)
 			c = bswap_32(c);
 
-		ret = read(fd, &mem_total, sizeof(u64));
+		ret = do_read(fd, &mem_total, sizeof(u64));
 		if (ret != sizeof(u64))
 			goto error;
 
-		ret = read(fd, &mem_free, sizeof(u64));
+		ret = do_read(fd, &mem_free, sizeof(u64));
 		if (ret != sizeof(u64))
 			goto error;
 
-		if (h->needs_swap) {
+		if (fd->needs_swap) {
 			mem_total = bswap_64(mem_total);
 			mem_free = bswap_64(mem_free);
 		}
@@ -1305,7 +1347,7 @@ static void print_numa_topology(struct perf_header *h __used, int fd, FILE *fp)
 			mem_total,
 			mem_free);
 
-		str = do_read_string(fd, h);
+		str = do_read_string(fd);
 		fprintf(fp, "# node%u cpu list : %s\n", c, str);
 		free(str);
 	}
@@ -1314,15 +1356,14 @@ static void print_numa_topology(struct perf_header *h __used, int fd, FILE *fp)
 	fprintf(fp, "# numa topology : not available\n");
 }
 
-static void print_cpuid(struct perf_header *ph, int fd, FILE *fp)
+static void print_cpuid(struct feat_fd *fd, FILE *fp)
 {
-	char *str = do_read_string(fd, ph);
+	char *str = do_read_string(fd);
 	fprintf(fp, "# cpuid : %s\n", str);
 	free(str);
 }
 
-static void print_branch_stack(struct perf_header *ph __used, int fd __used,
-			       FILE *fp)
+static void print_branch_stack(struct feat_fd *fd __used, FILE *fp)
 {
 	fprintf(fp, "# contains samples with branch stack\n");
 }
@@ -1495,43 +1536,88 @@ static int process_build_id(struct perf_file_section *section,
 }
 
 struct feature_ops {
-	int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist);
-	void (*print)(struct perf_header *h, int fd, FILE *fp);
+	int (*write)(struct feat_fd *fd, struct perf_evlist *evlist);
+	void (*print)(struct feat_fd *fd, FILE *fp);
 	int (*process)(struct perf_file_section *section,
 		       struct perf_header *h, int feat, int fd);
 	const char *name;
 	bool full_only;
+	int record_type;
 };
 
-#define FEAT_OPA(n, func) \
-	[n] = { .name = #n, .write = write_##func, .print = print_##func }
-#define FEAT_OPP(n, func) \
-	[n] = { .name = #n, .write = write_##func, .print = print_##func, \
-		.process = process_##func }
-#define FEAT_OPF(n, func) \
-	[n] = { .name = #n, .write = write_##func, .print = print_##func, \
-		.full_only = true }
+#define FEAT_OPA(n, func)				\
+	[HEADER_##n] = { .name = #n,			\
+		.write = write_##func,			\
+		.print = print_##func,			\
+		.full_only = false,			\
+		.record_type = PERF_RECORD_HEADER_##n	\
+	}
+
+#define FEAT_OPB(n, func)                               \
+	[HEADER_##n] = { .name = #n,			\
+		.write = write_##func,			\
+		.print = print_##func,			\
+		.full_only = false,			\
+	}
+
+#define FEAT_OPP(n, func)				\
+	[HEADER_##n] = { .name = #n,			\
+		.write = write_##func,			\
+		.print = print_##func,			\
+		.process = process_##func,		\
+		.record_type = PERF_RECORD_HEADER_##n	\
+	}
+
+#define FEAT_OPF(n, func)				\
+	[HEADER_##n] = { .name = #n,			\
+		.write = write_##func,			\
+		.print = print_##func,			\
+		.full_only = true,			\
+		.record_type = PERF_RECORD_HEADER_##n	\
+	}
 
 /* feature_ops not implemented: */
 #define print_tracing_data	NULL
 #define print_build_id		NULL
 
 static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
-	FEAT_OPP(HEADER_TRACING_DATA,	tracing_data),
-	FEAT_OPP(HEADER_BUILD_ID,	build_id),
-	FEAT_OPA(HEADER_HOSTNAME,	hostname),
-	FEAT_OPA(HEADER_OSRELEASE,	osrelease),
-	FEAT_OPA(HEADER_VERSION,	version),
-	FEAT_OPA(HEADER_ARCH,		arch),
-	FEAT_OPA(HEADER_NRCPUS,		nrcpus),
-	FEAT_OPA(HEADER_CPUDESC,	cpudesc),
-	FEAT_OPA(HEADER_CPUID,		cpuid),
-	FEAT_OPA(HEADER_TOTAL_MEM,	total_mem),
-	FEAT_OPA(HEADER_EVENT_DESC,	event_desc),
-	FEAT_OPA(HEADER_CMDLINE,	cmdline),
-	FEAT_OPF(HEADER_CPU_TOPOLOGY,	cpu_topology),
-	FEAT_OPF(HEADER_NUMA_TOPOLOGY,	numa_topology),
-	FEAT_OPA(HEADER_BRANCH_STACK,	branch_stack),
+	FEAT_OPP(TRACING_DATA,	tracing_data),
+	FEAT_OPP(BUILD_ID,	build_id),
+	FEAT_OPA(HOSTNAME,	hostname),
+	FEAT_OPA(OSRELEASE,	osrelease),
+	FEAT_OPA(VERSION,	version),
+	FEAT_OPA(ARCH,		arch),
+	FEAT_OPA(NRCPUS,	nrcpus),
+	FEAT_OPA(CPUDESC,	cpudesc),
+	FEAT_OPA(CPUID,		cpuid),
+	FEAT_OPA(TOTAL_MEM,	total_mem),
+	FEAT_OPA(EVENT_DESC,	event_desc),
+	FEAT_OPA(CMDLINE,	cmdline),
+	FEAT_OPF(CPU_TOPOLOGY,	cpu_topology),
+	FEAT_OPF(NUMA_TOPOLOGY,	numa_topology),
+	FEAT_OPB(BRANCH_STACK,	branch_stack),
+};
+
+/*
+ * we use a mapping table to go from record type to feature header
+ * because we have no guarantee that new record types may not be added
+ * after the feature header.
+ */
+#define REC2FEAT(a)	[PERF_RECORD_HEADER_##a] = HEADER_##a
+
+static const int rec2feat[PERF_RECORD_HEADER_MAX] = {
+	REC2FEAT(HOSTNAME),
+	REC2FEAT(OSRELEASE),
+	REC2FEAT(VERSION),
+	REC2FEAT(ARCH),
+	REC2FEAT(NRCPUS),
+	REC2FEAT(CPUDESC),
+	REC2FEAT(CPUID),
+	REC2FEAT(TOTAL_MEM),
+	REC2FEAT(EVENT_DESC),
+	REC2FEAT(CMDLINE),
+	REC2FEAT(CPU_TOPOLOGY),
+	REC2FEAT(NUMA_TOPOLOGY),
 };
 
 struct header_print_data {
@@ -1544,6 +1630,7 @@ static int perf_file_section__fprintf_info(struct perf_file_section *section,
 					   int feat, int fd, void *data)
 {
 	struct header_print_data *hd = data;
+	struct feat_fd fdd;
 
 	if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) {
 		pr_debug("Failed to lseek to %" PRIu64 " offset for feature "
@@ -1557,8 +1644,19 @@ static int perf_file_section__fprintf_info(struct perf_file_section *section,
 	if (!feat_ops[feat].print)
 		return 0;
 
+	memset(&fdd, 0, sizeof(fdd));
+	fdd.fd = fd;
+	fdd.hdr = ph;
+	fdd.needs_swap = ph->needs_swap;
+
+	/*
+	 * propagate endianess setting to fdd
+	 * in pipe-mode there is no perf_header
+	 */
+	fdd.needs_swap = ph->needs_swap;
+
 	if (!feat_ops[feat].full_only || hd->full)
-		feat_ops[feat].print(ph, fd, hd->fp);
+		feat_ops[feat].print(&fdd, hd->fp);
 	else
 		fprintf(hd->fp, "# %s info available, use -I to display\n",
 			feat_ops[feat].name);
@@ -1579,44 +1677,51 @@ int perf_header__fprintf_info(struct perf_session *session, FILE *fp, bool full)
 	return 0;
 }
 
-static int do_write_feat(int fd, struct perf_header *h, int type,
+static int do_write_feat(struct feat_fd *fdd, int type,
 			 struct perf_file_section **p,
 			 struct perf_evlist *evlist)
 {
 	int err;
-	int ret = 0;
 
-	if (perf_header__has_feat(h, type)) {
+	if (perf_header__has_feat(fdd->hdr, type)) {
 		if (!feat_ops[type].write)
 			return -1;
 
-		(*p)->offset = lseek(fd, 0, SEEK_CUR);
+		(*p)->offset = lseek(fdd->fd, 0, SEEK_CUR);
 
-		err = feat_ops[type].write(fd, h, evlist);
+		err = feat_ops[type].write(fdd, evlist);
 		if (err < 0) {
 			pr_debug("failed to write feature %d\n", type);
-
-			/* undo anything written */
-			lseek(fd, (*p)->offset, SEEK_SET);
-
+			lseek(fdd->fd, (*p)->offset, SEEK_SET);
 			return -1;
+
 		}
-		(*p)->size = lseek(fd, 0, SEEK_CUR) - (*p)->offset;
+		(*p)->size = lseek(fdd->fd, 0, SEEK_CUR) - (*p)->offset;
 		(*p)++;
 	}
-	return ret;
+	return 0;
 }
 
 static int perf_header__adds_write(struct perf_header *header,
 				   struct perf_evlist *evlist, int fd)
 {
 	int nr_sections;
+	struct feat_fd fdd;
 	struct perf_file_section *feat_sec, *p;
 	int sec_size;
 	u64 sec_start;
 	int feat;
 	int err;
 
+	/*
+	 * may write more than needed due to dropped feature, but
+	 * this is okay, reader will skip the mising entries
+	 */
+	memset(&fdd, 0, sizeof(fdd));
+	fdd.fd  = fd;
+	fdd.hdr = header;
+	fdd.needs_swap = header->needs_swap;
+
 	nr_sections = bitmap_weight(header->adds_features, HEADER_FEAT_BITS);
 	if (!nr_sections)
 		return 0;
@@ -1631,38 +1736,40 @@ static int perf_header__adds_write(struct perf_header *header,
 	lseek(fd, sec_start + sec_size, SEEK_SET);
 
 	for_each_set_bit(feat, header->adds_features, HEADER_FEAT_BITS) {
-		if (do_write_feat(fd, header, feat, &p, evlist))
+		if (do_write_feat(&fdd, feat, &p, evlist))
 			perf_header__clear_feat(header, feat);
 	}
 
 	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);
+
+	err = do_write(&fdd, feat_sec, sec_size);
 	if (err < 0)
 		pr_debug("failed to write feature section\n");
+
 	free(feat_sec);
+
 	return err;
 }
 
 int perf_header__write_pipe(int fd)
 {
 	struct perf_pipe_file_header f_header;
+	struct feat_fd fdd;
 	int err;
 
+	memset(&fdd, 0, sizeof(fdd));
+	fdd.fd = fd;
+
 	f_header = (struct perf_pipe_file_header){
 		.magic	   = PERF_MAGIC,
 		.size	   = sizeof(f_header),
 	};
 
-	err = do_write(fd, &f_header, sizeof(f_header));
+	err = do_write(&fdd, &f_header, sizeof(f_header));
 	if (err < 0) {
 		pr_debug("failed to write perf pipe header\n");
 		return err;
 	}
-
 	return 0;
 }
 
@@ -1670,12 +1777,16 @@ int perf_session__write_header(struct perf_session *session,
 			       struct perf_evlist *evlist,
 			       int fd, bool at_exit)
 {
+	struct feat_fd fdd;
 	struct perf_file_header f_header;
 	struct perf_file_attr   f_attr;
 	struct perf_header *header = &session->header;
 	struct perf_evsel *attr, *pair = NULL;
 	int err;
 
+	memset(&fdd, 0, sizeof(fdd));
+	fdd.fd = fd;
+
 	lseek(fd, sizeof(f_header), SEEK_SET);
 
 	if (session->evlist != evlist)
@@ -1683,14 +1794,14 @@ int perf_session__write_header(struct perf_session *session,
 
 	list_for_each_entry(attr, &evlist->entries, node) {
 		attr->id_offset = lseek(fd, 0, SEEK_CUR);
-		err = do_write(fd, attr->id, attr->ids * sizeof(u64));
+		err = do_write(&fdd, attr->id, attr->ids * sizeof(u64));
 		if (err < 0) {
 out_err_write:
 			pr_debug("failed to write perf header\n");
 			return err;
 		}
 		if (session->evlist != evlist) {
-			err = do_write(fd, pair->id, pair->ids * sizeof(u64));
+			err = do_write(&fdd, pair->id, pair->ids * sizeof(u64));
 			if (err < 0)
 				goto out_err_write;
 			attr->ids += pair->ids;
@@ -1708,7 +1819,7 @@ int perf_session__write_header(struct perf_session *session,
 				.size   = attr->ids * sizeof(u64),
 			}
 		};
-		err = do_write(fd, &f_attr, sizeof(f_attr));
+		err = do_write(&fdd, &f_attr, sizeof(f_attr));
 		if (err < 0) {
 			pr_debug("failed to write perf header attribute\n");
 			return err;
@@ -1718,7 +1829,7 @@ int perf_session__write_header(struct perf_session *session,
 	header->event_offset = lseek(fd, 0, SEEK_CUR);
 	header->event_size = event_count * sizeof(struct perf_trace_event_type);
 	if (events) {
-		err = do_write(fd, events, header->event_size);
+		err = do_write(&fdd, events, header->event_size);
 		if (err < 0) {
 			pr_debug("failed to write perf header events\n");
 			return err;
@@ -1754,7 +1865,7 @@ int perf_session__write_header(struct perf_session *session,
 	memcpy(&f_header.adds_features, &header->adds_features, sizeof(header->adds_features));
 
 	lseek(fd, 0, SEEK_SET);
-	err = do_write(fd, &f_header, sizeof(f_header));
+	err = do_write(&fdd, &f_header, sizeof(f_header));
 	if (err < 0) {
 		pr_debug("failed to write perf header\n");
 		return err;
@@ -2014,8 +2125,13 @@ static int perf_file_header__read_pipe(struct perf_pipe_file_header *header,
 				       struct perf_header *ph, int fd,
 				       bool repipe)
 {
+	struct feat_fd fdd;
 	int ret;
 
+	memset(&fdd, 0, sizeof(fdd));
+	fdd.fd  = STDOUT_FILENO;
+	fdd.hdr = ph;
+
 	ret = readn(fd, header, sizeof(*header));
 	if (ret <= 0)
 		return -1;
@@ -2028,7 +2144,7 @@ static int perf_file_header__read_pipe(struct perf_pipe_file_header *header,
 	if (ph->needs_swap)
 		header->size = bswap_64(header->size);
 
-	if (repipe && do_write(STDOUT_FILENO, header, sizeof(*header)) < 0)
+	if (repipe && do_write(&fdd, header, sizeof(*header)) < 0)
 		return -1;
 
 	return 0;
@@ -2323,6 +2439,58 @@ int perf_event__synthesize_event_types(struct perf_tool *tool,
 	return err;
 }
 
+int perf_event__synthesize_features(struct perf_tool *tool,
+				    struct perf_session *session,
+				    struct perf_evlist *evlist,
+				    perf_event__handler_t process)
+{
+	struct perf_header *header = &session->header;
+	struct feat_fd fdd;
+	struct feature_event *fe;
+	size_t sz, sz_hdr;
+	int feat, ret;
+
+	sz_hdr = sizeof(fe->header);
+	sz = sizeof(union perf_event);
+	/* get a nice alignment */
+	sz = ALIGN(sz, getpagesize());
+
+	memset(&fdd, 0, sizeof(fdd));
+
+	fdd.buf = malloc(sz);
+	if (!fdd.buf)
+		return -1;
+
+	fdd.size = sz - sz_hdr;
+
+	for_each_set_bit(feat, header->adds_features, HEADER_FEAT_BITS) {
+		/*
+		 * those are already written as:
+		 * - PERF_RECORD_HEADER_TRACING_DATA
+		 * - PERF_RECORD_HEADER_BUILD_ID
+		 */
+		if (feat == HEADER_TRACING_DATA || feat == HEADER_BUILD_ID)
+			continue;
+
+		fdd.pos = sizeof(*fe);
+
+		ret = feat_ops[feat].write(&fdd, evlist);
+		if (ret || fdd.pos == sizeof(*fe))
+			continue;
+
+		/* fdd.buf may change due to realloc in do_write() */
+		fe = fdd.buf;
+		memset(fe, 0, sizeof(*fe));
+
+		fe->header.type = feat_ops[feat].record_type;
+		fe->header.size = fdd.pos;
+
+		process(tool, fdd.buf, NULL, NULL);
+	}
+	free(fdd.buf);
+	return 0;
+}
+
 int perf_event__process_event_type(struct perf_tool *tool __unused,
 				   union perf_event *event)
 {
@@ -2338,6 +2506,7 @@ int perf_event__synthesize_tracing_data(struct perf_tool *tool, int fd,
 					perf_event__handler_t process)
 {
 	union perf_event ev;
+	struct feat_fd fdd;
 	struct tracing_data *tdata;
 	ssize_t size = 0, aligned_size = 0, padding;
 	int err __used = 0;
@@ -2374,7 +2543,10 @@ int perf_event__synthesize_tracing_data(struct perf_tool *tool, int fd,
 	 */
 	tracing_data_put(tdata);
 
-	write_padded(fd, NULL, 0, padding);
+	memset(&fdd, 0, sizeof(fdd));
+	fdd.fd = fd;
+
+	write_padded(&fdd, NULL, 0, padding);
 
 	return aligned_size;
 }
@@ -2446,6 +2618,52 @@ int perf_event__process_build_id(struct perf_tool *tool __used,
 	return 0;
 }
 
+int perf_event__process_feature(struct perf_tool *tool,
+				union perf_event *event,
+				 struct perf_session *session __used)
+{
+	struct feat_fd fdd;
+	int type = event->header.type;
+	int feat;
+
+	if (type < 0 || type >= PERF_RECORD_HEADER_MAX) {
+		pr_warning("invalid record type %d\n", type);
+		return 0;
+	}
+	/*
+	 * unhandled feature
+	 */
+	feat = rec2feat[type];
+	if (feat == 0)
+		return 0;
+
+	/*
+	 * no priunt routine
+	 */
+	if (!feat_ops[feat].print)
+		return 0;
+
+	memset(&fdd, 0, sizeof(fdd));
+	fdd.buf  = (void *)event;
+	fdd.buf += sizeof(event->header);
+	fdd.size = event->header.size - sizeof(event->header);
+	/*
+	 * have to assume endianess match for now
+	 * as we do not have the info coming from the header.
+	 *
+	 * Once header is updated, we'll use the info here
+	 */
+	fdd.needs_swap = 0;
+
+	if (!feat_ops[feat].full_only || tool->show_all_features)
+		feat_ops[feat].print(&fdd, stdout);
+	else
+		fprintf(stdout, "# %s info available, use -I to display\n",
+			feat_ops[feat].name);
+
+	return 0;
+}
+
 void disable_buildid_cache(void)
 {
 	no_buildid_cache = true;
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
index 2d42b3e..af4db9e 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -98,6 +98,15 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
 			  const char *name, bool is_kallsyms);
 int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir);
 
+int perf_event__synthesize_features(struct perf_tool *tool,
+				    struct perf_session *session,
+				    struct perf_evlist *evlist,
+				    perf_event__handler_t process);
+
+int perf_event__process_feature(struct perf_tool *tool,
+				union perf_event *event,
+				 struct perf_session *session);
+
 int perf_event__synthesize_attr(struct perf_tool *tool,
 				struct perf_event_attr *attr, u16 ids, u64 *id,
 				perf_event__handler_t process);
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index d13e915..84012fa 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -390,6 +390,14 @@ static int process_event_type_stub(struct perf_tool *tool __used,
 	return 0;
 }
 
+static int process_feature_stub(struct perf_tool *tool __used,
+				union perf_event *event __used,
+				struct perf_session *session __used)
+{
+	dump_printf(": unhandled!\n");
+	return 0;
+}
+
 static int process_finished_round(struct perf_tool *tool,
 				  union perf_event *event,
 				  struct perf_session *session);
@@ -428,6 +436,8 @@ static void perf_tool__fill_defaults(struct perf_tool *tool)
 		else
 			tool->finished_round = process_finished_round_stub;
 	}
+	if (tool->feature == NULL)
+		tool->feature = process_feature_stub;
 }
 
 void mem_bswap_64(void *src, int byte_size)
@@ -530,8 +540,8 @@ static perf_event__swap_op perf_event__swap_ops[] = {
 	[PERF_RECORD_SAMPLE]		  = perf_event__all64_swap,
 	[PERF_RECORD_HEADER_ATTR]	  = perf_event__hdr_attr_swap,
 	[PERF_RECORD_HEADER_EVENT_TYPE]	  = perf_event__event_type_swap,
-	[PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap,
 	[PERF_RECORD_HEADER_BUILD_ID]	  = NULL,
+	[PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap,
 	[PERF_RECORD_HEADER_MAX]	  = NULL,
 };
 
@@ -939,6 +949,8 @@ static int perf_session__process_user_event(struct perf_session *session, union
 		return tool->build_id(tool, event, session);
 	case PERF_RECORD_FINISHED_ROUND:
 		return tool->finished_round(tool, event, session);
+	case PERF_RECORD_HEADER_HOSTNAME ... PERF_RECORD_HEADER_NUMA_TOPOLOGY:
+		return tool->feature(tool, event, session);
 	default:
 		return -EINVAL;
 	}
@@ -1514,6 +1526,7 @@ void perf_session__fprintf_info(struct perf_session *session, FILE *fp,
 
 	fprintf(fp, "# ========\n");
 	fprintf(fp, "# captured on: %s", ctime(&st.st_ctime));
-	perf_header__fprintf_info(session, fp, full);
 	fprintf(fp, "# ========\n#\n");
+	if (!session->fd_pipe)
+		perf_header__fprintf_info(session, fp, full);
 }
diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h
index b0e1aad..af16af1f 100644
--- a/tools/perf/util/tool.h
+++ b/tools/perf/util/tool.h
@@ -42,9 +42,11 @@ struct perf_tool {
 	event_synth_op	tracing_data;
 	event_simple_op	event_type;
 	event_op2	finished_round,
-			build_id;
+			build_id,
+			feature;
 	bool		ordered_samples;
 	bool		ordering_requires_timestamps;
+	bool		show_all_features;
 };
 
 #endif /* __PERF_TOOL_H */
-- 
1.7.4.1


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

* [PATCH 5/5] perf: make perf buildid-list work better with pipe mode
  2012-04-11  9:01 [PATCH 0/5] perf tools: add meta-data header support in pipe mode Stephane Eranian
                   ` (3 preceding siblings ...)
  2012-04-11  9:01 ` [PATCH 4/5] perf record: add meta-data support for pipe-mode Stephane Eranian
@ 2012-04-11  9:01 ` Stephane Eranian
  2012-04-26 15:14 ` [PATCH 0/5] perf tools: add meta-data header support in " Stephane Eranian
  5 siblings, 0 replies; 9+ messages in thread
From: Stephane Eranian @ 2012-04-11  9:01 UTC (permalink / raw)
  To: linux-kernel; +Cc: peterz, mingo, acme, dsahern, namhyung.kim

In order for perf buildid-list to work with pipe-mode files,
it needs to process buildids and event attr structs.

$ perf record -o - noploop 2 | ./perf inject -b | perf buildid-list -i - -H
noploop for 2 seconds
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.084 MB - (~3678 samples) ]
0000000000000000000000000000000000000000 [kernel.kallsyms]
3a0d0629efe74a8da3eeba372cdbd74ad9b8f5d5 /usr/local/bin/noploop

The reason [kernel.kallsyms] shows a 0 build-id comes from the
way buildids are injected in the stream. The buildid for the kernel
is provided by a BUILD_ID record. The [kernel.kallsyms] is provided
by a MMAP record. There is no clean and obvious way to link the two,
unfortunately. In regular mode, the kernel buildid is generated from
reading the ELF image or kallsyms and perf knows to associate
[kernel.kallsyms] to it. Later on, when perf processes the
[kernel.kallsyms] MMAP record, it will already have a dso for it.

So for now, make sure perf buildid-list shows the buildids for
everything but the kernel image.

Signed-off-by: Stephane Eranian <eranian@google.com>
---
 tools/perf/builtin-buildid-list.c |    6 +++++-
 tools/perf/util/build-id.c        |    2 ++
 2 files changed, 7 insertions(+), 1 deletions(-)

diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c
index 5248046..6b2bcfb 100644
--- a/tools/perf/builtin-buildid-list.c
+++ b/tools/perf/builtin-buildid-list.c
@@ -84,7 +84,11 @@ static int perf_session__list_build_ids(void)
 	if (filename__fprintf_build_id(session->filename, stdout))
 		goto out;
 
-	if (with_hits)
+	/*
+	 * in pipe-mode, the only way to get the buildids is to parse
+	 * the record stream. Buildids are stored as RECORD_HEADER_BUILD_ID
+	 */
+	if (with_hits || session->fd_pipe)
 		perf_session__process_events(session, &build_id__mark_dso_hit_ops);
 
 	perf_session__fprintf_dsos_buildid(session, stdout, with_hits);
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
index dff9c7a..fd9a594 100644
--- a/tools/perf/util/build-id.c
+++ b/tools/perf/util/build-id.c
@@ -65,6 +65,8 @@ struct perf_tool build_id__mark_dso_hit_ops = {
 	.mmap	= perf_event__process_mmap,
 	.fork	= perf_event__process_task,
 	.exit	= perf_event__exit_del_thread,
+	.attr		 = perf_event__process_attr,
+	.build_id	 = perf_event__process_build_id,
 };
 
 char *dso__build_id_filename(struct dso *self, char *bf, size_t size)
-- 
1.7.4.1


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

* Re: [PATCH 0/5] perf tools: add meta-data header support in pipe mode
  2012-04-11  9:01 [PATCH 0/5] perf tools: add meta-data header support in pipe mode Stephane Eranian
                   ` (4 preceding siblings ...)
  2012-04-11  9:01 ` [PATCH 5/5] perf: make perf buildid-list work better with pipe mode Stephane Eranian
@ 2012-04-26 15:14 ` Stephane Eranian
  5 siblings, 0 replies; 9+ messages in thread
From: Stephane Eranian @ 2012-04-26 15:14 UTC (permalink / raw)
  To: linux-kernel; +Cc: peterz, mingo, acme, dsahern, namhyung.kim

Arnaldo,

Any comment on this series?

On Wed, Apr 11, 2012 at 11:01 AM, Stephane Eranian <eranian@google.com> wrote:
> This patch series adds meta-data support to perf record/report in pipe
> mode:
>        perf record -o - foo | perf inject -b | perf report -i -
>
> We had meta-data (information about host configuration, perf tool version,...)
> in regular (file) mode but it was lacking in pipe mode. This patch series fixes
> this.
>
> In pipe mode, there is no meta-data header structures at the beginning of the
> streamed data given we cannot seek in a pipe. Instead, we need to create pseudo
> record types for each of the possible features, e.g., hostname, cpuid, event_desc,
> and so on. Those pseudo records are guaranteed to be before any actual sample
> records, therefore perf report/annotate are guaranteed to get the information
> they need before processing the first sample.
>
> The series also fixes perf inject to actually inject build-ids and buildid-list
> to work better with pipe mode, i.e., print the build-ids.
>
> With this series:
>
>  $ perf record -o - noploop 2 | perf inject -b | perf report -i -
>      # ========
>      # captured on: Fri Jan 20 18:13:55 2012
>      # ========
>      #
>      # hostname : quad
>      # os release : 3.2.0-rc7-tip
>      # perf version : 3.2.0
>      # arch : x86_64
>      # nrcpus online : 4
>      # nrcpus avail : 4
>      # cpudesc : Intel(R) Core(TM)2 Quad CPU Q6600 @ 2.40GHz
>      # cpuid : GenuineIntel,6,15,11
>      # total memory : 8092884 kB
>      ...
>      # HEADER_CPU_TOPOLOGY info available, use -I to display
>      noploop for 2 seconds
>      [ perf record: Woken up 1 times to write data ]
>      [ perf record: Captured and wrote 0.084 MB - (~3677 samples) ]
>        99.80%  noploop  noploop            [.] noploop
>         0.19%  noploop  [kernel.kallsyms]  [k] radix_tree_gang_lookup
>
> Signed-off-by: Stephane Eranian <eranian@google.com>
>
> Stephane Eranian (5):
>  perf inject: fix broken perf inject -b
>  perf tools: fix piped mode read code
>  perf tools: rename HEADER_TRACE_INFO to HEADER_TRACING_DATA
>  perf record: add meta-data support for pipe-mode
>  perf: make perf buildid-list work better with pipe mode
>
>  tools/perf/builtin-annotate.c     |    5 +
>  tools/perf/builtin-buildid-list.c |    6 +-
>  tools/perf/builtin-inject.c       |    6 +
>  tools/perf/builtin-record.c       |   11 +-
>  tools/perf/builtin-report.c       |    7 +-
>  tools/perf/util/build-id.c        |    2 +
>  tools/perf/util/event.c           |   12 +
>  tools/perf/util/event.h           |   18 ++
>  tools/perf/util/header.c          |  538 ++++++++++++++++++++++++++-----------
>  tools/perf/util/header.h          |   11 +-
>  tools/perf/util/session.c         |   52 +++-
>  tools/perf/util/tool.h            |    4 +-
>  12 files changed, 494 insertions(+), 178 deletions(-)
>
> --
> 1.7.4.1
>

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

* Re: [PATCH 2/5] perf tools: fix piped mode read code
  2012-04-11  9:01 ` [PATCH 2/5] perf tools: fix piped mode read code Stephane Eranian
@ 2012-05-11  0:11   ` David Ahern
  2012-05-14 20:24     ` Stephane Eranian
  0 siblings, 1 reply; 9+ messages in thread
From: David Ahern @ 2012-05-11  0:11 UTC (permalink / raw)
  To: Stephane Eranian; +Cc: linux-kernel, peterz, mingo, acme, namhyung.kim

Hi Stephane:

This patch no longer applies cleanly. Can you update the series?

David

On 4/11/12 3:01 AM, Stephane Eranian wrote:
> In __perf_session__process_pipe_events(), there was a risk
> we would read more than what a union perf_event struct can
> hold. this could happen in case, perf is reading a file which
> contains new record types it does not know about and which are
> larger than anything it knows about.
>
> In general, perf is supposed to skip records it does not
> understand, but in pipe mode, those have to be read and ignored.
> The fixed size header contains the size of the record, but that
> size may be larger than union perf_event, yet it was used as
> the backing to the read in:
>
>    union perf_event event;
>    void *p;
>
>    size = event->header.size;
>
>    p =&event;
>    p += sizeof(struct perf_event_header);
>    if (size - sizeof(struct perf_event_header)) {
>      err = readn(self->fd, p, size - sizeof(struct perf_event_header));
>
> We fix this by allocating a buffer based on the size reported in
> the header. We reuse the buffer as much as we can. We realloc in
> case it becomes too small. In the  common case, the performance
> impact is negligible.
>
> Signed-off-by: Stephane Eranian<eranian@google.com>
> ---
>   tools/perf/util/session.c |   35 +++++++++++++++++++++++++++--------
>   1 files changed, 27 insertions(+), 8 deletions(-)
>
> diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
> index 9412e3b..d13e915 100644
> --- a/tools/perf/util/session.c
> +++ b/tools/perf/util/session.c
> @@ -1056,8 +1056,9 @@ volatile int session_done;
>   static int __perf_session__process_pipe_events(struct perf_session *self,
>   					       struct perf_tool *tool)
>   {
> -	union perf_event event;
> -	uint32_t size;
> +	union perf_event *event;
> +	uint32_t size, cur_size = 0;
> +	void *buf = NULL;
>   	int skip = 0;
>   	u64 head;
>   	int err;
> @@ -1066,8 +1067,14 @@ static int __perf_session__process_pipe_events(struct perf_session *self,
>   	perf_tool__fill_defaults(tool);
>
>   	head = 0;
> +	cur_size = sizeof(union perf_event);
> +
> +	buf = malloc(cur_size);
> +	if (!buf)
> +		return -errno;
>   more:
> -	err = readn(self->fd,&event, sizeof(struct perf_event_header));
> +	event = buf;
> +	err = readn(self->fd, event, sizeof(struct perf_event_header));
>   	if (err<= 0) {
>   		if (err == 0)
>   			goto done;
> @@ -1077,13 +1084,23 @@ static int __perf_session__process_pipe_events(struct perf_session *self,
>   	}
>
>   	if (self->header.needs_swap)
> -		perf_event_header__bswap(&event.header);
> +		perf_event_header__bswap(&event->header);
>
> -	size = event.header.size;
> +	size = event->header.size;
>   	if (size == 0)
>   		size = 8;
>
> -	p =&event;
> +	if (size>  cur_size) {
> +		void *new = realloc(buf, size);
> +		if (!new) {
> +			pr_err("failed to allocate memory to read event\n");
> +			goto out_err;
> +		}
> +		buf = new;
> +		cur_size = size;
> +		event = buf;
> +	}
> +	p = event;
>   	p += sizeof(struct perf_event_header);
>
>   	if (size - sizeof(struct perf_event_header)) {
> @@ -1099,9 +1116,10 @@ static int __perf_session__process_pipe_events(struct perf_session *self,
>   		}
>   	}
>
> -	if ((skip = perf_session__process_event(self,&event, tool, head))<  0) {
> +	skip = perf_session__process_event(self, event, tool, head);
> +	if (skip<  0) {
>   		dump_printf("%#" PRIx64 " [%#x]: skipping unknown header type: %d\n",
> -			    head, event.header.size, event.header.type);
> +			    head, event->header.size, event->header.type);
>   		/*
>   		 * assume we lost track of the stream, check alignment, and
>   		 * increment a single u64 in the hope to catch on again 'soon'.
> @@ -1122,6 +1140,7 @@ static int __perf_session__process_pipe_events(struct perf_session *self,
>   done:
>   	err = 0;
>   out_err:
> +	free(buf);
>   	perf_session__warn_about_errors(self, tool);
>   	perf_session_free_sample_buffers(self);
>   	return err;


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

* Re: [PATCH 2/5] perf tools: fix piped mode read code
  2012-05-11  0:11   ` David Ahern
@ 2012-05-14 20:24     ` Stephane Eranian
  0 siblings, 0 replies; 9+ messages in thread
From: Stephane Eranian @ 2012-05-14 20:24 UTC (permalink / raw)
  To: David Ahern; +Cc: linux-kernel, peterz, mingo, acme, namhyung.kim

On Fri, May 11, 2012 at 2:11 AM, David Ahern <dsahern@gmail.com> wrote:
> Hi Stephane:
>
> This patch no longer applies cleanly. Can you update the series?
>
Will post the series again tomorrow.
Arnaldo, you need to take a look at this, it's been posted weeks ago.
We need this patch to make perf  in pipe mode behave like perf in
file mode.
Thanks
> David
>
>
> On 4/11/12 3:01 AM, Stephane Eranian wrote:
>>
>> In __perf_session__process_pipe_events(), there was a risk
>> we would read more than what a union perf_event struct can
>> hold. this could happen in case, perf is reading a file which
>> contains new record types it does not know about and which are
>> larger than anything it knows about.
>>
>> In general, perf is supposed to skip records it does not
>> understand, but in pipe mode, those have to be read and ignored.
>> The fixed size header contains the size of the record, but that
>> size may be larger than union perf_event, yet it was used as
>> the backing to the read in:
>>
>>   union perf_event event;
>>   void *p;
>>
>>   size = event->header.size;
>>
>>   p =&event;
>>   p += sizeof(struct perf_event_header);
>>   if (size - sizeof(struct perf_event_header)) {
>>     err = readn(self->fd, p, size - sizeof(struct perf_event_header));
>>
>> We fix this by allocating a buffer based on the size reported in
>> the header. We reuse the buffer as much as we can. We realloc in
>> case it becomes too small. In the  common case, the performance
>> impact is negligible.
>>
>> Signed-off-by: Stephane Eranian<eranian@google.com>
>> ---
>>  tools/perf/util/session.c |   35 +++++++++++++++++++++++++++--------
>>  1 files changed, 27 insertions(+), 8 deletions(-)
>>
>> diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
>> index 9412e3b..d13e915 100644
>> --- a/tools/perf/util/session.c
>> +++ b/tools/perf/util/session.c
>> @@ -1056,8 +1056,9 @@ volatile int session_done;
>>  static int __perf_session__process_pipe_events(struct perf_session *self,
>>                                               struct perf_tool *tool)
>>  {
>> -       union perf_event event;
>> -       uint32_t size;
>> +       union perf_event *event;
>> +       uint32_t size, cur_size = 0;
>> +       void *buf = NULL;
>>        int skip = 0;
>>        u64 head;
>>        int err;
>> @@ -1066,8 +1067,14 @@ static int
>> __perf_session__process_pipe_events(struct perf_session *self,
>>        perf_tool__fill_defaults(tool);
>>
>>        head = 0;
>> +       cur_size = sizeof(union perf_event);
>> +
>> +       buf = malloc(cur_size);
>> +       if (!buf)
>> +               return -errno;
>>  more:
>> -       err = readn(self->fd,&event, sizeof(struct perf_event_header));
>>
>> +       event = buf;
>> +       err = readn(self->fd, event, sizeof(struct perf_event_header));
>>        if (err<= 0) {
>>                if (err == 0)
>>                        goto done;
>> @@ -1077,13 +1084,23 @@ static int
>> __perf_session__process_pipe_events(struct perf_session *self,
>>        }
>>
>>        if (self->header.needs_swap)
>> -               perf_event_header__bswap(&event.header);
>> +               perf_event_header__bswap(&event->header);
>>
>> -       size = event.header.size;
>> +       size = event->header.size;
>>        if (size == 0)
>>                size = 8;
>>
>> -       p =&event;
>> +       if (size>  cur_size) {
>> +               void *new = realloc(buf, size);
>> +               if (!new) {
>> +                       pr_err("failed to allocate memory to read
>> event\n");
>> +                       goto out_err;
>> +               }
>> +               buf = new;
>> +               cur_size = size;
>> +               event = buf;
>> +       }
>> +       p = event;
>>        p += sizeof(struct perf_event_header);
>>
>>        if (size - sizeof(struct perf_event_header)) {
>> @@ -1099,9 +1116,10 @@ static int
>> __perf_session__process_pipe_events(struct perf_session *self,
>>                }
>>        }
>>
>> -       if ((skip = perf_session__process_event(self,&event, tool, head))<
>>  0) {
>>
>> +       skip = perf_session__process_event(self, event, tool, head);
>> +       if (skip<  0) {
>>                dump_printf("%#" PRIx64 " [%#x]: skipping unknown header
>> type: %d\n",
>> -                           head, event.header.size, event.header.type);
>> +                           head, event->header.size, event->header.type);
>>                /*
>>                 * assume we lost track of the stream, check alignment, and
>>                 * increment a single u64 in the hope to catch on again
>> 'soon'.
>> @@ -1122,6 +1140,7 @@ static int
>> __perf_session__process_pipe_events(struct perf_session *self,
>>  done:
>>        err = 0;
>>  out_err:
>> +       free(buf);
>>        perf_session__warn_about_errors(self, tool);
>>        perf_session_free_sample_buffers(self);
>>        return err;
>
>

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

end of thread, other threads:[~2012-05-14 20:24 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-04-11  9:01 [PATCH 0/5] perf tools: add meta-data header support in pipe mode Stephane Eranian
2012-04-11  9:01 ` [PATCH 1/5] perf inject: fix broken perf inject -b Stephane Eranian
2012-04-11  9:01 ` [PATCH 2/5] perf tools: fix piped mode read code Stephane Eranian
2012-05-11  0:11   ` David Ahern
2012-05-14 20:24     ` Stephane Eranian
2012-04-11  9:01 ` [PATCH 3/5] perf tools: rename HEADER_TRACE_INFO to HEADER_TRACING_DATA Stephane Eranian
2012-04-11  9:01 ` [PATCH 4/5] perf record: add meta-data support for pipe-mode Stephane Eranian
2012-04-11  9:01 ` [PATCH 5/5] perf: make perf buildid-list work better with pipe mode Stephane Eranian
2012-04-26 15:14 ` [PATCH 0/5] perf tools: add meta-data header support in " Stephane Eranian

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.