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