xref: /linux/tools/perf/tests/pmu.c (revision bfb921b2a9d5d1123d1d10b196a39db629ddef87)
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 "tests.h"
7 #include "debug.h"
8 #include "fncache.h"
9 #include <api/fs/fs.h>
10 #include <ctype.h>
11 #include <dirent.h>
12 #include <errno.h>
13 #include <fcntl.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <unistd.h>
17 #include <sys/stat.h>
18 #include <sys/types.h>
19 
20 /* Fake PMUs created in temp directory. */
21 static LIST_HEAD(test_pmus);
22 
23 /* Cleanup test PMU directory. */
24 static int test_pmu_put(const char *dir, struct perf_pmu *pmu)
25 {
26 	char buf[PATH_MAX + 20];
27 	int ret;
28 
29 	if (scnprintf(buf, sizeof(buf), "rm -fr %s", dir) < 0) {
30 		pr_err("Failure to set up buffer for \"%s\"\n", dir);
31 		return -EINVAL;
32 	}
33 	ret = system(buf);
34 	if (ret)
35 		pr_err("Failure to \"%s\"\n", buf);
36 
37 	list_del(&pmu->list);
38 	perf_pmu__delete(pmu);
39 	return ret;
40 }
41 
42 /*
43  * Prepare test PMU directory data, normally exported by kernel at
44  * /sys/bus/event_source/devices/<pmu>/. Give as input a buffer to hold the file
45  * path, the result is PMU loaded using that directory.
46  */
47 static struct perf_pmu *test_pmu_get(char *dir, size_t sz)
48 {
49 	/* Simulated format definitions. */
50 	const struct test_format {
51 		const char *name;
52 		const char *value;
53 	} test_formats[] = {
54 		{ "krava01", "config:0-1,62-63\n", },
55 		{ "krava02", "config:10-17\n", },
56 		{ "krava03", "config:5\n", },
57 		{ "krava11", "config1:0,2,4,6,8,20-28\n", },
58 		{ "krava12", "config1:63\n", },
59 		{ "krava13", "config1:45-47\n", },
60 		{ "krava21", "config2:0-3,10-13,20-23,30-33,40-43,50-53,60-63\n", },
61 		{ "krava22", "config2:8,18,48,58\n", },
62 		{ "krava23", "config2:28-29,38\n", },
63 	};
64 	const char *test_event = "krava01=15,krava02=170,krava03=1,krava11=27,krava12=1,"
65 		"krava13=2,krava21=119,krava22=11,krava23=2\n";
66 
67 	char name[PATH_MAX];
68 	int dirfd, file;
69 	struct perf_pmu *pmu = NULL;
70 	ssize_t len;
71 
72 	/* Create equivalent of sysfs mount point. */
73 	scnprintf(dir, sz, "/tmp/perf-pmu-test-XXXXXX");
74 	if (!mkdtemp(dir)) {
75 		pr_err("mkdtemp failed\n");
76 		dir[0] = '\0';
77 		return NULL;
78 	}
79 	dirfd = open(dir, O_DIRECTORY);
80 	if (dirfd < 0) {
81 		pr_err("Failed to open test directory \"%s\"\n", dir);
82 		goto err_out;
83 	}
84 
85 	/* Create the test PMU directory and give it a perf_event_attr type number. */
86 	if (mkdirat(dirfd, "perf-pmu-test", 0755) < 0) {
87 		pr_err("Failed to mkdir PMU directory\n");
88 		goto err_out;
89 	}
90 	file = openat(dirfd, "perf-pmu-test/type", O_WRONLY | O_CREAT, 0600);
91 	if (!file) {
92 		pr_err("Failed to open for writing file \"type\"\n");
93 		goto err_out;
94 	}
95 	len = strlen("9999");
96 	if (write(file, "9999\n", len) < len) {
97 		close(file);
98 		pr_err("Failed to write to 'type' file\n");
99 		goto err_out;
100 	}
101 	close(file);
102 
103 	/* Create format directory and files. */
104 	if (mkdirat(dirfd, "perf-pmu-test/format", 0755) < 0) {
105 		pr_err("Failed to mkdir PMU format directory\n)");
106 		goto err_out;
107 	}
108 	for (size_t i = 0; i < ARRAY_SIZE(test_formats); i++) {
109 		const struct test_format *format = &test_formats[i];
110 
111 		if (scnprintf(name, PATH_MAX, "perf-pmu-test/format/%s", format->name) < 0) {
112 			pr_err("Failure to set up path for \"%s\"\n", format->name);
113 			goto err_out;
114 		}
115 		file = openat(dirfd, name, O_WRONLY | O_CREAT, 0600);
116 		if (!file) {
117 			pr_err("Failed to open for writing file \"%s\"\n", name);
118 			goto err_out;
119 		}
120 
121 		if (write(file, format->value, strlen(format->value)) < 0) {
122 			pr_err("Failed to write to file \"%s\"\n", name);
123 			close(file);
124 			goto err_out;
125 		}
126 		close(file);
127 	}
128 
129 	/* Create test event. */
130 	if (mkdirat(dirfd, "perf-pmu-test/events", 0755) < 0) {
131 		pr_err("Failed to mkdir PMU events directory\n");
132 		goto err_out;
133 	}
134 	file = openat(dirfd, "perf-pmu-test/events/test-event", O_WRONLY | O_CREAT, 0600);
135 	if (!file) {
136 		pr_err("Failed to open for writing file \"type\"\n");
137 		goto err_out;
138 	}
139 	len = strlen(test_event);
140 	if (write(file, test_event, len) < len) {
141 		close(file);
142 		pr_err("Failed to write to 'test-event' file\n");
143 		goto err_out;
144 	}
145 	close(file);
146 
147 	/* Make the PMU reading the files created above. */
148 	pmu = perf_pmus__add_test_pmu(dirfd, "perf-pmu-test");
149 	if (!pmu)
150 		pr_err("Test PMU creation failed\n");
151 
152 err_out:
153 	if (!pmu)
154 		test_pmu_put(dir, pmu);
155 	if (dirfd >= 0)
156 		close(dirfd);
157 	return pmu;
158 }
159 
160 static int test__pmu_format(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
161 {
162 	char dir[PATH_MAX];
163 	struct perf_event_attr attr;
164 	struct parse_events_terms terms;
165 	int ret = TEST_FAIL;
166 	struct perf_pmu *pmu = test_pmu_get(dir, sizeof(dir));
167 
168 	if (!pmu)
169 		return TEST_FAIL;
170 
171 	parse_events_terms__init(&terms);
172 	if (parse_events_terms(&terms,
173 				"krava01=15,krava02=170,krava03=1,krava11=27,krava12=1,"
174 				"krava13=2,krava21=119,krava22=11,krava23=2",
175 				NULL)) {
176 		pr_err("Term parsing failed\n");
177 		goto err_out;
178 	}
179 
180 	memset(&attr, 0, sizeof(attr));
181 	ret = perf_pmu__config_terms(pmu, &attr, &terms, /*zero=*/false, /*err=*/NULL);
182 	if (ret) {
183 		pr_err("perf_pmu__config_terms failed");
184 		goto err_out;
185 	}
186 
187 	if (attr.config  != 0xc00000000002a823) {
188 		pr_err("Unexpected config value %llx\n", attr.config);
189 		goto err_out;
190 	}
191 	if (attr.config1 != 0x8000400000000145) {
192 		pr_err("Unexpected config1 value %llx\n", attr.config1);
193 		goto err_out;
194 	}
195 	if (attr.config2 != 0x0400000020041d07) {
196 		pr_err("Unexpected config2 value %llx\n", attr.config2);
197 		goto err_out;
198 	}
199 
200 	ret = TEST_OK;
201 err_out:
202 	parse_events_terms__exit(&terms);
203 	test_pmu_put(dir, pmu);
204 	return ret;
205 }
206 
207 static int test__pmu_events(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
208 {
209 	char dir[PATH_MAX];
210 	struct parse_events_error err;
211 	struct evlist *evlist;
212 	struct evsel *evsel;
213 	struct perf_event_attr *attr;
214 	int ret = TEST_FAIL;
215 	struct perf_pmu *pmu = test_pmu_get(dir, sizeof(dir));
216 	const char *event = "perf-pmu-test/test-event/";
217 
218 
219 	if (!pmu)
220 		return TEST_FAIL;
221 
222 	evlist = evlist__new();
223 	if (evlist == NULL) {
224 		pr_err("Failed allocation");
225 		goto err_out;
226 	}
227 	parse_events_error__init(&err);
228 	ret = parse_events(evlist, event, &err);
229 	if (ret) {
230 		pr_debug("failed to parse event '%s', err %d\n", event, ret);
231 		parse_events_error__print(&err, event);
232 		if (parse_events_error__contains(&err, "can't access trace events"))
233 			ret = TEST_SKIP;
234 		goto err_out;
235 	}
236 	evsel = evlist__first(evlist);
237 	attr = &evsel->core.attr;
238 	if (attr->config  != 0xc00000000002a823) {
239 		pr_err("Unexpected config value %llx\n", attr->config);
240 		goto err_out;
241 	}
242 	if (attr->config1 != 0x8000400000000145) {
243 		pr_err("Unexpected config1 value %llx\n", attr->config1);
244 		goto err_out;
245 	}
246 	if (attr->config2 != 0x0400000020041d07) {
247 		pr_err("Unexpected config2 value %llx\n", attr->config2);
248 		goto err_out;
249 	}
250 
251 	ret = TEST_OK;
252 err_out:
253 	parse_events_error__exit(&err);
254 	evlist__delete(evlist);
255 	test_pmu_put(dir, pmu);
256 	return ret;
257 }
258 
259 static bool permitted_event_name(const char *name)
260 {
261 	bool has_lower = false, has_upper = false;
262 
263 	for (size_t i = 0; i < strlen(name); i++) {
264 		char c = name[i];
265 
266 		if (islower(c)) {
267 			if (has_upper)
268 				return false;
269 			has_lower = true;
270 			continue;
271 		}
272 		if (isupper(c)) {
273 			if (has_lower)
274 				return false;
275 			has_upper = true;
276 			continue;
277 		}
278 		if (!isdigit(c) && c != '.' && c != '_' && c != '-')
279 			return false;
280 	}
281 	return true;
282 }
283 
284 static int test__pmu_event_names(struct test_suite *test __maybe_unused,
285 				 int subtest __maybe_unused)
286 {
287 	char path[PATH_MAX];
288 	DIR *pmu_dir, *event_dir;
289 	struct dirent *pmu_dent, *event_dent;
290 	const char *sysfs = sysfs__mountpoint();
291 	int ret = TEST_OK;
292 
293 	if (!sysfs) {
294 		pr_err("Sysfs not mounted\n");
295 		return TEST_FAIL;
296 	}
297 
298 	snprintf(path, sizeof(path), "%s/bus/event_source/devices/", sysfs);
299 	pmu_dir = opendir(path);
300 	if (!pmu_dir) {
301 		pr_err("Error opening \"%s\"\n", path);
302 		return TEST_FAIL;
303 	}
304 	while ((pmu_dent = readdir(pmu_dir))) {
305 		if (!strcmp(pmu_dent->d_name, ".") ||
306 		    !strcmp(pmu_dent->d_name, ".."))
307 			continue;
308 
309 		snprintf(path, sizeof(path), "%s/bus/event_source/devices/%s/type",
310 			 sysfs, pmu_dent->d_name);
311 
312 		/* Does it look like a PMU? */
313 		if (!file_available(path))
314 			continue;
315 
316 		/* Process events. */
317 		snprintf(path, sizeof(path), "%s/bus/event_source/devices/%s/events",
318 			 sysfs, pmu_dent->d_name);
319 
320 		event_dir = opendir(path);
321 		if (!event_dir) {
322 			pr_debug("Skipping as no event directory \"%s\"\n", path);
323 			continue;
324 		}
325 		while ((event_dent = readdir(event_dir))) {
326 			const char *event_name = event_dent->d_name;
327 
328 			if (!strcmp(event_name, ".") || !strcmp(event_name, ".."))
329 				continue;
330 
331 			if (!permitted_event_name(event_name)) {
332 				pr_err("Invalid sysfs event name: %s/%s\n",
333 					pmu_dent->d_name, event_name);
334 				ret = TEST_FAIL;
335 			}
336 		}
337 		closedir(event_dir);
338 	}
339 	closedir(pmu_dir);
340 	return ret;
341 }
342 
343 static struct test_case tests__pmu[] = {
344 	TEST_CASE("Parsing with PMU format directory", pmu_format),
345 	TEST_CASE("Parsing with PMU event", pmu_events),
346 	TEST_CASE("PMU event names", pmu_event_names),
347 	{	.name = NULL, }
348 };
349 
350 struct test_suite suite__pmu = {
351 	.desc = "Sysfs PMU tests",
352 	.test_cases = tests__pmu,
353 };
354