Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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. */
22static LIST_HEAD(test_pmus);
23
24/* Cleanup test PMU directory. */
25static 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 */
48static 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
153err_out:
154 if (!pmu)
155 test_pmu_put(dir, pmu);
156 if (dirfd >= 0)
157 close(dirfd);
158 return pmu;
159}
160
161static 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;
202err_out:
203 parse_events_terms__exit(&terms);
204 test_pmu_put(dir, pmu);
205 return ret;
206}
207
208static 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;
253err_out:
254 parse_events_error__exit(&err);
255 evlist__delete(evlist);
256 test_pmu_put(dir, pmu);
257 return ret;
258}
259
260static bool permitted_event_name(const char *name)
261{
262 bool has_lower = false, has_upper = false;
263 __u64 config;
264
265 for (size_t i = 0; i < strlen(name); i++) {
266 char c = name[i];
267
268 if (islower(c)) {
269 if (has_upper)
270 goto check_legacy;
271 has_lower = true;
272 continue;
273 }
274 if (isupper(c)) {
275 if (has_lower)
276 goto check_legacy;
277 has_upper = true;
278 continue;
279 }
280 if (!isdigit(c) && c != '.' && c != '_' && c != '-')
281 goto check_legacy;
282 }
283 return true;
284check_legacy:
285 /*
286 * If the event name matches a legacy cache name the legacy encoding
287 * will still be used. This isn't quite WAI as sysfs events should take
288 * priority, but this case happens on PowerPC and matches the behavior
289 * in older perf tools where legacy events were the priority. Be
290 * permissive and assume later PMU drivers will use all lower or upper
291 * case names.
292 */
293 if (parse_events__decode_legacy_cache(name, /*extended_pmu_type=*/0, &config) == 0) {
294 pr_warning("sysfs event '%s' should be all lower/upper case, it will be matched using legacy encoding.",
295 name);
296 return true;
297 }
298 return false;
299}
300
301static int test__pmu_event_names(struct test_suite *test __maybe_unused,
302 int subtest __maybe_unused)
303{
304 char path[PATH_MAX];
305 DIR *pmu_dir, *event_dir;
306 struct dirent *pmu_dent, *event_dent;
307 const char *sysfs = sysfs__mountpoint();
308 int ret = TEST_OK;
309
310 if (!sysfs) {
311 pr_err("Sysfs not mounted\n");
312 return TEST_FAIL;
313 }
314
315 snprintf(path, sizeof(path), "%s/bus/event_source/devices/", sysfs);
316 pmu_dir = opendir(path);
317 if (!pmu_dir) {
318 pr_err("Error opening \"%s\"\n", path);
319 return TEST_FAIL;
320 }
321 while ((pmu_dent = readdir(pmu_dir))) {
322 if (!strcmp(pmu_dent->d_name, ".") ||
323 !strcmp(pmu_dent->d_name, ".."))
324 continue;
325
326 snprintf(path, sizeof(path), "%s/bus/event_source/devices/%s/type",
327 sysfs, pmu_dent->d_name);
328
329 /* Does it look like a PMU? */
330 if (!file_available(path))
331 continue;
332
333 /* Process events. */
334 snprintf(path, sizeof(path), "%s/bus/event_source/devices/%s/events",
335 sysfs, pmu_dent->d_name);
336
337 event_dir = opendir(path);
338 if (!event_dir) {
339 pr_debug("Skipping as no event directory \"%s\"\n", path);
340 continue;
341 }
342 while ((event_dent = readdir(event_dir))) {
343 const char *event_name = event_dent->d_name;
344
345 if (!strcmp(event_name, ".") || !strcmp(event_name, ".."))
346 continue;
347
348 if (!permitted_event_name(event_name)) {
349 pr_err("Invalid sysfs event name: %s/%s\n",
350 pmu_dent->d_name, event_name);
351 ret = TEST_FAIL;
352 }
353 }
354 closedir(event_dir);
355 }
356 closedir(pmu_dir);
357 return ret;
358}
359
360static const char * const uncore_chas[] = {
361 "uncore_cha_0",
362 "uncore_cha_1",
363 "uncore_cha_2",
364 "uncore_cha_3",
365 "uncore_cha_4",
366 "uncore_cha_5",
367 "uncore_cha_6",
368 "uncore_cha_7",
369 "uncore_cha_8",
370 "uncore_cha_9",
371 "uncore_cha_10",
372 "uncore_cha_11",
373 "uncore_cha_12",
374 "uncore_cha_13",
375 "uncore_cha_14",
376 "uncore_cha_15",
377 "uncore_cha_16",
378 "uncore_cha_17",
379 "uncore_cha_18",
380 "uncore_cha_19",
381 "uncore_cha_20",
382 "uncore_cha_21",
383 "uncore_cha_22",
384 "uncore_cha_23",
385 "uncore_cha_24",
386 "uncore_cha_25",
387 "uncore_cha_26",
388 "uncore_cha_27",
389 "uncore_cha_28",
390 "uncore_cha_29",
391 "uncore_cha_30",
392 "uncore_cha_31",
393};
394
395static const char * const mrvl_ddrs[] = {
396 "mrvl_ddr_pmu_87e1b0000000",
397 "mrvl_ddr_pmu_87e1b1000000",
398 "mrvl_ddr_pmu_87e1b2000000",
399 "mrvl_ddr_pmu_87e1b3000000",
400 "mrvl_ddr_pmu_87e1b4000000",
401 "mrvl_ddr_pmu_87e1b5000000",
402 "mrvl_ddr_pmu_87e1b6000000",
403 "mrvl_ddr_pmu_87e1b7000000",
404 "mrvl_ddr_pmu_87e1b8000000",
405 "mrvl_ddr_pmu_87e1b9000000",
406 "mrvl_ddr_pmu_87e1ba000000",
407 "mrvl_ddr_pmu_87e1bb000000",
408 "mrvl_ddr_pmu_87e1bc000000",
409 "mrvl_ddr_pmu_87e1bd000000",
410 "mrvl_ddr_pmu_87e1be000000",
411 "mrvl_ddr_pmu_87e1bf000000",
412};
413
414static int test__name_len(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
415{
416 TEST_ASSERT_VAL("cpu", pmu_name_len_no_suffix("cpu") == strlen("cpu"));
417 TEST_ASSERT_VAL("i915", pmu_name_len_no_suffix("i915") == strlen("i915"));
418 TEST_ASSERT_VAL("cpum_cf", pmu_name_len_no_suffix("cpum_cf") == strlen("cpum_cf"));
419 for (size_t i = 0; i < ARRAY_SIZE(uncore_chas); i++) {
420 TEST_ASSERT_VAL("Strips uncore_cha suffix",
421 pmu_name_len_no_suffix(uncore_chas[i]) ==
422 strlen("uncore_cha"));
423 }
424 for (size_t i = 0; i < ARRAY_SIZE(mrvl_ddrs); i++) {
425 TEST_ASSERT_VAL("Strips mrvl_ddr_pmu suffix",
426 pmu_name_len_no_suffix(mrvl_ddrs[i]) ==
427 strlen("mrvl_ddr_pmu"));
428 }
429 return TEST_OK;
430}
431
432static int test__name_cmp(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
433{
434 TEST_ASSERT_EQUAL("cpu", pmu_name_cmp("cpu", "cpu"), 0);
435 TEST_ASSERT_EQUAL("i915", pmu_name_cmp("i915", "i915"), 0);
436 TEST_ASSERT_EQUAL("cpum_cf", pmu_name_cmp("cpum_cf", "cpum_cf"), 0);
437 TEST_ASSERT_VAL("i915", pmu_name_cmp("cpu", "i915") < 0);
438 TEST_ASSERT_VAL("i915", pmu_name_cmp("i915", "cpu") > 0);
439 TEST_ASSERT_VAL("cpum_cf", pmu_name_cmp("cpum_cf", "cpum_ce") > 0);
440 TEST_ASSERT_VAL("cpum_cf", pmu_name_cmp("cpum_cf", "cpum_d0") < 0);
441 for (size_t i = 1; i < ARRAY_SIZE(uncore_chas); i++) {
442 TEST_ASSERT_VAL("uncore_cha suffixes ordered lt",
443 pmu_name_cmp(uncore_chas[i-1], uncore_chas[i]) < 0);
444 TEST_ASSERT_VAL("uncore_cha suffixes ordered gt",
445 pmu_name_cmp(uncore_chas[i], uncore_chas[i-1]) > 0);
446 }
447 for (size_t i = 1; i < ARRAY_SIZE(mrvl_ddrs); i++) {
448 TEST_ASSERT_VAL("mrvl_ddr_pmu suffixes ordered lt",
449 pmu_name_cmp(mrvl_ddrs[i-1], mrvl_ddrs[i]) < 0);
450 TEST_ASSERT_VAL("mrvl_ddr_pmu suffixes ordered gt",
451 pmu_name_cmp(mrvl_ddrs[i], mrvl_ddrs[i-1]) > 0);
452 }
453 return TEST_OK;
454}
455
456/**
457 * Test perf_pmu__match() that's used to search for a PMU given a name passed
458 * on the command line. The name that's passed may also be a filename type glob
459 * match.
460 */
461static int test__pmu_match(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
462{
463 struct perf_pmu test_pmu;
464
465 test_pmu.name = "pmuname";
466 TEST_ASSERT_EQUAL("Exact match", perf_pmu__match(&test_pmu, "pmuname"), true);
467 TEST_ASSERT_EQUAL("Longer token", perf_pmu__match(&test_pmu, "longertoken"), false);
468 TEST_ASSERT_EQUAL("Shorter token", perf_pmu__match(&test_pmu, "pmu"), false);
469
470 test_pmu.name = "pmuname_10";
471 TEST_ASSERT_EQUAL("Diff suffix_", perf_pmu__match(&test_pmu, "pmuname_2"), false);
472 TEST_ASSERT_EQUAL("Sub suffix_", perf_pmu__match(&test_pmu, "pmuname_1"), true);
473 TEST_ASSERT_EQUAL("Same suffix_", perf_pmu__match(&test_pmu, "pmuname_10"), true);
474 TEST_ASSERT_EQUAL("No suffix_", perf_pmu__match(&test_pmu, "pmuname"), true);
475 TEST_ASSERT_EQUAL("Underscore_", perf_pmu__match(&test_pmu, "pmuname_"), true);
476 TEST_ASSERT_EQUAL("Substring_", perf_pmu__match(&test_pmu, "pmuna"), false);
477
478 test_pmu.name = "pmuname_ab23";
479 TEST_ASSERT_EQUAL("Diff suffix hex_", perf_pmu__match(&test_pmu, "pmuname_2"), false);
480 TEST_ASSERT_EQUAL("Sub suffix hex_", perf_pmu__match(&test_pmu, "pmuname_ab"), true);
481 TEST_ASSERT_EQUAL("Same suffix hex_", perf_pmu__match(&test_pmu, "pmuname_ab23"), true);
482 TEST_ASSERT_EQUAL("No suffix hex_", perf_pmu__match(&test_pmu, "pmuname"), true);
483 TEST_ASSERT_EQUAL("Underscore hex_", perf_pmu__match(&test_pmu, "pmuname_"), true);
484 TEST_ASSERT_EQUAL("Substring hex_", perf_pmu__match(&test_pmu, "pmuna"), false);
485
486 test_pmu.name = "pmuname10";
487 TEST_ASSERT_EQUAL("Diff suffix", perf_pmu__match(&test_pmu, "pmuname2"), false);
488 TEST_ASSERT_EQUAL("Sub suffix", perf_pmu__match(&test_pmu, "pmuname1"), true);
489 TEST_ASSERT_EQUAL("Same suffix", perf_pmu__match(&test_pmu, "pmuname10"), true);
490 TEST_ASSERT_EQUAL("No suffix", perf_pmu__match(&test_pmu, "pmuname"), true);
491 TEST_ASSERT_EQUAL("Underscore", perf_pmu__match(&test_pmu, "pmuname_"), false);
492 TEST_ASSERT_EQUAL("Substring", perf_pmu__match(&test_pmu, "pmuna"), false);
493
494 test_pmu.name = "pmunameab23";
495 TEST_ASSERT_EQUAL("Diff suffix hex", perf_pmu__match(&test_pmu, "pmuname2"), false);
496 TEST_ASSERT_EQUAL("Sub suffix hex", perf_pmu__match(&test_pmu, "pmunameab"), true);
497 TEST_ASSERT_EQUAL("Same suffix hex", perf_pmu__match(&test_pmu, "pmunameab23"), true);
498 TEST_ASSERT_EQUAL("No suffix hex", perf_pmu__match(&test_pmu, "pmuname"), true);
499 TEST_ASSERT_EQUAL("Underscore hex", perf_pmu__match(&test_pmu, "pmuname_"), false);
500 TEST_ASSERT_EQUAL("Substring hex", perf_pmu__match(&test_pmu, "pmuna"), false);
501
502 /*
503 * 2 hex chars or less are not considered suffixes so it shouldn't be
504 * possible to wildcard by skipping the suffix. Therefore there are more
505 * false results here than above.
506 */
507 test_pmu.name = "pmuname_a3";
508 TEST_ASSERT_EQUAL("Diff suffix 2 hex_", perf_pmu__match(&test_pmu, "pmuname_2"), false);
509 /*
510 * This one should be false, but because pmuname_a3 ends in 3 which is
511 * decimal, it's not possible to determine if it's a short hex suffix or
512 * a normal decimal suffix following text. And we want to match on any
513 * length of decimal suffix. Run the test anyway and expect the wrong
514 * result. And slightly fuzzy matching shouldn't do too much harm.
515 */
516 TEST_ASSERT_EQUAL("Sub suffix 2 hex_", perf_pmu__match(&test_pmu, "pmuname_a"), true);
517 TEST_ASSERT_EQUAL("Same suffix 2 hex_", perf_pmu__match(&test_pmu, "pmuname_a3"), true);
518 TEST_ASSERT_EQUAL("No suffix 2 hex_", perf_pmu__match(&test_pmu, "pmuname"), false);
519 TEST_ASSERT_EQUAL("Underscore 2 hex_", perf_pmu__match(&test_pmu, "pmuname_"), false);
520 TEST_ASSERT_EQUAL("Substring 2 hex_", perf_pmu__match(&test_pmu, "pmuna"), false);
521
522 test_pmu.name = "pmuname_5";
523 TEST_ASSERT_EQUAL("Glob 1", perf_pmu__match(&test_pmu, "pmu*"), true);
524 TEST_ASSERT_EQUAL("Glob 2", perf_pmu__match(&test_pmu, "nomatch*"), false);
525 TEST_ASSERT_EQUAL("Seq 1", perf_pmu__match(&test_pmu, "pmuname_[12345]"), true);
526 TEST_ASSERT_EQUAL("Seq 2", perf_pmu__match(&test_pmu, "pmuname_[67890]"), false);
527 TEST_ASSERT_EQUAL("? 1", perf_pmu__match(&test_pmu, "pmuname_?"), true);
528 TEST_ASSERT_EQUAL("? 2", perf_pmu__match(&test_pmu, "pmuname_1?"), false);
529
530 return TEST_OK;
531}
532
533static struct test_case tests__pmu[] = {
534 TEST_CASE("Parsing with PMU format directory", pmu_format),
535 TEST_CASE("Parsing with PMU event", pmu_events),
536 TEST_CASE("PMU event names", pmu_event_names),
537 TEST_CASE("PMU name combining", name_len),
538 TEST_CASE("PMU name comparison", name_cmp),
539 TEST_CASE("PMU cmdline match", pmu_match),
540 { .name = NULL, }
541};
542
543struct test_suite suite__pmu = {
544 .desc = "Sysfs PMU tests",
545 .test_cases = tests__pmu,
546};