1// SPDX-License-Identifier: GPL-2.0
2#include "arch-tests.h"
3#include "debug.h"
4#include "evlist.h"
5#include "evsel.h"
6#include "pmu.h"
7#include "pmus.h"
8#include "tests/tests.h"
9
10static bool test_config(const struct evsel *evsel, __u64 expected_config)
11{
12	return (evsel->core.attr.config & PERF_HW_EVENT_MASK) == expected_config;
13}
14
15static bool test_perf_config(const struct perf_evsel *evsel, __u64 expected_config)
16{
17	return (evsel->attr.config & PERF_HW_EVENT_MASK) == expected_config;
18}
19
20static bool test_hybrid_type(const struct evsel *evsel, __u64 expected_config)
21{
22	return (evsel->core.attr.config >> PERF_PMU_TYPE_SHIFT) == expected_config;
23}
24
25static int test__hybrid_hw_event_with_pmu(struct evlist *evlist)
26{
27	struct evsel *evsel = evlist__first(evlist);
28
29	TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries);
30	TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
31	TEST_ASSERT_VAL("wrong hybrid type", test_hybrid_type(evsel, PERF_TYPE_RAW));
32	TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES));
33	return TEST_OK;
34}
35
36static int test__hybrid_hw_group_event(struct evlist *evlist)
37{
38	struct evsel *evsel, *leader;
39
40	evsel = leader = evlist__first(evlist);
41	TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries);
42	TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
43	TEST_ASSERT_VAL("wrong hybrid type", test_hybrid_type(evsel, PERF_TYPE_RAW));
44	TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES));
45	TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader));
46
47	evsel = evsel__next(evsel);
48	TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
49	TEST_ASSERT_VAL("wrong hybrid type", test_hybrid_type(evsel, PERF_TYPE_RAW));
50	TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_BRANCH_INSTRUCTIONS));
51	TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader));
52	return TEST_OK;
53}
54
55static int test__hybrid_sw_hw_group_event(struct evlist *evlist)
56{
57	struct evsel *evsel, *leader;
58
59	evsel = leader = evlist__first(evlist);
60	TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries);
61	TEST_ASSERT_VAL("wrong type", PERF_TYPE_SOFTWARE == evsel->core.attr.type);
62	TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader));
63
64	evsel = evsel__next(evsel);
65	TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
66	TEST_ASSERT_VAL("wrong hybrid type", test_hybrid_type(evsel, PERF_TYPE_RAW));
67	TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES));
68	TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader));
69	return TEST_OK;
70}
71
72static int test__hybrid_hw_sw_group_event(struct evlist *evlist)
73{
74	struct evsel *evsel, *leader;
75
76	evsel = leader = evlist__first(evlist);
77	TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries);
78	TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
79	TEST_ASSERT_VAL("wrong hybrid type", test_hybrid_type(evsel, PERF_TYPE_RAW));
80	TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES));
81	TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader));
82
83	evsel = evsel__next(evsel);
84	TEST_ASSERT_VAL("wrong type", PERF_TYPE_SOFTWARE == evsel->core.attr.type);
85	TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader));
86	return TEST_OK;
87}
88
89static int test__hybrid_group_modifier1(struct evlist *evlist)
90{
91	struct evsel *evsel, *leader;
92
93	evsel = leader = evlist__first(evlist);
94	TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries);
95	TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
96	TEST_ASSERT_VAL("wrong hybrid type", test_hybrid_type(evsel, PERF_TYPE_RAW));
97	TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES));
98	TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader));
99	TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user);
100	TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel);
101
102	evsel = evsel__next(evsel);
103	TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
104	TEST_ASSERT_VAL("wrong hybrid type", test_hybrid_type(evsel, PERF_TYPE_RAW));
105	TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_BRANCH_INSTRUCTIONS));
106	TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader));
107	TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user);
108	TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel);
109	return TEST_OK;
110}
111
112static int test__hybrid_raw1(struct evlist *evlist)
113{
114	struct perf_evsel *evsel;
115
116	perf_evlist__for_each_evsel(&evlist->core, evsel) {
117		struct perf_pmu *pmu = perf_pmus__find_by_type(evsel->attr.type);
118
119		TEST_ASSERT_VAL("missing pmu", pmu);
120		TEST_ASSERT_VAL("unexpected pmu", !strncmp(pmu->name, "cpu_", 4));
121		TEST_ASSERT_VAL("wrong config", test_perf_config(evsel, 0x1a));
122	}
123	return TEST_OK;
124}
125
126static int test__hybrid_raw2(struct evlist *evlist)
127{
128	struct evsel *evsel = evlist__first(evlist);
129
130	TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries);
131	TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type);
132	TEST_ASSERT_VAL("wrong config", test_config(evsel, 0x1a));
133	return TEST_OK;
134}
135
136static int test__hybrid_cache_event(struct evlist *evlist)
137{
138	struct evsel *evsel = evlist__first(evlist);
139
140	TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries);
141	TEST_ASSERT_VAL("wrong type", PERF_TYPE_HW_CACHE == evsel->core.attr.type);
142	TEST_ASSERT_VAL("wrong config", 0x2 == (evsel->core.attr.config & 0xffffffff));
143	return TEST_OK;
144}
145
146static int test__checkevent_pmu(struct evlist *evlist)
147{
148
149	struct evsel *evsel = evlist__first(evlist);
150
151	TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries);
152	TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type);
153	TEST_ASSERT_VAL("wrong config",    10 == evsel->core.attr.config);
154	TEST_ASSERT_VAL("wrong config1",    1 == evsel->core.attr.config1);
155	TEST_ASSERT_VAL("wrong config2",    3 == evsel->core.attr.config2);
156	TEST_ASSERT_VAL("wrong config3",    0 == evsel->core.attr.config3);
157	/*
158	 * The period value gets configured within evlist__config,
159	 * while this test executes only parse events method.
160	 */
161	TEST_ASSERT_VAL("wrong period",     0 == evsel->core.attr.sample_period);
162
163	return TEST_OK;
164}
165
166static int test__hybrid_hw_group_event_2(struct evlist *evlist)
167{
168	struct evsel *evsel, *leader;
169
170	evsel = leader = evlist__first(evlist);
171	TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries);
172	TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
173	TEST_ASSERT_VAL("wrong hybrid type", test_hybrid_type(evsel, PERF_TYPE_RAW));
174	TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES));
175	TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader));
176
177	evsel = evsel__next(evsel);
178	TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type);
179	TEST_ASSERT_VAL("wrong config", evsel->core.attr.config == 0x3c);
180	TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader));
181	return TEST_OK;
182}
183
184struct evlist_test {
185	const char *name;
186	bool (*valid)(void);
187	int (*check)(struct evlist *evlist);
188};
189
190static const struct evlist_test test__hybrid_events[] = {
191	{
192		.name  = "cpu_core/cycles/",
193		.check = test__hybrid_hw_event_with_pmu,
194		/* 0 */
195	},
196	{
197		.name  = "{cpu_core/cycles/,cpu_core/branches/}",
198		.check = test__hybrid_hw_group_event,
199		/* 1 */
200	},
201	{
202		.name  = "{cpu-clock,cpu_core/cycles/}",
203		.check = test__hybrid_sw_hw_group_event,
204		/* 2 */
205	},
206	{
207		.name  = "{cpu_core/cycles/,cpu-clock}",
208		.check = test__hybrid_hw_sw_group_event,
209		/* 3 */
210	},
211	{
212		.name  = "{cpu_core/cycles/k,cpu_core/branches/u}",
213		.check = test__hybrid_group_modifier1,
214		/* 4 */
215	},
216	{
217		.name  = "r1a",
218		.check = test__hybrid_raw1,
219		/* 5 */
220	},
221	{
222		.name  = "cpu_core/r1a/",
223		.check = test__hybrid_raw2,
224		/* 6 */
225	},
226	{
227		.name  = "cpu_core/config=10,config1,config2=3,period=1000/u",
228		.check = test__checkevent_pmu,
229		/* 7 */
230	},
231	{
232		.name  = "cpu_core/LLC-loads/",
233		.check = test__hybrid_cache_event,
234		/* 8 */
235	},
236	{
237		.name  = "{cpu_core/cycles/,cpu_core/cpu-cycles/}",
238		.check = test__hybrid_hw_group_event_2,
239		/* 9 */
240	},
241};
242
243static int test_event(const struct evlist_test *e)
244{
245	struct parse_events_error err;
246	struct evlist *evlist;
247	int ret;
248
249	if (e->valid && !e->valid()) {
250		pr_debug("... SKIP\n");
251		return TEST_OK;
252	}
253
254	evlist = evlist__new();
255	if (evlist == NULL) {
256		pr_err("Failed allocation");
257		return TEST_FAIL;
258	}
259	parse_events_error__init(&err);
260	ret = parse_events(evlist, e->name, &err);
261	if (ret) {
262		pr_debug("failed to parse event '%s', err %d\n", e->name, ret);
263		parse_events_error__print(&err, e->name);
264		ret = TEST_FAIL;
265		if (parse_events_error__contains(&err, "can't access trace events"))
266			ret = TEST_SKIP;
267	} else {
268		ret = e->check(evlist);
269	}
270	parse_events_error__exit(&err);
271	evlist__delete(evlist);
272
273	return ret;
274}
275
276static int combine_test_results(int existing, int latest)
277{
278	if (existing == TEST_FAIL)
279		return TEST_FAIL;
280	if (existing == TEST_SKIP)
281		return latest == TEST_OK ? TEST_SKIP : latest;
282	return latest;
283}
284
285static int test_events(const struct evlist_test *events, int cnt)
286{
287	int ret = TEST_OK;
288
289	for (int i = 0; i < cnt; i++) {
290		const struct evlist_test *e = &events[i];
291		int test_ret;
292
293		pr_debug("running test %d '%s'\n", i, e->name);
294		test_ret = test_event(e);
295		if (test_ret != TEST_OK) {
296			pr_debug("Event test failure: test %d '%s'", i, e->name);
297			ret = combine_test_results(ret, test_ret);
298		}
299	}
300
301	return ret;
302}
303
304int test__hybrid(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
305{
306	if (perf_pmus__num_core_pmus() == 1)
307		return TEST_SKIP;
308
309	return test_events(test__hybrid_events, ARRAY_SIZE(test__hybrid_events));
310}
311