xref: /linux/tools/perf/tests/pmu.c (revision 6c1785cd75ef55a308701813330a162002ffe192)
1 // SPDX-License-Identifier: GPL-2.0
2 #include "evlist.h"
3 #include "evsel.h"
4 #include "parse-events.h"
5 #include "pmu.h"
6 #include "pmus.h"
7 #include "tests.h"
8 #include "debug.h"
9 #include "fncache.h"
10 #include <api/fs/fs.h>
11 #include <ctype.h>
12 #include <dirent.h>
13 #include <errno.h>
14 #include <fcntl.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <unistd.h>
18 #include <sys/stat.h>
19 #include <sys/types.h>
20 
21 /* Fake PMUs created in temp directory. */
22 static LIST_HEAD(test_pmus);
23 
24 /* Cleanup test PMU directory. */
25 static int test_pmu_put(const char *dir, struct perf_pmu *pmu)
26 {
27 	char buf[PATH_MAX + 20];
28 	int ret;
29 
30 	if (scnprintf(buf, sizeof(buf), "rm -fr %s", dir) < 0) {
31 		pr_err("Failure to set up buffer for \"%s\"\n", dir);
32 		return -EINVAL;
33 	}
34 	ret = system(buf);
35 	if (ret)
36 		pr_err("Failure to \"%s\"\n", buf);
37 
38 	list_del(&pmu->list);
39 	perf_pmu__delete(pmu);
40 	return ret;
41 }
42 
43 /*
44  * Prepare test PMU directory data, normally exported by kernel at
45  * /sys/bus/event_source/devices/<pmu>/. Give as input a buffer to hold the file
46  * path, the result is PMU loaded using that directory.
47  */
48 static struct perf_pmu *test_pmu_get(char *dir, size_t sz)
49 {
50 	/* Simulated format definitions. */
51 	const struct test_format {
52 		const char *name;
53 		const char *value;
54 	} test_formats[] = {
55 		{ "krava01", "config:0-1,62-63\n", },
56 		{ "krava02", "config:10-17\n", },
57 		{ "krava03", "config:5\n", },
58 		{ "krava11", "config1:0,2,4,6,8,20-28\n", },
59 		{ "krava12", "config1:63\n", },
60 		{ "krava13", "config1:45-47\n", },
61 		{ "krava21", "config2:0-3,10-13,20-23,30-33,40-43,50-53,60-63\n", },
62 		{ "krava22", "config2:8,18,48,58\n", },
63 		{ "krava23", "config2:28-29,38\n", },
64 	};
65 	const char *test_event = "krava01=15,krava02=170,krava03=1,krava11=27,krava12=1,"
66 		"krava13=2,krava21=119,krava22=11,krava23=2\n";
67 
68 	char name[PATH_MAX];
69 	int dirfd, file;
70 	struct perf_pmu *pmu = NULL;
71 	ssize_t len;
72 
73 	/* Create equivalent of sysfs mount point. */
74 	scnprintf(dir, sz, "/tmp/perf-pmu-test-XXXXXX");
75 	if (!mkdtemp(dir)) {
76 		pr_err("mkdtemp failed\n");
77 		dir[0] = '\0';
78 		return NULL;
79 	}
80 	dirfd = open(dir, O_DIRECTORY);
81 	if (dirfd < 0) {
82 		pr_err("Failed to open test directory \"%s\"\n", dir);
83 		goto err_out;
84 	}
85 
86 	/* Create the test PMU directory and give it a perf_event_attr type number. */
87 	if (mkdirat(dirfd, "perf-pmu-test", 0755) < 0) {
88 		pr_err("Failed to mkdir PMU directory\n");
89 		goto err_out;
90 	}
91 	file = openat(dirfd, "perf-pmu-test/type", O_WRONLY | O_CREAT, 0600);
92 	if (!file) {
93 		pr_err("Failed to open for writing file \"type\"\n");
94 		goto err_out;
95 	}
96 	len = strlen("9999");
97 	if (write(file, "9999\n", len) < len) {
98 		close(file);
99 		pr_err("Failed to write to 'type' file\n");
100 		goto err_out;
101 	}
102 	close(file);
103 
104 	/* Create format directory and files. */
105 	if (mkdirat(dirfd, "perf-pmu-test/format", 0755) < 0) {
106 		pr_err("Failed to mkdir PMU format directory\n)");
107 		goto err_out;
108 	}
109 	for (size_t i = 0; i < ARRAY_SIZE(test_formats); i++) {
110 		const struct test_format *format = &test_formats[i];
111 
112 		if (scnprintf(name, PATH_MAX, "perf-pmu-test/format/%s", format->name) < 0) {
113 			pr_err("Failure to set up path for \"%s\"\n", format->name);
114 			goto err_out;
115 		}
116 		file = openat(dirfd, name, O_WRONLY | O_CREAT, 0600);
117 		if (!file) {
118 			pr_err("Failed to open for writing file \"%s\"\n", name);
119 			goto err_out;
120 		}
121 
122 		if (write(file, format->value, strlen(format->value)) < 0) {
123 			pr_err("Failed to write to file \"%s\"\n", name);
124 			close(file);
125 			goto err_out;
126 		}
127 		close(file);
128 	}
129 
130 	/* Create test event. */
131 	if (mkdirat(dirfd, "perf-pmu-test/events", 0755) < 0) {
132 		pr_err("Failed to mkdir PMU events directory\n");
133 		goto err_out;
134 	}
135 	file = openat(dirfd, "perf-pmu-test/events/test-event", O_WRONLY | O_CREAT, 0600);
136 	if (!file) {
137 		pr_err("Failed to open for writing file \"type\"\n");
138 		goto err_out;
139 	}
140 	len = strlen(test_event);
141 	if (write(file, test_event, len) < len) {
142 		close(file);
143 		pr_err("Failed to write to 'test-event' file\n");
144 		goto err_out;
145 	}
146 	close(file);
147 
148 	/* Make the PMU reading the files created above. */
149 	pmu = perf_pmus__add_test_pmu(dirfd, "perf-pmu-test");
150 	if (!pmu)
151 		pr_err("Test PMU creation failed\n");
152 
153 err_out:
154 	if (!pmu)
155 		test_pmu_put(dir, pmu);
156 	if (dirfd >= 0)
157 		close(dirfd);
158 	return pmu;
159 }
160 
161 static int test__pmu_format(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
162 {
163 	char dir[PATH_MAX];
164 	struct perf_event_attr attr;
165 	struct parse_events_terms terms;
166 	int ret = TEST_FAIL;
167 	struct perf_pmu *pmu = test_pmu_get(dir, sizeof(dir));
168 
169 	if (!pmu)
170 		return TEST_FAIL;
171 
172 	parse_events_terms__init(&terms);
173 	if (parse_events_terms(&terms,
174 				"krava01=15,krava02=170,krava03=1,krava11=27,krava12=1,"
175 				"krava13=2,krava21=119,krava22=11,krava23=2",
176 				NULL)) {
177 		pr_err("Term parsing failed\n");
178 		goto err_out;
179 	}
180 
181 	memset(&attr, 0, sizeof(attr));
182 	ret = perf_pmu__config_terms(pmu, &attr, &terms, /*zero=*/false, /*err=*/NULL);
183 	if (ret) {
184 		pr_err("perf_pmu__config_terms failed");
185 		goto err_out;
186 	}
187 
188 	if (attr.config  != 0xc00000000002a823) {
189 		pr_err("Unexpected config value %llx\n", attr.config);
190 		goto err_out;
191 	}
192 	if (attr.config1 != 0x8000400000000145) {
193 		pr_err("Unexpected config1 value %llx\n", attr.config1);
194 		goto err_out;
195 	}
196 	if (attr.config2 != 0x0400000020041d07) {
197 		pr_err("Unexpected config2 value %llx\n", attr.config2);
198 		goto err_out;
199 	}
200 
201 	ret = TEST_OK;
202 err_out:
203 	parse_events_terms__exit(&terms);
204 	test_pmu_put(dir, pmu);
205 	return ret;
206 }
207 
208 static int test__pmu_events(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
209 {
210 	char dir[PATH_MAX];
211 	struct parse_events_error err;
212 	struct evlist *evlist;
213 	struct evsel *evsel;
214 	struct perf_event_attr *attr;
215 	int ret = TEST_FAIL;
216 	struct perf_pmu *pmu = test_pmu_get(dir, sizeof(dir));
217 	const char *event = "perf-pmu-test/test-event/";
218 
219 
220 	if (!pmu)
221 		return TEST_FAIL;
222 
223 	evlist = evlist__new();
224 	if (evlist == NULL) {
225 		pr_err("Failed allocation");
226 		goto err_out;
227 	}
228 	parse_events_error__init(&err);
229 	ret = parse_events(evlist, event, &err);
230 	if (ret) {
231 		pr_debug("failed to parse event '%s', err %d\n", event, ret);
232 		parse_events_error__print(&err, event);
233 		if (parse_events_error__contains(&err, "can't access trace events"))
234 			ret = TEST_SKIP;
235 		goto err_out;
236 	}
237 	evsel = evlist__first(evlist);
238 	attr = &evsel->core.attr;
239 	if (attr->config  != 0xc00000000002a823) {
240 		pr_err("Unexpected config value %llx\n", attr->config);
241 		goto err_out;
242 	}
243 	if (attr->config1 != 0x8000400000000145) {
244 		pr_err("Unexpected config1 value %llx\n", attr->config1);
245 		goto err_out;
246 	}
247 	if (attr->config2 != 0x0400000020041d07) {
248 		pr_err("Unexpected config2 value %llx\n", attr->config2);
249 		goto err_out;
250 	}
251 
252 	ret = TEST_OK;
253 err_out:
254 	parse_events_error__exit(&err);
255 	evlist__delete(evlist);
256 	test_pmu_put(dir, pmu);
257 	return ret;
258 }
259 
260 static bool permitted_event_name(const char *name)
261 {
262 	bool has_lower = false, has_upper = false;
263 
264 	for (size_t i = 0; i < strlen(name); i++) {
265 		char c = name[i];
266 
267 		if (islower(c)) {
268 			if (has_upper)
269 				return false;
270 			has_lower = true;
271 			continue;
272 		}
273 		if (isupper(c)) {
274 			if (has_lower)
275 				return false;
276 			has_upper = true;
277 			continue;
278 		}
279 		if (!isdigit(c) && c != '.' && c != '_' && c != '-')
280 			return false;
281 	}
282 	return true;
283 }
284 
285 static int test__pmu_event_names(struct test_suite *test __maybe_unused,
286 				 int subtest __maybe_unused)
287 {
288 	char path[PATH_MAX];
289 	DIR *pmu_dir, *event_dir;
290 	struct dirent *pmu_dent, *event_dent;
291 	const char *sysfs = sysfs__mountpoint();
292 	int ret = TEST_OK;
293 
294 	if (!sysfs) {
295 		pr_err("Sysfs not mounted\n");
296 		return TEST_FAIL;
297 	}
298 
299 	snprintf(path, sizeof(path), "%s/bus/event_source/devices/", sysfs);
300 	pmu_dir = opendir(path);
301 	if (!pmu_dir) {
302 		pr_err("Error opening \"%s\"\n", path);
303 		return TEST_FAIL;
304 	}
305 	while ((pmu_dent = readdir(pmu_dir))) {
306 		if (!strcmp(pmu_dent->d_name, ".") ||
307 		    !strcmp(pmu_dent->d_name, ".."))
308 			continue;
309 
310 		snprintf(path, sizeof(path), "%s/bus/event_source/devices/%s/type",
311 			 sysfs, pmu_dent->d_name);
312 
313 		/* Does it look like a PMU? */
314 		if (!file_available(path))
315 			continue;
316 
317 		/* Process events. */
318 		snprintf(path, sizeof(path), "%s/bus/event_source/devices/%s/events",
319 			 sysfs, pmu_dent->d_name);
320 
321 		event_dir = opendir(path);
322 		if (!event_dir) {
323 			pr_debug("Skipping as no event directory \"%s\"\n", path);
324 			continue;
325 		}
326 		while ((event_dent = readdir(event_dir))) {
327 			const char *event_name = event_dent->d_name;
328 
329 			if (!strcmp(event_name, ".") || !strcmp(event_name, ".."))
330 				continue;
331 
332 			if (!permitted_event_name(event_name)) {
333 				pr_err("Invalid sysfs event name: %s/%s\n",
334 					pmu_dent->d_name, event_name);
335 				ret = TEST_FAIL;
336 			}
337 		}
338 		closedir(event_dir);
339 	}
340 	closedir(pmu_dir);
341 	return ret;
342 }
343 
344 static const char * const uncore_chas[] = {
345 	"uncore_cha_0",
346 	"uncore_cha_1",
347 	"uncore_cha_2",
348 	"uncore_cha_3",
349 	"uncore_cha_4",
350 	"uncore_cha_5",
351 	"uncore_cha_6",
352 	"uncore_cha_7",
353 	"uncore_cha_8",
354 	"uncore_cha_9",
355 	"uncore_cha_10",
356 	"uncore_cha_11",
357 	"uncore_cha_12",
358 	"uncore_cha_13",
359 	"uncore_cha_14",
360 	"uncore_cha_15",
361 	"uncore_cha_16",
362 	"uncore_cha_17",
363 	"uncore_cha_18",
364 	"uncore_cha_19",
365 	"uncore_cha_20",
366 	"uncore_cha_21",
367 	"uncore_cha_22",
368 	"uncore_cha_23",
369 	"uncore_cha_24",
370 	"uncore_cha_25",
371 	"uncore_cha_26",
372 	"uncore_cha_27",
373 	"uncore_cha_28",
374 	"uncore_cha_29",
375 	"uncore_cha_30",
376 	"uncore_cha_31",
377 };
378 
379 static const char * const mrvl_ddrs[] = {
380 	"mrvl_ddr_pmu_87e1b0000000",
381 	"mrvl_ddr_pmu_87e1b1000000",
382 	"mrvl_ddr_pmu_87e1b2000000",
383 	"mrvl_ddr_pmu_87e1b3000000",
384 	"mrvl_ddr_pmu_87e1b4000000",
385 	"mrvl_ddr_pmu_87e1b5000000",
386 	"mrvl_ddr_pmu_87e1b6000000",
387 	"mrvl_ddr_pmu_87e1b7000000",
388 	"mrvl_ddr_pmu_87e1b8000000",
389 	"mrvl_ddr_pmu_87e1b9000000",
390 	"mrvl_ddr_pmu_87e1ba000000",
391 	"mrvl_ddr_pmu_87e1bb000000",
392 	"mrvl_ddr_pmu_87e1bc000000",
393 	"mrvl_ddr_pmu_87e1bd000000",
394 	"mrvl_ddr_pmu_87e1be000000",
395 	"mrvl_ddr_pmu_87e1bf000000",
396 };
397 
398 static int test__name_len(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
399 {
400 	TEST_ASSERT_VAL("cpu", pmu_name_len_no_suffix("cpu") == strlen("cpu"));
401 	TEST_ASSERT_VAL("i915", pmu_name_len_no_suffix("i915") == strlen("i915"));
402 	TEST_ASSERT_VAL("cpum_cf", pmu_name_len_no_suffix("cpum_cf") == strlen("cpum_cf"));
403 	for (size_t i = 0; i < ARRAY_SIZE(uncore_chas); i++) {
404 		TEST_ASSERT_VAL("Strips uncore_cha suffix",
405 				pmu_name_len_no_suffix(uncore_chas[i]) ==
406 				strlen("uncore_cha"));
407 	}
408 	for (size_t i = 0; i < ARRAY_SIZE(mrvl_ddrs); i++) {
409 		TEST_ASSERT_VAL("Strips mrvl_ddr_pmu suffix",
410 				pmu_name_len_no_suffix(mrvl_ddrs[i]) ==
411 				strlen("mrvl_ddr_pmu"));
412 	}
413 	return TEST_OK;
414 }
415 
416 static int test__name_cmp(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
417 {
418 	TEST_ASSERT_EQUAL("cpu", pmu_name_cmp("cpu", "cpu"), 0);
419 	TEST_ASSERT_EQUAL("i915", pmu_name_cmp("i915", "i915"), 0);
420 	TEST_ASSERT_EQUAL("cpum_cf", pmu_name_cmp("cpum_cf", "cpum_cf"), 0);
421 	TEST_ASSERT_VAL("i915", pmu_name_cmp("cpu", "i915") < 0);
422 	TEST_ASSERT_VAL("i915", pmu_name_cmp("i915", "cpu") > 0);
423 	TEST_ASSERT_VAL("cpum_cf", pmu_name_cmp("cpum_cf", "cpum_ce") > 0);
424 	TEST_ASSERT_VAL("cpum_cf", pmu_name_cmp("cpum_cf", "cpum_d0") < 0);
425 	for (size_t i = 1; i < ARRAY_SIZE(uncore_chas); i++) {
426 		TEST_ASSERT_VAL("uncore_cha suffixes ordered lt",
427 				pmu_name_cmp(uncore_chas[i-1], uncore_chas[i]) < 0);
428 		TEST_ASSERT_VAL("uncore_cha suffixes ordered gt",
429 				pmu_name_cmp(uncore_chas[i], uncore_chas[i-1]) > 0);
430 	}
431 	for (size_t i = 1; i < ARRAY_SIZE(mrvl_ddrs); i++) {
432 		TEST_ASSERT_VAL("mrvl_ddr_pmu suffixes ordered lt",
433 				pmu_name_cmp(mrvl_ddrs[i-1], mrvl_ddrs[i]) < 0);
434 		TEST_ASSERT_VAL("mrvl_ddr_pmu suffixes ordered gt",
435 				pmu_name_cmp(mrvl_ddrs[i], mrvl_ddrs[i-1]) > 0);
436 	}
437 	return TEST_OK;
438 }
439 
440 static struct test_case tests__pmu[] = {
441 	TEST_CASE("Parsing with PMU format directory", pmu_format),
442 	TEST_CASE("Parsing with PMU event", pmu_events),
443 	TEST_CASE("PMU event names", pmu_event_names),
444 	TEST_CASE("PMU name combining", name_len),
445 	TEST_CASE("PMU name comparison", name_cmp),
446 	{	.name = NULL, }
447 };
448 
449 struct test_suite suite__pmu = {
450 	.desc = "Sysfs PMU tests",
451 	.test_cases = tests__pmu,
452 };
453