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