-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
perf tests: Add a sample parsing test
Add a test that checks that sample parsing is correctly implemented. Signed-off-by: Adrian Hunter <adrian.hunter@intel.com> Cc: David Ahern <dsahern@gmail.com> Cc: Frederic Weisbecker <fweisbec@gmail.com> Cc: Ingo Molnar <mingo@kernel.org> Cc: Jiri Olsa <jolsa@redhat.com> Cc: Mike Galbraith <efault@gmx.de> Cc: Namhyung Kim <namhyung@gmail.com> Cc: Paul Mackerras <paulus@samba.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Stephane Eranian <eranian@google.com> Link: http://lkml.kernel.org/r/1377591794-30553-12-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
- Loading branch information
Showing
4 changed files
with
322 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,316 @@ | ||
#include <stdbool.h> | ||
#include <inttypes.h> | ||
|
||
#include "util.h" | ||
#include "event.h" | ||
#include "evsel.h" | ||
|
||
#include "tests.h" | ||
|
||
#define COMP(m) do { \ | ||
if (s1->m != s2->m) { \ | ||
pr_debug("Samples differ at '"#m"'\n"); \ | ||
return false; \ | ||
} \ | ||
} while (0) | ||
|
||
#define MCOMP(m) do { \ | ||
if (memcmp(&s1->m, &s2->m, sizeof(s1->m))) { \ | ||
pr_debug("Samples differ at '"#m"'\n"); \ | ||
return false; \ | ||
} \ | ||
} while (0) | ||
|
||
static bool samples_same(const struct perf_sample *s1, | ||
const struct perf_sample *s2, u64 type, u64 regs_user, | ||
u64 read_format) | ||
{ | ||
size_t i; | ||
|
||
if (type & PERF_SAMPLE_IDENTIFIER) | ||
COMP(id); | ||
|
||
if (type & PERF_SAMPLE_IP) | ||
COMP(ip); | ||
|
||
if (type & PERF_SAMPLE_TID) { | ||
COMP(pid); | ||
COMP(tid); | ||
} | ||
|
||
if (type & PERF_SAMPLE_TIME) | ||
COMP(time); | ||
|
||
if (type & PERF_SAMPLE_ADDR) | ||
COMP(addr); | ||
|
||
if (type & PERF_SAMPLE_ID) | ||
COMP(id); | ||
|
||
if (type & PERF_SAMPLE_STREAM_ID) | ||
COMP(stream_id); | ||
|
||
if (type & PERF_SAMPLE_CPU) | ||
COMP(cpu); | ||
|
||
if (type & PERF_SAMPLE_PERIOD) | ||
COMP(period); | ||
|
||
if (type & PERF_SAMPLE_READ) { | ||
if (read_format & PERF_FORMAT_GROUP) | ||
COMP(read.group.nr); | ||
else | ||
COMP(read.one.value); | ||
if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) | ||
COMP(read.time_enabled); | ||
if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) | ||
COMP(read.time_running); | ||
/* PERF_FORMAT_ID is forced for PERF_SAMPLE_READ */ | ||
if (read_format & PERF_FORMAT_GROUP) { | ||
for (i = 0; i < s1->read.group.nr; i++) | ||
MCOMP(read.group.values[i]); | ||
} else { | ||
COMP(read.one.id); | ||
} | ||
} | ||
|
||
if (type & PERF_SAMPLE_CALLCHAIN) { | ||
COMP(callchain->nr); | ||
for (i = 0; i < s1->callchain->nr; i++) | ||
COMP(callchain->ips[i]); | ||
} | ||
|
||
if (type & PERF_SAMPLE_RAW) { | ||
COMP(raw_size); | ||
if (memcmp(s1->raw_data, s2->raw_data, s1->raw_size)) { | ||
pr_debug("Samples differ at 'raw_data'\n"); | ||
return false; | ||
} | ||
} | ||
|
||
if (type & PERF_SAMPLE_BRANCH_STACK) { | ||
COMP(branch_stack->nr); | ||
for (i = 0; i < s1->branch_stack->nr; i++) | ||
MCOMP(branch_stack->entries[i]); | ||
} | ||
|
||
if (type & PERF_SAMPLE_REGS_USER) { | ||
size_t sz = hweight_long(regs_user) * sizeof(u64); | ||
|
||
COMP(user_regs.abi); | ||
if (s1->user_regs.abi && | ||
(!s1->user_regs.regs || !s2->user_regs.regs || | ||
memcmp(s1->user_regs.regs, s2->user_regs.regs, sz))) { | ||
pr_debug("Samples differ at 'user_regs'\n"); | ||
return false; | ||
} | ||
} | ||
|
||
if (type & PERF_SAMPLE_STACK_USER) { | ||
COMP(user_stack.size); | ||
if (memcmp(s1->user_stack.data, s1->user_stack.data, | ||
s1->user_stack.size)) { | ||
pr_debug("Samples differ at 'user_stack'\n"); | ||
return false; | ||
} | ||
} | ||
|
||
if (type & PERF_SAMPLE_WEIGHT) | ||
COMP(weight); | ||
|
||
if (type & PERF_SAMPLE_DATA_SRC) | ||
COMP(data_src); | ||
|
||
return true; | ||
} | ||
|
||
static int do_test(u64 sample_type, u64 sample_regs_user, u64 read_format) | ||
{ | ||
struct perf_evsel evsel = { | ||
.needs_swap = false, | ||
.attr = { | ||
.sample_type = sample_type, | ||
.sample_regs_user = sample_regs_user, | ||
.read_format = read_format, | ||
}, | ||
}; | ||
union perf_event *event; | ||
union { | ||
struct ip_callchain callchain; | ||
u64 data[64]; | ||
} callchain = { | ||
/* 3 ips */ | ||
.data = {3, 201, 202, 203}, | ||
}; | ||
union { | ||
struct branch_stack branch_stack; | ||
u64 data[64]; | ||
} branch_stack = { | ||
/* 1 branch_entry */ | ||
.data = {1, 211, 212, 213}, | ||
}; | ||
u64 user_regs[64]; | ||
const u64 raw_data[] = {0x123456780a0b0c0dULL, 0x1102030405060708ULL}; | ||
const u64 data[] = {0x2211443366558877ULL, 0, 0xaabbccddeeff4321ULL}; | ||
struct perf_sample sample = { | ||
.ip = 101, | ||
.pid = 102, | ||
.tid = 103, | ||
.time = 104, | ||
.addr = 105, | ||
.id = 106, | ||
.stream_id = 107, | ||
.period = 108, | ||
.weight = 109, | ||
.cpu = 110, | ||
.raw_size = sizeof(raw_data), | ||
.data_src = 111, | ||
.raw_data = (void *)raw_data, | ||
.callchain = &callchain.callchain, | ||
.branch_stack = &branch_stack.branch_stack, | ||
.user_regs = { | ||
.abi = PERF_SAMPLE_REGS_ABI_64, | ||
.regs = user_regs, | ||
}, | ||
.user_stack = { | ||
.size = sizeof(data), | ||
.data = (void *)data, | ||
}, | ||
.read = { | ||
.time_enabled = 0x030a59d664fca7deULL, | ||
.time_running = 0x011b6ae553eb98edULL, | ||
}, | ||
}; | ||
struct sample_read_value values[] = {{1, 5}, {9, 3}, {2, 7}, {6, 4},}; | ||
struct perf_sample sample_out; | ||
size_t i, sz, bufsz; | ||
int err, ret = -1; | ||
|
||
for (i = 0; i < sizeof(user_regs); i++) | ||
*(i + (u8 *)user_regs) = i & 0xfe; | ||
|
||
if (read_format & PERF_FORMAT_GROUP) { | ||
sample.read.group.nr = 4; | ||
sample.read.group.values = values; | ||
} else { | ||
sample.read.one.value = 0x08789faeb786aa87ULL; | ||
sample.read.one.id = 99; | ||
} | ||
|
||
sz = perf_event__sample_event_size(&sample, sample_type, | ||
sample_regs_user, read_format); | ||
bufsz = sz + 4096; /* Add a bit for overrun checking */ | ||
event = malloc(bufsz); | ||
if (!event) { | ||
pr_debug("malloc failed\n"); | ||
return -1; | ||
} | ||
|
||
memset(event, 0xff, bufsz); | ||
event->header.type = PERF_RECORD_SAMPLE; | ||
event->header.misc = 0; | ||
event->header.size = sz; | ||
|
||
err = perf_event__synthesize_sample(event, sample_type, | ||
sample_regs_user, read_format, | ||
&sample, false); | ||
if (err) { | ||
pr_debug("%s failed for sample_type %#"PRIx64", error %d\n", | ||
"perf_event__synthesize_sample", sample_type, err); | ||
goto out_free; | ||
} | ||
|
||
/* The data does not contain 0xff so we use that to check the size */ | ||
for (i = bufsz; i > 0; i--) { | ||
if (*(i - 1 + (u8 *)event) != 0xff) | ||
break; | ||
} | ||
if (i != sz) { | ||
pr_debug("Event size mismatch: actual %zu vs expected %zu\n", | ||
i, sz); | ||
goto out_free; | ||
} | ||
|
||
evsel.sample_size = __perf_evsel__sample_size(sample_type); | ||
|
||
err = perf_evsel__parse_sample(&evsel, event, &sample_out); | ||
if (err) { | ||
pr_debug("%s failed for sample_type %#"PRIx64", error %d\n", | ||
"perf_evsel__parse_sample", sample_type, err); | ||
goto out_free; | ||
} | ||
|
||
if (!samples_same(&sample, &sample_out, sample_type, | ||
sample_regs_user, read_format)) { | ||
pr_debug("parsing failed for sample_type %#"PRIx64"\n", | ||
sample_type); | ||
goto out_free; | ||
} | ||
|
||
ret = 0; | ||
out_free: | ||
free(event); | ||
if (ret && read_format) | ||
pr_debug("read_format %#"PRIx64"\n", read_format); | ||
return ret; | ||
} | ||
|
||
/** | ||
* test__sample_parsing - test sample parsing. | ||
* | ||
* This function implements a test that synthesizes a sample event, parses it | ||
* and then checks that the parsed sample matches the original sample. The test | ||
* checks sample format bits separately and together. If the test passes %0 is | ||
* returned, otherwise %-1 is returned. | ||
*/ | ||
int test__sample_parsing(void) | ||
{ | ||
const u64 rf[] = {4, 5, 6, 7, 12, 13, 14, 15}; | ||
u64 sample_type; | ||
u64 sample_regs_user; | ||
size_t i; | ||
int err; | ||
|
||
/* | ||
* Fail the test if it has not been updated when new sample format bits | ||
* were added. | ||
*/ | ||
if (PERF_SAMPLE_MAX > PERF_SAMPLE_IDENTIFIER << 1) { | ||
pr_debug("sample format has changed - test needs updating\n"); | ||
return -1; | ||
} | ||
|
||
/* Test each sample format bit separately */ | ||
for (sample_type = 1; sample_type != PERF_SAMPLE_MAX; | ||
sample_type <<= 1) { | ||
/* Test read_format variations */ | ||
if (sample_type == PERF_SAMPLE_READ) { | ||
for (i = 0; i < ARRAY_SIZE(rf); i++) { | ||
err = do_test(sample_type, 0, rf[i]); | ||
if (err) | ||
return err; | ||
} | ||
continue; | ||
} | ||
|
||
if (sample_type == PERF_SAMPLE_REGS_USER) | ||
sample_regs_user = 0x3fff; | ||
else | ||
sample_regs_user = 0; | ||
|
||
err = do_test(sample_type, sample_regs_user, 0); | ||
if (err) | ||
return err; | ||
} | ||
|
||
/* Test all sample format bits together */ | ||
sample_type = PERF_SAMPLE_MAX - 1; | ||
sample_regs_user = 0x3fff; | ||
for (i = 0; i < ARRAY_SIZE(rf); i++) { | ||
err = do_test(sample_type, sample_regs_user, rf[i]); | ||
if (err) | ||
return err; | ||
} | ||
|
||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters