diff options
| author | Adrian Hunter <adrian.hunter@intel.com> | 2013-08-27 04:23:13 -0400 |
|---|---|---|
| committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2013-08-29 15:46:58 -0400 |
| commit | 045f8cd8542d2fcd424a32fa10fcd3dd29c6d374 (patch) | |
| tree | a4b3dc7cfe7d17f2a601bcbab76e4106aadcea5d /tools | |
| parent | b1cf6f65aa7096984836addab7cec6b5b6d4393a (diff) | |
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>
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/perf/Makefile | 1 | ||||
| -rw-r--r-- | tools/perf/tests/builtin-test.c | 4 | ||||
| -rw-r--r-- | tools/perf/tests/sample-parsing.c | 316 | ||||
| -rw-r--r-- | tools/perf/tests/tests.h | 1 |
4 files changed, 322 insertions, 0 deletions
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 845cc762735d..ecebfd00295e 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
| @@ -439,6 +439,7 @@ PERFLIBS = $(LIB_FILE) $(LIBLK) $(LIBTRACEEVENT) | |||
| 439 | ifneq ($(OUTPUT),) | 439 | ifneq ($(OUTPUT),) |
| 440 | CFLAGS += -I$(OUTPUT) | 440 | CFLAGS += -I$(OUTPUT) |
| 441 | endif | 441 | endif |
| 442 | LIB_OBJS += $(OUTPUT)tests/sample-parsing.o | ||
| 442 | 443 | ||
| 443 | ifdef NO_LIBELF | 444 | ifdef NO_LIBELF |
| 444 | EXTLIBS := $(filter-out -lelf,$(EXTLIBS)) | 445 | EXTLIBS := $(filter-out -lelf,$(EXTLIBS)) |
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index f5af19244a05..8ad9415dd847 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c | |||
| @@ -104,6 +104,10 @@ static struct test { | |||
| 104 | .func = test__code_reading, | 104 | .func = test__code_reading, |
| 105 | }, | 105 | }, |
| 106 | { | 106 | { |
| 107 | .desc = "Test sample parsing", | ||
| 108 | .func = test__sample_parsing, | ||
| 109 | }, | ||
| 110 | { | ||
| 107 | .func = NULL, | 111 | .func = NULL, |
| 108 | }, | 112 | }, |
| 109 | }; | 113 | }; |
diff --git a/tools/perf/tests/sample-parsing.c b/tools/perf/tests/sample-parsing.c new file mode 100644 index 000000000000..77f598dbd97a --- /dev/null +++ b/tools/perf/tests/sample-parsing.c | |||
| @@ -0,0 +1,316 @@ | |||
| 1 | #include <stdbool.h> | ||
| 2 | #include <inttypes.h> | ||
| 3 | |||
| 4 | #include "util.h" | ||
| 5 | #include "event.h" | ||
| 6 | #include "evsel.h" | ||
| 7 | |||
| 8 | #include "tests.h" | ||
| 9 | |||
| 10 | #define COMP(m) do { \ | ||
| 11 | if (s1->m != s2->m) { \ | ||
| 12 | pr_debug("Samples differ at '"#m"'\n"); \ | ||
| 13 | return false; \ | ||
| 14 | } \ | ||
| 15 | } while (0) | ||
| 16 | |||
| 17 | #define MCOMP(m) do { \ | ||
| 18 | if (memcmp(&s1->m, &s2->m, sizeof(s1->m))) { \ | ||
| 19 | pr_debug("Samples differ at '"#m"'\n"); \ | ||
| 20 | return false; \ | ||
| 21 | } \ | ||
| 22 | } while (0) | ||
| 23 | |||
| 24 | static bool samples_same(const struct perf_sample *s1, | ||
| 25 | const struct perf_sample *s2, u64 type, u64 regs_user, | ||
| 26 | u64 read_format) | ||
| 27 | { | ||
| 28 | size_t i; | ||
| 29 | |||
| 30 | if (type & PERF_SAMPLE_IDENTIFIER) | ||
| 31 | COMP(id); | ||
| 32 | |||
| 33 | if (type & PERF_SAMPLE_IP) | ||
| 34 | COMP(ip); | ||
| 35 | |||
| 36 | if (type & PERF_SAMPLE_TID) { | ||
| 37 | COMP(pid); | ||
| 38 | COMP(tid); | ||
| 39 | } | ||
| 40 | |||
| 41 | if (type & PERF_SAMPLE_TIME) | ||
| 42 | COMP(time); | ||
| 43 | |||
| 44 | if (type & PERF_SAMPLE_ADDR) | ||
| 45 | COMP(addr); | ||
| 46 | |||
| 47 | if (type & PERF_SAMPLE_ID) | ||
| 48 | COMP(id); | ||
| 49 | |||
| 50 | if (type & PERF_SAMPLE_STREAM_ID) | ||
| 51 | COMP(stream_id); | ||
| 52 | |||
| 53 | if (type & PERF_SAMPLE_CPU) | ||
| 54 | COMP(cpu); | ||
| 55 | |||
| 56 | if (type & PERF_SAMPLE_PERIOD) | ||
| 57 | COMP(period); | ||
| 58 | |||
| 59 | if (type & PERF_SAMPLE_READ) { | ||
| 60 | if (read_format & PERF_FORMAT_GROUP) | ||
| 61 | COMP(read.group.nr); | ||
| 62 | else | ||
| 63 | COMP(read.one.value); | ||
| 64 | if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) | ||
| 65 | COMP(read.time_enabled); | ||
| 66 | if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) | ||
| 67 | COMP(read.time_running); | ||
| 68 | /* PERF_FORMAT_ID is forced for PERF_SAMPLE_READ */ | ||
| 69 | if (read_format & PERF_FORMAT_GROUP) { | ||
| 70 | for (i = 0; i < s1->read.group.nr; i++) | ||
| 71 | MCOMP(read.group.values[i]); | ||
| 72 | } else { | ||
| 73 | COMP(read.one.id); | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | if (type & PERF_SAMPLE_CALLCHAIN) { | ||
| 78 | COMP(callchain->nr); | ||
| 79 | for (i = 0; i < s1->callchain->nr; i++) | ||
| 80 | COMP(callchain->ips[i]); | ||
| 81 | } | ||
| 82 | |||
| 83 | if (type & PERF_SAMPLE_RAW) { | ||
| 84 | COMP(raw_size); | ||
| 85 | if (memcmp(s1->raw_data, s2->raw_data, s1->raw_size)) { | ||
| 86 | pr_debug("Samples differ at 'raw_data'\n"); | ||
| 87 | return false; | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | if (type & PERF_SAMPLE_BRANCH_STACK) { | ||
| 92 | COMP(branch_stack->nr); | ||
| 93 | for (i = 0; i < s1->branch_stack->nr; i++) | ||
| 94 | MCOMP(branch_stack->entries[i]); | ||
| 95 | } | ||
| 96 | |||
| 97 | if (type & PERF_SAMPLE_REGS_USER) { | ||
| 98 | size_t sz = hweight_long(regs_user) * sizeof(u64); | ||
| 99 | |||
| 100 | COMP(user_regs.abi); | ||
| 101 | if (s1->user_regs.abi && | ||
| 102 | (!s1->user_regs.regs || !s2->user_regs.regs || | ||
| 103 | memcmp(s1->user_regs.regs, s2->user_regs.regs, sz))) { | ||
| 104 | pr_debug("Samples differ at 'user_regs'\n"); | ||
| 105 | return false; | ||
| 106 | } | ||
| 107 | } | ||
| 108 | |||
| 109 | if (type & PERF_SAMPLE_STACK_USER) { | ||
| 110 | COMP(user_stack.size); | ||
| 111 | if (memcmp(s1->user_stack.data, s1->user_stack.data, | ||
| 112 | s1->user_stack.size)) { | ||
| 113 | pr_debug("Samples differ at 'user_stack'\n"); | ||
| 114 | return false; | ||
| 115 | } | ||
| 116 | } | ||
| 117 | |||
| 118 | if (type & PERF_SAMPLE_WEIGHT) | ||
| 119 | COMP(weight); | ||
| 120 | |||
| 121 | if (type & PERF_SAMPLE_DATA_SRC) | ||
| 122 | COMP(data_src); | ||
| 123 | |||
| 124 | return true; | ||
| 125 | } | ||
| 126 | |||
| 127 | static int do_test(u64 sample_type, u64 sample_regs_user, u64 read_format) | ||
| 128 | { | ||
| 129 | struct perf_evsel evsel = { | ||
| 130 | .needs_swap = false, | ||
| 131 | .attr = { | ||
| 132 | .sample_type = sample_type, | ||
| 133 | .sample_regs_user = sample_regs_user, | ||
| 134 | .read_format = read_format, | ||
| 135 | }, | ||
| 136 | }; | ||
| 137 | union perf_event *event; | ||
| 138 | union { | ||
| 139 | struct ip_callchain callchain; | ||
| 140 | u64 data[64]; | ||
| 141 | } callchain = { | ||
| 142 | /* 3 ips */ | ||
| 143 | .data = {3, 201, 202, 203}, | ||
| 144 | }; | ||
| 145 | union { | ||
| 146 | struct branch_stack branch_stack; | ||
| 147 | u64 data[64]; | ||
| 148 | } branch_stack = { | ||
| 149 | /* 1 branch_entry */ | ||
| 150 | .data = {1, 211, 212, 213}, | ||
| 151 | }; | ||
| 152 | u64 user_regs[64]; | ||
| 153 | const u64 raw_data[] = {0x123456780a0b0c0dULL, 0x1102030405060708ULL}; | ||
| 154 | const u64 data[] = {0x2211443366558877ULL, 0, 0xaabbccddeeff4321ULL}; | ||
| 155 | struct perf_sample sample = { | ||
| 156 | .ip = 101, | ||
| 157 | .pid = 102, | ||
| 158 | .tid = 103, | ||
| 159 | .time = 104, | ||
| 160 | .addr = 105, | ||
| 161 | .id = 106, | ||
| 162 | .stream_id = 107, | ||
| 163 | .period = 108, | ||
| 164 | .weight = 109, | ||
| 165 | .cpu = 110, | ||
| 166 | .raw_size = sizeof(raw_data), | ||
| 167 | .data_src = 111, | ||
| 168 | .raw_data = (void *)raw_data, | ||
| 169 | .callchain = &callchain.callchain, | ||
| 170 | .branch_stack = &branch_stack.branch_stack, | ||
| 171 | .user_regs = { | ||
| 172 | .abi = PERF_SAMPLE_REGS_ABI_64, | ||
| 173 | .regs = user_regs, | ||
| 174 | }, | ||
| 175 | .user_stack = { | ||
| 176 | .size = sizeof(data), | ||
| 177 | .data = (void *)data, | ||
| 178 | }, | ||
| 179 | .read = { | ||
| 180 | .time_enabled = 0x030a59d664fca7deULL, | ||
| 181 | .time_running = 0x011b6ae553eb98edULL, | ||
| 182 | }, | ||
| 183 | }; | ||
| 184 | struct sample_read_value values[] = {{1, 5}, {9, 3}, {2, 7}, {6, 4},}; | ||
| 185 | struct perf_sample sample_out; | ||
| 186 | size_t i, sz, bufsz; | ||
| 187 | int err, ret = -1; | ||
| 188 | |||
| 189 | for (i = 0; i < sizeof(user_regs); i++) | ||
| 190 | *(i + (u8 *)user_regs) = i & 0xfe; | ||
| 191 | |||
| 192 | if (read_format & PERF_FORMAT_GROUP) { | ||
| 193 | sample.read.group.nr = 4; | ||
| 194 | sample.read.group.values = values; | ||
| 195 | } else { | ||
| 196 | sample.read.one.value = 0x08789faeb786aa87ULL; | ||
| 197 | sample.read.one.id = 99; | ||
| 198 | } | ||
| 199 | |||
| 200 | sz = perf_event__sample_event_size(&sample, sample_type, | ||
| 201 | sample_regs_user, read_format); | ||
| 202 | bufsz = sz + 4096; /* Add a bit for overrun checking */ | ||
| 203 | event = malloc(bufsz); | ||
| 204 | if (!event) { | ||
| 205 | pr_debug("malloc failed\n"); | ||
| 206 | return -1; | ||
| 207 | } | ||
| 208 | |||
| 209 | memset(event, 0xff, bufsz); | ||
| 210 | event->header.type = PERF_RECORD_SAMPLE; | ||
| 211 | event->header.misc = 0; | ||
| 212 | event->header.size = sz; | ||
| 213 | |||
| 214 | err = perf_event__synthesize_sample(event, sample_type, | ||
| 215 | sample_regs_user, read_format, | ||
| 216 | &sample, false); | ||
| 217 | if (err) { | ||
| 218 | pr_debug("%s failed for sample_type %#"PRIx64", error %d\n", | ||
| 219 | "perf_event__synthesize_sample", sample_type, err); | ||
| 220 | goto out_free; | ||
| 221 | } | ||
| 222 | |||
| 223 | /* The data does not contain 0xff so we use that to check the size */ | ||
| 224 | for (i = bufsz; i > 0; i--) { | ||
| 225 | if (*(i - 1 + (u8 *)event) != 0xff) | ||
| 226 | break; | ||
| 227 | } | ||
| 228 | if (i != sz) { | ||
| 229 | pr_debug("Event size mismatch: actual %zu vs expected %zu\n", | ||
| 230 | i, sz); | ||
| 231 | goto out_free; | ||
| 232 | } | ||
| 233 | |||
| 234 | evsel.sample_size = __perf_evsel__sample_size(sample_type); | ||
| 235 | |||
| 236 | err = perf_evsel__parse_sample(&evsel, event, &sample_out); | ||
| 237 | if (err) { | ||
| 238 | pr_debug("%s failed for sample_type %#"PRIx64", error %d\n", | ||
| 239 | "perf_evsel__parse_sample", sample_type, err); | ||
| 240 | goto out_free; | ||
| 241 | } | ||
| 242 | |||
| 243 | if (!samples_same(&sample, &sample_out, sample_type, | ||
| 244 | sample_regs_user, read_format)) { | ||
| 245 | pr_debug("parsing failed for sample_type %#"PRIx64"\n", | ||
| 246 | sample_type); | ||
| 247 | goto out_free; | ||
| 248 | } | ||
| 249 | |||
| 250 | ret = 0; | ||
| 251 | out_free: | ||
| 252 | free(event); | ||
| 253 | if (ret && read_format) | ||
| 254 | pr_debug("read_format %#"PRIx64"\n", read_format); | ||
| 255 | return ret; | ||
| 256 | } | ||
| 257 | |||
| 258 | /** | ||
| 259 | * test__sample_parsing - test sample parsing. | ||
| 260 | * | ||
| 261 | * This function implements a test that synthesizes a sample event, parses it | ||
| 262 | * and then checks that the parsed sample matches the original sample. The test | ||
| 263 | * checks sample format bits separately and together. If the test passes %0 is | ||
| 264 | * returned, otherwise %-1 is returned. | ||
| 265 | */ | ||
| 266 | int test__sample_parsing(void) | ||
| 267 | { | ||
| 268 | const u64 rf[] = {4, 5, 6, 7, 12, 13, 14, 15}; | ||
| 269 | u64 sample_type; | ||
| 270 | u64 sample_regs_user; | ||
| 271 | size_t i; | ||
| 272 | int err; | ||
| 273 | |||
| 274 | /* | ||
| 275 | * Fail the test if it has not been updated when new sample format bits | ||
| 276 | * were added. | ||
| 277 | */ | ||
| 278 | if (PERF_SAMPLE_MAX > PERF_SAMPLE_IDENTIFIER << 1) { | ||
| 279 | pr_debug("sample format has changed - test needs updating\n"); | ||
| 280 | return -1; | ||
| 281 | } | ||
| 282 | |||
| 283 | /* Test each sample format bit separately */ | ||
| 284 | for (sample_type = 1; sample_type != PERF_SAMPLE_MAX; | ||
| 285 | sample_type <<= 1) { | ||
| 286 | /* Test read_format variations */ | ||
| 287 | if (sample_type == PERF_SAMPLE_READ) { | ||
| 288 | for (i = 0; i < ARRAY_SIZE(rf); i++) { | ||
| 289 | err = do_test(sample_type, 0, rf[i]); | ||
| 290 | if (err) | ||
| 291 | return err; | ||
| 292 | } | ||
| 293 | continue; | ||
| 294 | } | ||
| 295 | |||
| 296 | if (sample_type == PERF_SAMPLE_REGS_USER) | ||
| 297 | sample_regs_user = 0x3fff; | ||
| 298 | else | ||
| 299 | sample_regs_user = 0; | ||
| 300 | |||
| 301 | err = do_test(sample_type, sample_regs_user, 0); | ||
| 302 | if (err) | ||
| 303 | return err; | ||
| 304 | } | ||
| 305 | |||
| 306 | /* Test all sample format bits together */ | ||
| 307 | sample_type = PERF_SAMPLE_MAX - 1; | ||
| 308 | sample_regs_user = 0x3fff; | ||
| 309 | for (i = 0; i < ARRAY_SIZE(rf); i++) { | ||
| 310 | err = do_test(sample_type, sample_regs_user, rf[i]); | ||
| 311 | if (err) | ||
| 312 | return err; | ||
| 313 | } | ||
| 314 | |||
| 315 | return 0; | ||
| 316 | } | ||
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index c748f532b20f..83d5b71a3ce4 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h | |||
| @@ -37,5 +37,6 @@ int test__task_exit(void); | |||
| 37 | int test__sw_clock_freq(void); | 37 | int test__sw_clock_freq(void); |
| 38 | int test__perf_time_to_tsc(void); | 38 | int test__perf_time_to_tsc(void); |
| 39 | int test__code_reading(void); | 39 | int test__code_reading(void); |
| 40 | int test__sample_parsing(void); | ||
| 40 | 41 | ||
| 41 | #endif /* TESTS_H */ | 42 | #endif /* TESTS_H */ |
