Skip to content

Commit

Permalink
perf tool: Fix endianness handling of u32 data in samples
Browse files Browse the repository at this point in the history
Currently, analyzing PPC data files on x86 the cpu field is always 0 and
the tid and pid are backwards. For example, analyzing a PPC file on PPC
the pid/tid fields show:

        rsyslogd  1210/1212

and analyzing the same PPC file using an x86 perf binary shows:

        rsyslogd  1212/1210

The problem is that the swap_op method for samples is
perf_event__all64_swap which assumes all elements in the sample_data
struct are u64s. cpu, tid and pid are u32s and need to be handled
individually. Given that the swap is done before the sample is parsed,
the simplest solution is to undo the 64-bit swap of those elements when
the sample is parsed and do the proper swap.

The RAW data field is generic and perf cannot have programmatic knowledge
of how to treat that data. Instead a warning is given to the user.

Thanks to Anton Blanchard for providing a data file for a mult-CPU
PPC system so I could verify the fix for the CPU fields.

v3 -> v4:
- fixed use of WARN_ONCE

v2 -> v3:
- used WARN_ONCE for message regarding raw data
- removed struct wrapper around union
- fixed whitespace issues

v1 -> v2:
- added a union for undoing the byte-swap on u64 and redoing swap on
  u32's to address compiler errors (see git commit 65014ab)

Cc: Anton Blanchard <anton@samba.org>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/1315321946-16993-1-git-send-email-dsahern@gmail.com
Signed-off-by: David Ahern <dsahern@gmail.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
  • Loading branch information
David Ahern authored and Arnaldo Carvalho de Melo committed Sep 23, 2011
1 parent 6bb8f31 commit 936be50
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 14 deletions.
2 changes: 1 addition & 1 deletion tools/perf/builtin-test.c
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,7 @@ static int test__basic_mmap(void)
}

err = perf_event__parse_sample(event, attr.sample_type, sample_size,
false, &sample);
false, &sample, false);
if (err) {
pr_err("Can't parse sample, err = %d\n", err);
goto out_munmap;
Expand Down
2 changes: 1 addition & 1 deletion tools/perf/util/event.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,6 @@ const char *perf_event__name(unsigned int id);

int perf_event__parse_sample(const union perf_event *event, u64 type,
int sample_size, bool sample_id_all,
struct perf_sample *sample);
struct perf_sample *sample, bool swapped);

#endif /* __PERF_RECORD_H */
54 changes: 43 additions & 11 deletions tools/perf/util/evsel.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
* Released under the GPL v2. (and only v2, not any later version)
*/

#include <byteswap.h>
#include "asm/bug.h"
#include "evsel.h"
#include "evlist.h"
#include "util.h"
Expand Down Expand Up @@ -342,10 +344,20 @@ static bool sample_overlap(const union perf_event *event,

int perf_event__parse_sample(const union perf_event *event, u64 type,
int sample_size, bool sample_id_all,
struct perf_sample *data)
struct perf_sample *data, bool swapped)
{
const u64 *array;

/*
* used for cross-endian analysis. See git commit 65014ab3
* for why this goofiness is needed.
*/
union {
u64 val64;
u32 val32[2];
} u;


data->cpu = data->pid = data->tid = -1;
data->stream_id = data->id = data->time = -1ULL;

Expand All @@ -366,9 +378,16 @@ int perf_event__parse_sample(const union perf_event *event, u64 type,
}

if (type & PERF_SAMPLE_TID) {
u32 *p = (u32 *)array;
data->pid = p[0];
data->tid = p[1];
u.val64 = *array;
if (swapped) {
/* undo swap of u64, then swap on individual u32s */
u.val64 = bswap_64(u.val64);
u.val32[0] = bswap_32(u.val32[0]);
u.val32[1] = bswap_32(u.val32[1]);
}

data->pid = u.val32[0];
data->tid = u.val32[1];
array++;
}

Expand All @@ -395,8 +414,15 @@ int perf_event__parse_sample(const union perf_event *event, u64 type,
}

if (type & PERF_SAMPLE_CPU) {
u32 *p = (u32 *)array;
data->cpu = *p;

u.val64 = *array;
if (swapped) {
/* undo swap of u64, then swap on individual u32s */
u.val64 = bswap_64(u.val64);
u.val32[0] = bswap_32(u.val32[0]);
}

data->cpu = u.val32[0];
array++;
}

Expand All @@ -423,18 +449,24 @@ int perf_event__parse_sample(const union perf_event *event, u64 type,
}

if (type & PERF_SAMPLE_RAW) {
u32 *p = (u32 *)array;
u.val64 = *array;
if (WARN_ONCE(swapped,
"Endianness of raw data not corrected!\n")) {
/* undo swap of u64, then swap on individual u32s */
u.val64 = bswap_64(u.val64);
u.val32[0] = bswap_32(u.val32[0]);
u.val32[1] = bswap_32(u.val32[1]);
}

if (sample_overlap(event, array, sizeof(u32)))
return -EFAULT;

data->raw_size = *p;
p++;
data->raw_size = u.val32[0];

if (sample_overlap(event, p, data->raw_size))
if (sample_overlap(event, &u.val32[1], data->raw_size))
return -EFAULT;

data->raw_data = p;
data->raw_data = &u.val32[1];
}

return 0;
Expand Down
3 changes: 2 additions & 1 deletion tools/perf/util/session.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,8 @@ static inline int perf_session__parse_sample(struct perf_session *session,
{
return perf_event__parse_sample(event, session->sample_type,
session->sample_size,
session->sample_id_all, sample);
session->sample_id_all, sample,
session->header.needs_swap);
}

struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,
Expand Down

0 comments on commit 936be50

Please sign in to comment.