xref: /linux/tools/perf/arch/x86/tests/hybrid.c (revision 9f7861c56b51b84d30114e7fea9d744a9d5ba9b7)
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_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_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 struct evlist_test {
167 	const char *name;
168 	bool (*valid)(void);
169 	int (*check)(struct evlist *evlist);
170 };
171 
172 static const struct evlist_test test__hybrid_events[] = {
173 	{
174 		.name  = "cpu_core/cpu-cycles/",
175 		.check = test__hybrid_hw_event_with_pmu,
176 		/* 0 */
177 	},
178 	{
179 		.name  = "{cpu_core/cpu-cycles/,cpu_core/instructions/}",
180 		.check = test__hybrid_hw_group_event,
181 		/* 1 */
182 	},
183 	{
184 		.name  = "{cpu-clock,cpu_core/cpu-cycles/}",
185 		.check = test__hybrid_sw_hw_group_event,
186 		/* 2 */
187 	},
188 	{
189 		.name  = "{cpu_core/cpu-cycles/,cpu-clock}",
190 		.check = test__hybrid_hw_sw_group_event,
191 		/* 3 */
192 	},
193 	{
194 		.name  = "{cpu_core/cpu-cycles/k,cpu_core/instructions/u}",
195 		.check = test__hybrid_group_modifier1,
196 		/* 4 */
197 	},
198 	{
199 		.name  = "r1a",
200 		.check = test__hybrid_raw1,
201 		/* 5 */
202 	},
203 	{
204 		.name  = "cpu_core/r1a/",
205 		.check = test__hybrid_raw2,
206 		/* 6 */
207 	},
208 	{
209 		.name  = "cpu_core/config=10,config1,config2=3,period=1000/u",
210 		.check = test__checkevent_pmu,
211 		/* 7 */
212 	},
213 	{
214 		.name  = "cpu_core/LLC-loads/",
215 		.check = test__hybrid_cache_event,
216 		/* 8 */
217 	},
218 };
219 
220 static int test_event(const struct evlist_test *e)
221 {
222 	struct parse_events_error err;
223 	struct evlist *evlist;
224 	int ret;
225 
226 	if (e->valid && !e->valid()) {
227 		pr_debug("... SKIP\n");
228 		return TEST_OK;
229 	}
230 
231 	evlist = evlist__new();
232 	if (evlist == NULL) {
233 		pr_err("Failed allocation");
234 		return TEST_FAIL;
235 	}
236 	parse_events_error__init(&err);
237 	ret = parse_events(evlist, e->name, &err);
238 	if (ret) {
239 		pr_debug("failed to parse event '%s', err %d, str '%s'\n",
240 			 e->name, ret, err.str);
241 		parse_events_error__print(&err, e->name);
242 		ret = TEST_FAIL;
243 		if (strstr(err.str, "can't access trace events"))
244 			ret = TEST_SKIP;
245 	} else {
246 		ret = e->check(evlist);
247 	}
248 	parse_events_error__exit(&err);
249 	evlist__delete(evlist);
250 
251 	return ret;
252 }
253 
254 static int combine_test_results(int existing, int latest)
255 {
256 	if (existing == TEST_FAIL)
257 		return TEST_FAIL;
258 	if (existing == TEST_SKIP)
259 		return latest == TEST_OK ? TEST_SKIP : latest;
260 	return latest;
261 }
262 
263 static int test_events(const struct evlist_test *events, int cnt)
264 {
265 	int ret = TEST_OK;
266 
267 	for (int i = 0; i < cnt; i++) {
268 		const struct evlist_test *e = &events[i];
269 		int test_ret;
270 
271 		pr_debug("running test %d '%s'\n", i, e->name);
272 		test_ret = test_event(e);
273 		if (test_ret != TEST_OK) {
274 			pr_debug("Event test failure: test %d '%s'", i, e->name);
275 			ret = combine_test_results(ret, test_ret);
276 		}
277 	}
278 
279 	return ret;
280 }
281 
282 int test__hybrid(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
283 {
284 	if (perf_pmus__num_core_pmus() == 1)
285 		return TEST_SKIP;
286 
287 	return test_events(test__hybrid_events, ARRAY_SIZE(test__hybrid_events));
288 }
289