162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci#include <stdbool.h>
362306a36Sopenharmony_ci#include <inttypes.h>
462306a36Sopenharmony_ci#include <stdlib.h>
562306a36Sopenharmony_ci#include <string.h>
662306a36Sopenharmony_ci#include <linux/bitops.h>
762306a36Sopenharmony_ci#include <linux/kernel.h>
862306a36Sopenharmony_ci#include <linux/types.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "event.h"
1162306a36Sopenharmony_ci#include "evsel.h"
1262306a36Sopenharmony_ci#include "debug.h"
1362306a36Sopenharmony_ci#include "util/sample.h"
1462306a36Sopenharmony_ci#include "util/synthetic-events.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "tests/tests.h"
1762306a36Sopenharmony_ci#include "arch-tests.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define COMP(m) do {					\
2062306a36Sopenharmony_ci	if (s1->m != s2->m) {				\
2162306a36Sopenharmony_ci		pr_debug("Samples differ at '"#m"'\n");	\
2262306a36Sopenharmony_ci		return false;				\
2362306a36Sopenharmony_ci	}						\
2462306a36Sopenharmony_ci} while (0)
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic bool samples_same(const struct perf_sample *s1,
2762306a36Sopenharmony_ci			 const struct perf_sample *s2,
2862306a36Sopenharmony_ci			 u64 type)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	if (type & PERF_SAMPLE_WEIGHT_STRUCT) {
3162306a36Sopenharmony_ci		COMP(ins_lat);
3262306a36Sopenharmony_ci		COMP(retire_lat);
3362306a36Sopenharmony_ci	}
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	return true;
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic int do_test(u64 sample_type)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	struct evsel evsel = {
4162306a36Sopenharmony_ci		.needs_swap = false,
4262306a36Sopenharmony_ci		.core = {
4362306a36Sopenharmony_ci			. attr = {
4462306a36Sopenharmony_ci				.sample_type = sample_type,
4562306a36Sopenharmony_ci				.read_format = 0,
4662306a36Sopenharmony_ci			},
4762306a36Sopenharmony_ci		},
4862306a36Sopenharmony_ci	};
4962306a36Sopenharmony_ci	union perf_event *event;
5062306a36Sopenharmony_ci	struct perf_sample sample = {
5162306a36Sopenharmony_ci		.weight		= 101,
5262306a36Sopenharmony_ci		.ins_lat        = 102,
5362306a36Sopenharmony_ci		.retire_lat     = 103,
5462306a36Sopenharmony_ci	};
5562306a36Sopenharmony_ci	struct perf_sample sample_out;
5662306a36Sopenharmony_ci	size_t i, sz, bufsz;
5762306a36Sopenharmony_ci	int err, ret = -1;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	sz = perf_event__sample_event_size(&sample, sample_type, 0);
6062306a36Sopenharmony_ci	bufsz = sz + 4096; /* Add a bit for overrun checking */
6162306a36Sopenharmony_ci	event = malloc(bufsz);
6262306a36Sopenharmony_ci	if (!event) {
6362306a36Sopenharmony_ci		pr_debug("malloc failed\n");
6462306a36Sopenharmony_ci		return -1;
6562306a36Sopenharmony_ci	}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	memset(event, 0xff, bufsz);
6862306a36Sopenharmony_ci	event->header.type = PERF_RECORD_SAMPLE;
6962306a36Sopenharmony_ci	event->header.misc = 0;
7062306a36Sopenharmony_ci	event->header.size = sz;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	err = perf_event__synthesize_sample(event, sample_type, 0, &sample);
7362306a36Sopenharmony_ci	if (err) {
7462306a36Sopenharmony_ci		pr_debug("%s failed for sample_type %#"PRIx64", error %d\n",
7562306a36Sopenharmony_ci			 "perf_event__synthesize_sample", sample_type, err);
7662306a36Sopenharmony_ci		goto out_free;
7762306a36Sopenharmony_ci	}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	/* The data does not contain 0xff so we use that to check the size */
8062306a36Sopenharmony_ci	for (i = bufsz; i > 0; i--) {
8162306a36Sopenharmony_ci		if (*(i - 1 + (u8 *)event) != 0xff)
8262306a36Sopenharmony_ci			break;
8362306a36Sopenharmony_ci	}
8462306a36Sopenharmony_ci	if (i != sz) {
8562306a36Sopenharmony_ci		pr_debug("Event size mismatch: actual %zu vs expected %zu\n",
8662306a36Sopenharmony_ci			 i, sz);
8762306a36Sopenharmony_ci		goto out_free;
8862306a36Sopenharmony_ci	}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	evsel.sample_size = __evsel__sample_size(sample_type);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	err = evsel__parse_sample(&evsel, event, &sample_out);
9362306a36Sopenharmony_ci	if (err) {
9462306a36Sopenharmony_ci		pr_debug("%s failed for sample_type %#"PRIx64", error %d\n",
9562306a36Sopenharmony_ci			 "evsel__parse_sample", sample_type, err);
9662306a36Sopenharmony_ci		goto out_free;
9762306a36Sopenharmony_ci	}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	if (!samples_same(&sample, &sample_out, sample_type)) {
10062306a36Sopenharmony_ci		pr_debug("parsing failed for sample_type %#"PRIx64"\n",
10162306a36Sopenharmony_ci			 sample_type);
10262306a36Sopenharmony_ci		goto out_free;
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	ret = 0;
10662306a36Sopenharmony_ciout_free:
10762306a36Sopenharmony_ci	free(event);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	return ret;
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci/**
11362306a36Sopenharmony_ci * test__x86_sample_parsing - test X86 specific sample parsing
11462306a36Sopenharmony_ci *
11562306a36Sopenharmony_ci * This function implements a test that synthesizes a sample event, parses it
11662306a36Sopenharmony_ci * and then checks that the parsed sample matches the original sample. If the
11762306a36Sopenharmony_ci * test passes %0 is returned, otherwise %-1 is returned.
11862306a36Sopenharmony_ci *
11962306a36Sopenharmony_ci * For now, the PERF_SAMPLE_WEIGHT_STRUCT is the only X86 specific sample type.
12062306a36Sopenharmony_ci * The test only checks the PERF_SAMPLE_WEIGHT_STRUCT type.
12162306a36Sopenharmony_ci */
12262306a36Sopenharmony_ciint test__x86_sample_parsing(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	return do_test(PERF_SAMPLE_WEIGHT_STRUCT);
12562306a36Sopenharmony_ci}
126