xref: /linux/tools/perf/arch/x86/tests/hybrid.c (revision 4b660dbd9ee2059850fd30e0df420ca7a38a1856)
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 
10 static 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 
15 static 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 
20 static 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 
25 static 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 
36 static 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 
55 static 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 
72 static 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 
89 static 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 
112 static 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 
126 static 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 
136 static 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 
146 static 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 
166 static 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 
184 struct evlist_test {
185 	const char *name;
186 	bool (*valid)(void);
187 	int (*check)(struct evlist *evlist);
188 };
189 
190 static 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 
243 static 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 
276 static 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 
285 static 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 
304 int 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