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