| 1 | |
| 2 | #include <stdbool.h>
|
| 3 | #include <inttypes.h>
|
| 4 | #include <stdlib.h>
|
| 5 | #include <string.h>
|
| 6 | #include <linux/bitops.h>
|
| 7 | #include <linux/kernel.h>
|
| 8 | #include <linux/types.h>
|
| 9 |
|
| 10 | #include "map_symbol.h"
|
| 11 | #include "branch.h"
|
| 12 | #include "event.h"
|
| 13 | #include "evsel.h"
|
| 14 | #include "debug.h"
|
| 15 | #include "util/synthetic-events.h"
|
| 16 | #include "util/util.h"
|
| 17 |
|
| 18 | #include "tests.h"
|
| 19 |
|
| 20 | #define COMP(m) do { \
|
| 21 | if (s1->m != s2->m) { \
|
| 22 | pr_debug("Samples differ at '"#m"'\n"); \
|
| 23 | return false; \
|
| 24 | } \
|
| 25 | } while (0)
|
| 26 |
|
| 27 | #define MCOMP(m) do { \
|
| 28 | if (memcmp(&s1->m, &s2->m, sizeof(s1->m))) { \
|
| 29 | pr_debug("Samples differ at '"#m"'\n"); \
|
| 30 | return false; \
|
| 31 | } \
|
| 32 | } while (0)
|
| 33 |
|
| 34 | |
| 35 | * Hardcode the expected values for branch_entry flags.
|
| 36 | * These are based on the input value (213) specified
|
| 37 | * in branch_stack variable.
|
| 38 |
|
| 39 | #define BS_EXPECTED_BE 0xa000d00000000000
|
| 40 | #define BS_EXPECTED_LE 0x1aa00000000
|
| 41 | #define FLAG(s) s->branch_stack->entries[i].flags
|
| 42 |
|
| 43 | static bool samples_same(struct perf_sample *s1,
|
| 44 | struct perf_sample *s2,
|
| 45 | u64 type, u64 read_format, bool needs_swap)
|
| 46 | {
|
| 47 | size_t i;
|
| 48 |
|
| 49 | if (type & PERF_SAMPLE_IDENTIFIER)
|
| 50 | COMP(id);
|
| 51 |
|
| 52 | if (type & PERF_SAMPLE_IP)
|
| 53 | COMP(ip);
|
| 54 |
|
| 55 | if (type & PERF_SAMPLE_TID) {
|
| 56 | COMP(pid);
|
| 57 | COMP(tid);
|
| 58 | }
|
| 59 |
|
| 60 | if (type & PERF_SAMPLE_TIME)
|
| 61 | COMP(time);
|
| 62 |
|
| 63 | if (type & PERF_SAMPLE_ADDR)
|
| 64 | COMP(addr);
|
| 65 |
|
| 66 | if (type & PERF_SAMPLE_ID)
|
| 67 | COMP(id);
|
| 68 |
|
| 69 | if (type & PERF_SAMPLE_STREAM_ID)
|
| 70 | COMP(stream_id);
|
| 71 |
|
| 72 | if (type & PERF_SAMPLE_CPU)
|
| 73 | COMP(cpu);
|
| 74 |
|
| 75 | if (type & PERF_SAMPLE_PERIOD)
|
| 76 | COMP(period);
|
| 77 |
|
| 78 | if (type & PERF_SAMPLE_READ) {
|
| 79 | if (read_format & PERF_FORMAT_GROUP)
|
| 80 | COMP(read.group.nr);
|
| 81 | else
|
| 82 | COMP(read.one.value);
|
| 83 | if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
|
| 84 | COMP(read.time_enabled);
|
| 85 | if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
|
| 86 | COMP(read.time_running);
|
| 87 |
|
| 88 | if (read_format & PERF_FORMAT_GROUP) {
|
| 89 | struct sample_read_value *v1 = s1->read.group.values;
|
| 90 | struct sample_read_value *v2 = s2->read.group.values;
|
| 91 |
|
| 92 | for (i = 0; i < s1->read.group.nr; i++) {
|
| 93 | if (v1->value != v2->value) {
|
| 94 | pr_debug("Samples differ at 'read.group.values[].value'\n");
|
| 95 | return false;
|
| 96 | }
|
| 97 |
|
| 98 | if (v1->id != v2->id) {
|
| 99 | pr_debug("Samples differ at 'read.group.values[].id'\n");
|
| 100 | return false;
|
| 101 | }
|
| 102 |
|
| 103 | if (read_format & PERF_FORMAT_LOST &&
|
| 104 | v1->lost != v2->lost) {
|
| 105 | pr_debug("Samples differ at 'read.group.values[].lost'\n");
|
| 106 | return false;
|
| 107 | }
|
| 108 | v1 = next_sample_read_value(v1, read_format);
|
| 109 | v2 = next_sample_read_value(v2, read_format);
|
| 110 | }
|
| 111 | } else {
|
| 112 | COMP(read.one.id);
|
| 113 | if (read_format & PERF_FORMAT_LOST)
|
| 114 | COMP(read.one.lost);
|
| 115 | }
|
| 116 | }
|
| 117 |
|
| 118 | if (type & PERF_SAMPLE_CALLCHAIN) {
|
| 119 | COMP(callchain->nr);
|
| 120 | for (i = 0; i < s1->callchain->nr; i++)
|
| 121 | COMP(callchain->ips[i]);
|
| 122 | }
|
| 123 |
|
| 124 | if (type & PERF_SAMPLE_RAW) {
|
| 125 | COMP(raw_size);
|
| 126 | if (memcmp(s1->raw_data, s2->raw_data, s1->raw_size)) {
|
| 127 | pr_debug("Samples differ at 'raw_data'\n");
|
| 128 | return false;
|
| 129 | }
|
| 130 | }
|
| 131 |
|
| 132 | if (type & PERF_SAMPLE_BRANCH_STACK) {
|
| 133 | COMP(branch_stack->nr);
|
| 134 | COMP(branch_stack->hw_idx);
|
| 135 | for (i = 0; i < s1->branch_stack->nr; i++) {
|
| 136 | if (needs_swap)
|
| 137 | return ((host_is_bigendian()) ?
|
| 138 | (FLAG(s2).value == BS_EXPECTED_BE) :
|
| 139 | (FLAG(s2).value == BS_EXPECTED_LE));
|
| 140 | else
|
| 141 | MCOMP(branch_stack->entries[i]);
|
| 142 | }
|
| 143 | }
|
| 144 |
|
| 145 | if (type & PERF_SAMPLE_REGS_USER) {
|
| 146 | struct regs_dump *s1_regs = perf_sample__user_regs(s1);
|
| 147 | struct regs_dump *s2_regs = perf_sample__user_regs(s2);
|
| 148 | size_t sz = hweight_long(s1_regs->mask) * sizeof(u64);
|
| 149 |
|
| 150 | COMP(user_regs->mask);
|
| 151 | COMP(user_regs->abi);
|
| 152 | if (s1_regs->abi &&
|
| 153 | (!s1_regs->regs || !s2_regs->regs ||
|
| 154 | memcmp(s1_regs->regs, s2_regs->regs, sz))) {
|
| 155 | pr_debug("Samples differ at 'user_regs'\n");
|
| 156 | return false;
|
| 157 | }
|
| 158 | }
|
| 159 |
|
| 160 | if (type & PERF_SAMPLE_STACK_USER) {
|
| 161 | COMP(user_stack.size);
|
| 162 | if (memcmp(s1->user_stack.data, s2->user_stack.data,
|
| 163 | s1->user_stack.size)) {
|
| 164 | pr_debug("Samples differ at 'user_stack'\n");
|
| 165 | return false;
|
| 166 | }
|
| 167 | }
|
| 168 |
|
| 169 | if (type & PERF_SAMPLE_WEIGHT)
|
| 170 | COMP(weight);
|
| 171 |
|
| 172 | if (type & PERF_SAMPLE_WEIGHT_STRUCT) {
|
| 173 | COMP(weight);
|
| 174 | COMP(ins_lat);
|
| 175 | COMP(weight3);
|
| 176 | }
|
| 177 |
|
| 178 | if (type & PERF_SAMPLE_DATA_SRC)
|
| 179 | COMP(data_src);
|
| 180 |
|
| 181 | if (type & PERF_SAMPLE_TRANSACTION)
|
| 182 | COMP(transaction);
|
| 183 |
|
| 184 | if (type & PERF_SAMPLE_REGS_INTR) {
|
| 185 | struct regs_dump *s1_regs = perf_sample__intr_regs(s1);
|
| 186 | struct regs_dump *s2_regs = perf_sample__intr_regs(s2);
|
| 187 | size_t sz = hweight_long(s1_regs->mask) * sizeof(u64);
|
| 188 |
|
| 189 | COMP(intr_regs->mask);
|
| 190 | COMP(intr_regs->abi);
|
| 191 | if (s1_regs->abi &&
|
| 192 | (!s1_regs->regs || !s2_regs->regs ||
|
| 193 | memcmp(s1_regs->regs, s2_regs->regs, sz))) {
|
| 194 | pr_debug("Samples differ at 'intr_regs'\n");
|
| 195 | return false;
|
| 196 | }
|
| 197 | }
|
| 198 |
|
| 199 | if (type & PERF_SAMPLE_PHYS_ADDR)
|
| 200 | COMP(phys_addr);
|
| 201 |
|
| 202 | if (type & PERF_SAMPLE_CGROUP)
|
| 203 | COMP(cgroup);
|
| 204 |
|
| 205 | if (type & PERF_SAMPLE_DATA_PAGE_SIZE)
|
| 206 | COMP(data_page_size);
|
| 207 |
|
| 208 | if (type & PERF_SAMPLE_CODE_PAGE_SIZE)
|
| 209 | COMP(code_page_size);
|
| 210 |
|
| 211 | if (type & PERF_SAMPLE_AUX) {
|
| 212 | COMP(aux_sample.size);
|
| 213 | if (memcmp(s1->aux_sample.data, s2->aux_sample.data,
|
| 214 | s1->aux_sample.size)) {
|
| 215 | pr_debug("Samples differ at 'aux_sample'\n");
|
| 216 | return false;
|
| 217 | }
|
| 218 | }
|
| 219 |
|
| 220 | return true;
|
| 221 | }
|
| 222 |
|
| 223 | static int do_test(u64 sample_type, u64 sample_regs, u64 read_format)
|
| 224 | {
|
| 225 | struct perf_event_attr attr = {
|
| 226 | .sample_type = sample_type,
|
| 227 | .read_format = read_format,
|
| 228 | };
|
| 229 | struct evsel *evsel;
|
| 230 | union perf_event *event;
|
| 231 | union {
|
| 232 | struct ip_callchain callchain;
|
| 233 | u64 data[64];
|
| 234 | } callchain = {
|
| 235 |
|
| 236 | .data = {3, 201, 202, 203},
|
| 237 | };
|
| 238 | union {
|
| 239 | struct branch_stack branch_stack;
|
| 240 | u64 data[64];
|
| 241 | } branch_stack = {
|
| 242 |
|
| 243 | .data = {1, -1ULL, 211, 212, 213},
|
| 244 | };
|
| 245 | u64 regs[64];
|
| 246 | const u32 raw_data[] = {0x12345678, 0x0a0b0c0d, 0x11020304, 0x05060708, 0 };
|
| 247 | const u64 data[] = {0x2211443366558877ULL, 0, 0xaabbccddeeff4321ULL};
|
| 248 | const u64 aux_data[] = {0xa55a, 0, 0xeeddee, 0x0282028202820282};
|
| 249 | struct regs_dump user_regs = {
|
| 250 | .abi = PERF_SAMPLE_REGS_ABI_64,
|
| 251 | .mask = sample_regs,
|
| 252 | .regs = regs,
|
| 253 | };
|
| 254 | struct regs_dump intr_regs = {
|
| 255 | .abi = PERF_SAMPLE_REGS_ABI_64,
|
| 256 | .mask = sample_regs,
|
| 257 | .regs = regs,
|
| 258 | };
|
| 259 | struct perf_sample sample = {
|
| 260 | .ip = 101,
|
| 261 | .pid = 102,
|
| 262 | .tid = 103,
|
| 263 | .time = 104,
|
| 264 | .addr = 105,
|
| 265 | .id = 106,
|
| 266 | .stream_id = 107,
|
| 267 | .period = 108,
|
| 268 | .weight = 109,
|
| 269 | .cpu = 110,
|
| 270 | .raw_size = sizeof(raw_data),
|
| 271 | .data_src = 111,
|
| 272 | .transaction = 112,
|
| 273 | .raw_data = (void *)raw_data,
|
| 274 | .callchain = &callchain.callchain,
|
| 275 | .no_hw_idx = false,
|
| 276 | .branch_stack = &branch_stack.branch_stack,
|
| 277 | .user_regs = &user_regs,
|
| 278 | .user_stack = {
|
| 279 | .size = sizeof(data),
|
| 280 | .data = (void *)data,
|
| 281 | },
|
| 282 | .read = {
|
| 283 | .time_enabled = 0x030a59d664fca7deULL,
|
| 284 | .time_running = 0x011b6ae553eb98edULL,
|
| 285 | },
|
| 286 | .intr_regs = &intr_regs,
|
| 287 | .phys_addr = 113,
|
| 288 | .cgroup = 114,
|
| 289 | .data_page_size = 115,
|
| 290 | .code_page_size = 116,
|
| 291 | .ins_lat = 117,
|
| 292 | .weight3 = 118,
|
| 293 | .aux_sample = {
|
| 294 | .size = sizeof(aux_data),
|
| 295 | .data = (void *)aux_data,
|
| 296 | },
|
| 297 | };
|
| 298 | struct sample_read_value values[] = {{1, 5, 0}, {9, 3, 0}, {2, 7, 0}, {6, 4, 1},};
|
| 299 | struct sample_read_value packed_values[ARRAY_SIZE(values)];
|
| 300 | struct perf_sample sample_out, sample_out_endian;
|
| 301 | size_t i, sz, bufsz;
|
| 302 | int err, ret = -1;
|
| 303 |
|
| 304 | evsel = evsel__new(&attr);
|
| 305 | if (!evsel) {
|
| 306 | pr_debug("evsel__new failed\n");
|
| 307 | return -1;
|
| 308 | }
|
| 309 | perf_sample__init(&sample_out, false);
|
| 310 | perf_sample__init(&sample_out_endian, false);
|
| 311 | if (sample_type & PERF_SAMPLE_REGS_USER)
|
| 312 | evsel->core.attr.sample_regs_user = sample_regs;
|
| 313 |
|
| 314 | if (sample_type & PERF_SAMPLE_REGS_INTR)
|
| 315 | evsel->core.attr.sample_regs_intr = sample_regs;
|
| 316 |
|
| 317 | if (sample_type & PERF_SAMPLE_BRANCH_STACK)
|
| 318 | evsel->core.attr.branch_sample_type |= PERF_SAMPLE_BRANCH_HW_INDEX;
|
| 319 |
|
| 320 | for (i = 0; i < sizeof(regs); i++)
|
| 321 | *(i + (u8 *)regs) = i & 0xfe;
|
| 322 |
|
| 323 | if (read_format & PERF_FORMAT_GROUP) {
|
| 324 | size_t vsz = sample_read_value_size(read_format);
|
| 325 |
|
| 326 | |
| 327 | * evsel__parse_sample() points read.group.values at the event
|
| 328 | * data, where the entries are packed according to read_format,
|
| 329 | * so build the input the same way. Otherwise the fields
|
| 330 | * compared afterwards are just overlapping bytes.
|
| 331 |
|
| 332 | for (i = 0; i < ARRAY_SIZE(values); i++)
|
| 333 | memcpy((void *)packed_values + i * vsz, &values[i], vsz);
|
| 334 |
|
| 335 | sample.read.group.nr = ARRAY_SIZE(values);
|
| 336 | sample.read.group.values = packed_values;
|
| 337 | } else {
|
| 338 | sample.read.one.value = 0x08789faeb786aa87ULL;
|
| 339 | sample.read.one.id = 99;
|
| 340 | sample.read.one.lost = 1;
|
| 341 | }
|
| 342 |
|
| 343 | sz = perf_event__sample_event_size(&sample, sample_type, read_format,
|
| 344 | evsel->core.attr.branch_sample_type);
|
| 345 | bufsz = sz + 4096;
|
| 346 | event = malloc(bufsz);
|
| 347 | if (!event) {
|
| 348 | pr_debug("malloc failed\n");
|
| 349 | goto out_free;
|
| 350 | }
|
| 351 |
|
| 352 | memset(event, 0xff, bufsz);
|
| 353 | event->header.type = PERF_RECORD_SAMPLE;
|
| 354 | event->header.misc = 0;
|
| 355 | event->header.size = sz;
|
| 356 |
|
| 357 | err = perf_event__synthesize_sample(event, sample_type, read_format,
|
| 358 | evsel->core.attr.branch_sample_type, &sample);
|
| 359 | if (err) {
|
| 360 | pr_debug("%s failed for sample_type %#"PRIx64", error %d\n",
|
| 361 | "perf_event__synthesize_sample", sample_type, err);
|
| 362 | goto out_free;
|
| 363 | }
|
| 364 |
|
| 365 |
|
| 366 | for (i = bufsz; i > 0; i--) {
|
| 367 | if (*(i - 1 + (u8 *)event) != 0xff)
|
| 368 | break;
|
| 369 | }
|
| 370 | if (i != sz) {
|
| 371 | pr_debug("Event size mismatch: actual %zu vs expected %zu\n",
|
| 372 | i, sz);
|
| 373 | goto out_free;
|
| 374 | }
|
| 375 |
|
| 376 | evsel->sample_size = __evsel__sample_size(sample_type);
|
| 377 |
|
| 378 | err = evsel__parse_sample(evsel, event, &sample_out);
|
| 379 | if (err) {
|
| 380 | pr_debug("%s failed for sample_type %#"PRIx64", error %d\n",
|
| 381 | "evsel__parse_sample", sample_type, err);
|
| 382 | goto out_free;
|
| 383 | }
|
| 384 |
|
| 385 | if (!samples_same(&sample, &sample_out, sample_type, read_format, evsel->needs_swap)) {
|
| 386 | pr_debug("parsing failed for sample_type %#"PRIx64"\n",
|
| 387 | sample_type);
|
| 388 | goto out_free;
|
| 389 | }
|
| 390 |
|
| 391 | if (sample_type == PERF_SAMPLE_BRANCH_STACK) {
|
| 392 | evsel->needs_swap = true;
|
| 393 | evsel->sample_size = __evsel__sample_size(sample_type);
|
| 394 | err = evsel__parse_sample(evsel, event, &sample_out_endian);
|
| 395 | if (err) {
|
| 396 | pr_debug("%s failed for sample_type %#"PRIx64", error %d\n",
|
| 397 | "evsel__parse_sample", sample_type, err);
|
| 398 | goto out_free;
|
| 399 | }
|
| 400 |
|
| 401 | if (!samples_same(&sample, &sample_out_endian, sample_type,
|
| 402 | read_format, evsel->needs_swap)) {
|
| 403 | pr_debug("parsing failed for sample_type %#"PRIx64"\n",
|
| 404 | sample_type);
|
| 405 | goto out_free;
|
| 406 | }
|
| 407 | }
|
| 408 |
|
| 409 | ret = 0;
|
| 410 | out_free:
|
| 411 | free(event);
|
| 412 | perf_sample__exit(&sample_out_endian);
|
| 413 | perf_sample__exit(&sample_out);
|
| 414 | evsel__put(evsel);
|
| 415 | if (ret && read_format)
|
| 416 | pr_debug("read_format %#"PRIx64"\n", read_format);
|
| 417 | return ret;
|
| 418 | }
|
| 419 |
|
| 420 | |
| 421 | * test__sample_parsing - test sample parsing.
|
| 422 | *
|
| 423 | * This function implements a test that synthesizes a sample event, parses it
|
| 424 | * and then checks that the parsed sample matches the original sample. The test
|
| 425 | * checks sample format bits separately and together. If the test passes %0 is
|
| 426 | * returned, otherwise %-1 is returned.
|
| 427 |
|
| 428 | static int test__sample_parsing(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
|
| 429 | {
|
| 430 | const u64 rf[] = {4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 28, 29, 30, 31};
|
| 431 | u64 sample_type;
|
| 432 | u64 sample_regs;
|
| 433 | size_t i;
|
| 434 | int err;
|
| 435 |
|
| 436 | |
| 437 | * Fail the test if it has not been updated when new sample format bits
|
| 438 | * were added. Please actually update the test rather than just change
|
| 439 | * the condition below.
|
| 440 |
|
| 441 | if (PERF_SAMPLE_MAX > PERF_SAMPLE_WEIGHT_STRUCT << 1) {
|
| 442 | pr_debug("sample format has changed, some new PERF_SAMPLE_ bit was introduced - test needs updating\n");
|
| 443 | return -1;
|
| 444 | }
|
| 445 |
|
| 446 |
|
| 447 | for (sample_type = 1; sample_type != PERF_SAMPLE_MAX;
|
| 448 | sample_type <<= 1) {
|
| 449 |
|
| 450 | if (sample_type == PERF_SAMPLE_READ) {
|
| 451 | for (i = 0; i < ARRAY_SIZE(rf); i++) {
|
| 452 | err = do_test(sample_type, 0, rf[i]);
|
| 453 | if (err)
|
| 454 | return err;
|
| 455 | }
|
| 456 | continue;
|
| 457 | }
|
| 458 | sample_regs = 0;
|
| 459 |
|
| 460 | if (sample_type == PERF_SAMPLE_REGS_USER)
|
| 461 | sample_regs = 0x3fff;
|
| 462 |
|
| 463 | if (sample_type == PERF_SAMPLE_REGS_INTR)
|
| 464 | sample_regs = 0xff0fff;
|
| 465 |
|
| 466 | err = do_test(sample_type, sample_regs, 0);
|
| 467 | if (err)
|
| 468 | return err;
|
| 469 | }
|
| 470 |
|
| 471 | |
| 472 | * Test all sample format bits together
|
| 473 | * Note: PERF_SAMPLE_WEIGHT and PERF_SAMPLE_WEIGHT_STRUCT cannot
|
| 474 | * be set simultaneously.
|
| 475 |
|
| 476 | sample_type = (PERF_SAMPLE_MAX - 1) & ~PERF_SAMPLE_WEIGHT;
|
| 477 | sample_regs = 0x3fff;
|
| 478 | for (i = 0; i < ARRAY_SIZE(rf); i++) {
|
| 479 | err = do_test(sample_type, sample_regs, rf[i]);
|
| 480 | if (err)
|
| 481 | return err;
|
| 482 | }
|
| 483 | sample_type = (PERF_SAMPLE_MAX - 1) & ~PERF_SAMPLE_WEIGHT_STRUCT;
|
| 484 | for (i = 0; i < ARRAY_SIZE(rf); i++) {
|
| 485 | err = do_test(sample_type, sample_regs, rf[i]);
|
| 486 | if (err)
|
| 487 | return err;
|
| 488 | }
|
| 489 |
|
| 490 | return 0;
|
| 491 | }
|
| 492 |
|
| 493 | DEFINE_SUITE("Sample parsing", sample_parsing);
|