Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 114382a0 authored by Stephane Eranian's avatar Stephane Eranian Committed by Ingo Molnar
Browse files

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: default avatarStephane Eranian <eranian@google.com>
Cc: peterz@infradead.org
Cc: acme@redhat.com
Cc: robert.richter@amd.com
Cc: ming.m.lin@intel.com
Cc: andi@firstfloor.org
Cc: asharma@fb.com
Cc: ravitillo@lbl.gov
Cc: vweaver1@eecs.utk.edu
Cc: khandual@linux.vnet.ibm.com
Cc: dsahern@gmail.com
Link: http://lkml.kernel.org/r/1328826068-11713-19-git-send-email-eranian@google.com


Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent 62db9068
Loading
Loading
Loading
Loading
+94 −28
Original line number Diff line number Diff line
@@ -1803,35 +1803,101 @@ int perf_header__process_sections(struct perf_header *header, int fd,
	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)
	for (i = 0 ; attr_file_abi_sizes[i]; i++) {
		ref_size = attr_file_abi_sizes[i]
			 + sizeof(struct perf_file_section);
		if (hdr_sz != ref_size) {
			attr_size = bswap_64(hdr_sz);
			if (attr_size != ref_size)
				continue;

			ph->needs_swap = true;
		}
		pr_debug("ABI%d perf.data file detected, need_swap=%d\n",
			 i,
			 ph->needs_swap);
		return 0;
	}
	/* could not determine endianness */
	return -1;
}

		if (header->attr_size != sizeof(struct perf_file_attr)) {
			u64 attr_size = bswap_64(header->attr_size);
#define PERF_PIPE_HDR_VER0	16

			if (attr_size != sizeof(struct perf_file_attr))
				return -1;
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)
		return -1;

	if (repipe && do_write(STDOUT_FILENO, header, sizeof(*header)) < 0)
	if (check_magic_endian(header->magic, header->size, true, ph) < 0) {
		pr_debug("endian/magic failed\n");
		return -1;
	}

	if (header->size != sizeof(*header)) {
		u64 size = bswap_64(header->size);
	if (ph->needs_swap)
		header->size = bswap_64(header->size);

		if (size != sizeof(*header))
	if (repipe && do_write(STDOUT_FILENO, header, sizeof(*header)) < 0)
		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);