[tip:perf/hw-branch-sampling] perf tools: Make perf able to read files from older ABIs

From: tip-bot for Stephane Eranian
Date: Fri Mar 09 2012 - 08:32:54 EST


Commit-ID: 114382a0aea97974803c942f106d462cbca5c64d
Gitweb: http://git.kernel.org/tip/114382a0aea97974803c942f106d462cbca5c64d
Author: Stephane Eranian <eranian@xxxxxxxxxx>
AuthorDate: Thu, 9 Feb 2012 23:21:08 +0100
Committer: Ingo Molnar <mingo@xxxxxxx>
CommitDate: Fri, 9 Mar 2012 08:26:07 +0100

perf tools: Make perf able to read files from older ABIs

This patches provides a way to handle legacy perf.data
files. Legacy files are those using the older PERFFILE
signature.

For those, it is still necessary to detect endianness but
without comparing their header->attr_size with the
tool's own version as it may be different. Instead, we use
a reference table for all known sizes from the legacy era.

We try all the combinations for sizes and endianness. If we find
a match, we proceed, otherwise we return: "incompatible file
format".

This is also done for the pipe-mode file format.

Signed-off-by: Stephane Eranian <eranian@xxxxxxxxxx>
Cc: peterz@xxxxxxxxxxxxx
Cc: acme@xxxxxxxxxx
Cc: robert.richter@xxxxxxx
Cc: ming.m.lin@xxxxxxxxx
Cc: andi@xxxxxxxxxxxxxx
Cc: asharma@xxxxxx
Cc: ravitillo@xxxxxxx
Cc: vweaver1@xxxxxxxxxxxx
Cc: khandual@xxxxxxxxxxxxxxxxxx
Cc: dsahern@xxxxxxxxx
Link: http://lkml.kernel.org/r/1328826068-11713-19-git-send-email-eranian@xxxxxxxxxx
Signed-off-by: Ingo Molnar <mingo@xxxxxxx>
---
tools/perf/util/header.c | 122 +++++++++++++++++++++++++++++++++++-----------
1 files changed, 94 insertions(+), 28 deletions(-)

diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 6d58026..c851495 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -1803,35 +1803,101 @@ out_free:
return err;
}

-static int check_magic_endian(u64 *magic, struct perf_file_header *header,
- struct perf_header *ph)
+static const int attr_file_abi_sizes[] = {
+ [0] = PERF_ATTR_SIZE_VER0,
+ [1] = PERF_ATTR_SIZE_VER1,
+ 0,
+};
+
+/*
+ * In the legacy file format, the magic number is not used to encode endianness.
+ * hdr_sz was used to encode endianness. But given that hdr_sz can vary based
+ * on ABI revisions, we need to try all combinations for all endianness to
+ * detect the endianness.
+ */
+static int try_all_file_abis(uint64_t hdr_sz, struct perf_header *ph)
{
- int ret;
+ uint64_t ref_size, attr_size;
+ int i;

- /* check for legacy format */
- ret = memcmp(magic, __perf_magic1, sizeof(*magic));
- if (ret == 0) {
- pr_debug("legacy perf.data format\n");
- if (!header)
- return -1;
+ for (i = 0 ; attr_file_abi_sizes[i]; i++) {
+ ref_size = attr_file_abi_sizes[i]
+ + sizeof(struct perf_file_section);
+ if (hdr_sz != ref_size) {
+ attr_size = bswap_64(hdr_sz);
+ if (attr_size != ref_size)
+ continue;

- if (header->attr_size != sizeof(struct perf_file_attr)) {
- u64 attr_size = bswap_64(header->attr_size);
+ ph->needs_swap = true;
+ }
+ pr_debug("ABI%d perf.data file detected, need_swap=%d\n",
+ i,
+ ph->needs_swap);
+ return 0;
+ }
+ /* could not determine endianness */
+ return -1;
+}

- if (attr_size != sizeof(struct perf_file_attr))
- return -1;
+#define PERF_PIPE_HDR_VER0 16
+
+static const size_t attr_pipe_abi_sizes[] = {
+ [0] = PERF_PIPE_HDR_VER0,
+ 0,
+};
+
+/*
+ * In the legacy pipe format, there is an implicit assumption that endiannesss
+ * between host recording the samples, and host parsing the samples is the
+ * same. This is not always the case given that the pipe output may always be
+ * redirected into a file and analyzed on a different machine with possibly a
+ * different endianness and perf_event ABI revsions in the perf tool itself.
+ */
+static int try_all_pipe_abis(uint64_t hdr_sz, struct perf_header *ph)
+{
+ u64 attr_size;
+ int i;
+
+ for (i = 0 ; attr_pipe_abi_sizes[i]; i++) {
+ if (hdr_sz != attr_pipe_abi_sizes[i]) {
+ attr_size = bswap_64(hdr_sz);
+ if (attr_size != hdr_sz)
+ continue;

ph->needs_swap = true;
}
+ pr_debug("Pipe ABI%d perf.data file detected\n", i);
return 0;
}
+ return -1;
+}
+
+static int check_magic_endian(u64 magic, uint64_t hdr_sz,
+ bool is_pipe, struct perf_header *ph)
+{
+ int ret;
+
+ /* check for legacy format */
+ ret = memcmp(&magic, __perf_magic1, sizeof(magic));
+ if (ret == 0) {
+ pr_debug("legacy perf.data format\n");
+ if (is_pipe)
+ return try_all_pipe_abis(hdr_sz, ph);
+
+ return try_all_file_abis(hdr_sz, ph);
+ }
+ /*
+ * the new magic number serves two purposes:
+ * - unique number to identify actual perf.data files
+ * - encode endianness of file
+ */

- /* check magic number with same endianness */
- if (*magic == __perf_magic2)
+ /* check magic number with one endianness */
+ if (magic == __perf_magic2)
return 0;

- /* check magic number but opposite endianness */
- if (*magic != __perf_magic2_sw)
+ /* check magic number with opposite endianness */
+ if (magic != __perf_magic2_sw)
return -1;

ph->needs_swap = true;
@@ -1850,8 +1916,11 @@ int perf_file_header__read(struct perf_file_header *header,
if (ret <= 0)
return -1;

- if (check_magic_endian(&header->magic, header, ph) < 0)
+ if (check_magic_endian(header->magic,
+ header->attr_size, false, ph) < 0) {
+ pr_debug("magic/endian check failed\n");
return -1;
+ }

if (ph->needs_swap) {
mem_bswap_64(header, offsetof(struct perf_file_header,
@@ -1938,21 +2007,17 @@ static int perf_file_header__read_pipe(struct perf_pipe_file_header *header,
if (ret <= 0)
return -1;

- if (check_magic_endian(&header->magic, NULL, ph) < 0)
+ if (check_magic_endian(header->magic, header->size, true, ph) < 0) {
+ pr_debug("endian/magic failed\n");
return -1;
+ }
+
+ if (ph->needs_swap)
+ header->size = bswap_64(header->size);

if (repipe && do_write(STDOUT_FILENO, header, sizeof(*header)) < 0)
return -1;

- if (header->size != sizeof(*header)) {
- u64 size = bswap_64(header->size);
-
- if (size != sizeof(*header))
- return -1;
-
- ph->needs_swap = true;
- }
-
return 0;
}

@@ -1992,6 +2057,7 @@ static int read_attr(int fd, struct perf_header *ph,

/* on file perf_event_attr size */
sz = attr->size;
+
if (ph->needs_swap)
sz = bswap_32(sz);

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/