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