546 lines · c
1// SPDX-License-Identifier: GPL-2.02#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/* Cleanup test PMU directory. */22static int test_pmu_put(const char *dir, struct perf_pmu *pmu)23{24 char buf[PATH_MAX + 20];25 int ret;26 27 if (scnprintf(buf, sizeof(buf), "rm -fr %s", dir) < 0) {28 pr_err("Failure to set up buffer for \"%s\"\n", dir);29 return -EINVAL;30 }31 ret = system(buf);32 if (ret)33 pr_err("Failure to \"%s\"\n", buf);34 35 list_del(&pmu->list);36 perf_pmu__delete(pmu);37 return ret;38}39 40/*41 * Prepare test PMU directory data, normally exported by kernel at42 * /sys/bus/event_source/devices/<pmu>/. Give as input a buffer to hold the file43 * path, the result is PMU loaded using that directory.44 */45static struct perf_pmu *test_pmu_get(char *dir, size_t sz)46{47 /* Simulated format definitions. */48 const struct test_format {49 const char *name;50 const char *value;51 } test_formats[] = {52 { "krava01", "config:0-1,62-63\n", },53 { "krava02", "config:10-17\n", },54 { "krava03", "config:5\n", },55 { "krava11", "config1:0,2,4,6,8,20-28\n", },56 { "krava12", "config1:63\n", },57 { "krava13", "config1:45-47\n", },58 { "krava21", "config2:0-3,10-13,20-23,30-33,40-43,50-53,60-63\n", },59 { "krava22", "config2:8,18,48,58\n", },60 { "krava23", "config2:28-29,38\n", },61 };62 const char *test_event = "krava01=15,krava02=170,krava03=1,krava11=27,krava12=1,"63 "krava13=2,krava21=119,krava22=11,krava23=2\n";64 65 char name[PATH_MAX];66 int dirfd, file;67 struct perf_pmu *pmu = NULL;68 ssize_t len;69 70 /* Create equivalent of sysfs mount point. */71 scnprintf(dir, sz, "/tmp/perf-pmu-test-XXXXXX");72 if (!mkdtemp(dir)) {73 pr_err("mkdtemp failed\n");74 dir[0] = '\0';75 return NULL;76 }77 dirfd = open(dir, O_DIRECTORY);78 if (dirfd < 0) {79 pr_err("Failed to open test directory \"%s\"\n", dir);80 goto err_out;81 }82 83 /* Create the test PMU directory and give it a perf_event_attr type number. */84 if (mkdirat(dirfd, "perf-pmu-test", 0755) < 0) {85 pr_err("Failed to mkdir PMU directory\n");86 goto err_out;87 }88 file = openat(dirfd, "perf-pmu-test/type", O_WRONLY | O_CREAT, 0600);89 if (!file) {90 pr_err("Failed to open for writing file \"type\"\n");91 goto err_out;92 }93 len = strlen("9999");94 if (write(file, "9999\n", len) < len) {95 close(file);96 pr_err("Failed to write to 'type' file\n");97 goto err_out;98 }99 close(file);100 101 /* Create format directory and files. */102 if (mkdirat(dirfd, "perf-pmu-test/format", 0755) < 0) {103 pr_err("Failed to mkdir PMU format directory\n)");104 goto err_out;105 }106 for (size_t i = 0; i < ARRAY_SIZE(test_formats); i++) {107 const struct test_format *format = &test_formats[i];108 109 if (scnprintf(name, PATH_MAX, "perf-pmu-test/format/%s", format->name) < 0) {110 pr_err("Failure to set up path for \"%s\"\n", format->name);111 goto err_out;112 }113 file = openat(dirfd, name, O_WRONLY | O_CREAT, 0600);114 if (!file) {115 pr_err("Failed to open for writing file \"%s\"\n", name);116 goto err_out;117 }118 119 if (write(file, format->value, strlen(format->value)) < 0) {120 pr_err("Failed to write to file \"%s\"\n", name);121 close(file);122 goto err_out;123 }124 close(file);125 }126 127 /* Create test event. */128 if (mkdirat(dirfd, "perf-pmu-test/events", 0755) < 0) {129 pr_err("Failed to mkdir PMU events directory\n");130 goto err_out;131 }132 file = openat(dirfd, "perf-pmu-test/events/test-event", O_WRONLY | O_CREAT, 0600);133 if (!file) {134 pr_err("Failed to open for writing file \"type\"\n");135 goto err_out;136 }137 len = strlen(test_event);138 if (write(file, test_event, len) < len) {139 close(file);140 pr_err("Failed to write to 'test-event' file\n");141 goto err_out;142 }143 close(file);144 145 /* Make the PMU reading the files created above. */146 pmu = perf_pmus__add_test_pmu(dirfd, "perf-pmu-test");147 if (!pmu)148 pr_err("Test PMU creation failed\n");149 150err_out:151 if (!pmu)152 test_pmu_put(dir, pmu);153 if (dirfd >= 0)154 close(dirfd);155 return pmu;156}157 158static int test__pmu_format(struct test_suite *test __maybe_unused, int subtest __maybe_unused)159{160 char dir[PATH_MAX];161 struct perf_event_attr attr;162 struct parse_events_terms terms;163 int ret = TEST_FAIL;164 struct perf_pmu *pmu = test_pmu_get(dir, sizeof(dir));165 166 if (!pmu)167 return TEST_FAIL;168 169 parse_events_terms__init(&terms);170 if (parse_events_terms(&terms,171 "krava01=15,krava02=170,krava03=1,krava11=27,krava12=1,"172 "krava13=2,krava21=119,krava22=11,krava23=2",173 NULL)) {174 pr_err("Term parsing failed\n");175 goto err_out;176 }177 178 memset(&attr, 0, sizeof(attr));179 ret = perf_pmu__config_terms(pmu, &attr, &terms, /*zero=*/false, /*err=*/NULL);180 if (ret) {181 pr_err("perf_pmu__config_terms failed");182 goto err_out;183 }184 185 if (attr.config != 0xc00000000002a823) {186 pr_err("Unexpected config value %llx\n", attr.config);187 goto err_out;188 }189 if (attr.config1 != 0x8000400000000145) {190 pr_err("Unexpected config1 value %llx\n", attr.config1);191 goto err_out;192 }193 if (attr.config2 != 0x0400000020041d07) {194 pr_err("Unexpected config2 value %llx\n", attr.config2);195 goto err_out;196 }197 198 ret = TEST_OK;199err_out:200 parse_events_terms__exit(&terms);201 test_pmu_put(dir, pmu);202 return ret;203}204 205static int test__pmu_events(struct test_suite *test __maybe_unused, int subtest __maybe_unused)206{207 char dir[PATH_MAX];208 struct parse_events_error err;209 struct evlist *evlist;210 struct evsel *evsel;211 struct perf_event_attr *attr;212 int ret = TEST_FAIL;213 struct perf_pmu *pmu = test_pmu_get(dir, sizeof(dir));214 const char *event = "perf-pmu-test/test-event/";215 216 217 if (!pmu)218 return TEST_FAIL;219 220 evlist = evlist__new();221 if (evlist == NULL) {222 pr_err("Failed allocation");223 goto err_out;224 }225 parse_events_error__init(&err);226 ret = parse_events(evlist, event, &err);227 if (ret) {228 pr_debug("failed to parse event '%s', err %d\n", event, ret);229 parse_events_error__print(&err, event);230 if (parse_events_error__contains(&err, "can't access trace events"))231 ret = TEST_SKIP;232 goto err_out;233 }234 evsel = evlist__first(evlist);235 attr = &evsel->core.attr;236 if (attr->config != 0xc00000000002a823) {237 pr_err("Unexpected config value %llx\n", attr->config);238 goto err_out;239 }240 if (attr->config1 != 0x8000400000000145) {241 pr_err("Unexpected config1 value %llx\n", attr->config1);242 goto err_out;243 }244 if (attr->config2 != 0x0400000020041d07) {245 pr_err("Unexpected config2 value %llx\n", attr->config2);246 goto err_out;247 }248 249 ret = TEST_OK;250err_out:251 parse_events_error__exit(&err);252 evlist__delete(evlist);253 test_pmu_put(dir, pmu);254 return ret;255}256 257static bool permitted_event_name(const char *name)258{259 bool has_lower = false, has_upper = false;260 __u64 config;261 262 for (size_t i = 0; i < strlen(name); i++) {263 char c = name[i];264 265 if (islower(c)) {266 if (has_upper)267 goto check_legacy;268 has_lower = true;269 continue;270 }271 if (isupper(c)) {272 if (has_lower)273 goto check_legacy;274 has_upper = true;275 continue;276 }277 if (!isdigit(c) && c != '.' && c != '_' && c != '-')278 goto check_legacy;279 }280 return true;281check_legacy:282 /*283 * If the event name matches a legacy cache name the legacy encoding284 * will still be used. This isn't quite WAI as sysfs events should take285 * priority, but this case happens on PowerPC and matches the behavior286 * in older perf tools where legacy events were the priority. Be287 * permissive and assume later PMU drivers will use all lower or upper288 * case names.289 */290 if (parse_events__decode_legacy_cache(name, /*extended_pmu_type=*/0, &config) == 0) {291 pr_warning("sysfs event '%s' should be all lower/upper case, it will be matched using legacy encoding.",292 name);293 return true;294 }295 return false;296}297 298static int test__pmu_event_names(struct test_suite *test __maybe_unused,299 int subtest __maybe_unused)300{301 char path[PATH_MAX];302 DIR *pmu_dir, *event_dir;303 struct dirent *pmu_dent, *event_dent;304 const char *sysfs = sysfs__mountpoint();305 int ret = TEST_OK;306 307 if (!sysfs) {308 pr_err("Sysfs not mounted\n");309 return TEST_FAIL;310 }311 312 snprintf(path, sizeof(path), "%s/bus/event_source/devices/", sysfs);313 pmu_dir = opendir(path);314 if (!pmu_dir) {315 pr_err("Error opening \"%s\"\n", path);316 return TEST_FAIL;317 }318 while ((pmu_dent = readdir(pmu_dir))) {319 if (!strcmp(pmu_dent->d_name, ".") ||320 !strcmp(pmu_dent->d_name, ".."))321 continue;322 323 snprintf(path, sizeof(path), "%s/bus/event_source/devices/%s/type",324 sysfs, pmu_dent->d_name);325 326 /* Does it look like a PMU? */327 if (!file_available(path))328 continue;329 330 /* Process events. */331 snprintf(path, sizeof(path), "%s/bus/event_source/devices/%s/events",332 sysfs, pmu_dent->d_name);333 334 event_dir = opendir(path);335 if (!event_dir) {336 pr_debug("Skipping as no event directory \"%s\"\n", path);337 continue;338 }339 while ((event_dent = readdir(event_dir))) {340 const char *event_name = event_dent->d_name;341 342 if (!strcmp(event_name, ".") || !strcmp(event_name, ".."))343 continue;344 345 if (!permitted_event_name(event_name)) {346 pr_err("Invalid sysfs event name: %s/%s\n",347 pmu_dent->d_name, event_name);348 ret = TEST_FAIL;349 }350 }351 closedir(event_dir);352 }353 closedir(pmu_dir);354 return ret;355}356 357static const char * const uncore_chas[] = {358 "uncore_cha_0",359 "uncore_cha_1",360 "uncore_cha_2",361 "uncore_cha_3",362 "uncore_cha_4",363 "uncore_cha_5",364 "uncore_cha_6",365 "uncore_cha_7",366 "uncore_cha_8",367 "uncore_cha_9",368 "uncore_cha_10",369 "uncore_cha_11",370 "uncore_cha_12",371 "uncore_cha_13",372 "uncore_cha_14",373 "uncore_cha_15",374 "uncore_cha_16",375 "uncore_cha_17",376 "uncore_cha_18",377 "uncore_cha_19",378 "uncore_cha_20",379 "uncore_cha_21",380 "uncore_cha_22",381 "uncore_cha_23",382 "uncore_cha_24",383 "uncore_cha_25",384 "uncore_cha_26",385 "uncore_cha_27",386 "uncore_cha_28",387 "uncore_cha_29",388 "uncore_cha_30",389 "uncore_cha_31",390};391 392static const char * const mrvl_ddrs[] = {393 "mrvl_ddr_pmu_87e1b0000000",394 "mrvl_ddr_pmu_87e1b1000000",395 "mrvl_ddr_pmu_87e1b2000000",396 "mrvl_ddr_pmu_87e1b3000000",397 "mrvl_ddr_pmu_87e1b4000000",398 "mrvl_ddr_pmu_87e1b5000000",399 "mrvl_ddr_pmu_87e1b6000000",400 "mrvl_ddr_pmu_87e1b7000000",401 "mrvl_ddr_pmu_87e1b8000000",402 "mrvl_ddr_pmu_87e1b9000000",403 "mrvl_ddr_pmu_87e1ba000000",404 "mrvl_ddr_pmu_87e1bb000000",405 "mrvl_ddr_pmu_87e1bc000000",406 "mrvl_ddr_pmu_87e1bd000000",407 "mrvl_ddr_pmu_87e1be000000",408 "mrvl_ddr_pmu_87e1bf000000",409};410 411static int test__name_len(struct test_suite *test __maybe_unused, int subtest __maybe_unused)412{413 TEST_ASSERT_VAL("cpu", pmu_name_len_no_suffix("cpu") == strlen("cpu"));414 TEST_ASSERT_VAL("i915", pmu_name_len_no_suffix("i915") == strlen("i915"));415 TEST_ASSERT_VAL("cpum_cf", pmu_name_len_no_suffix("cpum_cf") == strlen("cpum_cf"));416 for (size_t i = 0; i < ARRAY_SIZE(uncore_chas); i++) {417 TEST_ASSERT_VAL("Strips uncore_cha suffix",418 pmu_name_len_no_suffix(uncore_chas[i]) ==419 strlen("uncore_cha"));420 }421 for (size_t i = 0; i < ARRAY_SIZE(mrvl_ddrs); i++) {422 TEST_ASSERT_VAL("Strips mrvl_ddr_pmu suffix",423 pmu_name_len_no_suffix(mrvl_ddrs[i]) ==424 strlen("mrvl_ddr_pmu"));425 }426 return TEST_OK;427}428 429static int test__name_cmp(struct test_suite *test __maybe_unused, int subtest __maybe_unused)430{431 TEST_ASSERT_EQUAL("cpu", pmu_name_cmp("cpu", "cpu"), 0);432 TEST_ASSERT_EQUAL("i915", pmu_name_cmp("i915", "i915"), 0);433 TEST_ASSERT_EQUAL("cpum_cf", pmu_name_cmp("cpum_cf", "cpum_cf"), 0);434 TEST_ASSERT_VAL("i915", pmu_name_cmp("cpu", "i915") < 0);435 TEST_ASSERT_VAL("i915", pmu_name_cmp("i915", "cpu") > 0);436 TEST_ASSERT_VAL("cpum_cf", pmu_name_cmp("cpum_cf", "cpum_ce") > 0);437 TEST_ASSERT_VAL("cpum_cf", pmu_name_cmp("cpum_cf", "cpum_d0") < 0);438 for (size_t i = 1; i < ARRAY_SIZE(uncore_chas); i++) {439 TEST_ASSERT_VAL("uncore_cha suffixes ordered lt",440 pmu_name_cmp(uncore_chas[i-1], uncore_chas[i]) < 0);441 TEST_ASSERT_VAL("uncore_cha suffixes ordered gt",442 pmu_name_cmp(uncore_chas[i], uncore_chas[i-1]) > 0);443 }444 for (size_t i = 1; i < ARRAY_SIZE(mrvl_ddrs); i++) {445 TEST_ASSERT_VAL("mrvl_ddr_pmu suffixes ordered lt",446 pmu_name_cmp(mrvl_ddrs[i-1], mrvl_ddrs[i]) < 0);447 TEST_ASSERT_VAL("mrvl_ddr_pmu suffixes ordered gt",448 pmu_name_cmp(mrvl_ddrs[i], mrvl_ddrs[i-1]) > 0);449 }450 return TEST_OK;451}452 453/**454 * Test perf_pmu__match() that's used to search for a PMU given a name passed455 * on the command line. The name that's passed may also be a filename type glob456 * match. If the name does not match, perf_pmu__match() attempts to match the457 * alias of the PMU, if provided.458 */459static int test__pmu_match(struct test_suite *test __maybe_unused, int subtest __maybe_unused)460{461 struct perf_pmu test_pmu = {462 .name = "pmuname",463 };464 465 TEST_ASSERT_EQUAL("Exact match", perf_pmu__match(&test_pmu, "pmuname"), true);466 TEST_ASSERT_EQUAL("Longer token", perf_pmu__match(&test_pmu, "longertoken"), false);467 TEST_ASSERT_EQUAL("Shorter token", perf_pmu__match(&test_pmu, "pmu"), false);468 469 test_pmu.name = "pmuname_10";470 TEST_ASSERT_EQUAL("Diff suffix_", perf_pmu__match(&test_pmu, "pmuname_2"), false);471 TEST_ASSERT_EQUAL("Sub suffix_", perf_pmu__match(&test_pmu, "pmuname_1"), true);472 TEST_ASSERT_EQUAL("Same suffix_", perf_pmu__match(&test_pmu, "pmuname_10"), true);473 TEST_ASSERT_EQUAL("No suffix_", perf_pmu__match(&test_pmu, "pmuname"), true);474 TEST_ASSERT_EQUAL("Underscore_", perf_pmu__match(&test_pmu, "pmuname_"), true);475 TEST_ASSERT_EQUAL("Substring_", perf_pmu__match(&test_pmu, "pmuna"), false);476 477 test_pmu.name = "pmuname_ab23";478 TEST_ASSERT_EQUAL("Diff suffix hex_", perf_pmu__match(&test_pmu, "pmuname_2"), false);479 TEST_ASSERT_EQUAL("Sub suffix hex_", perf_pmu__match(&test_pmu, "pmuname_ab"), true);480 TEST_ASSERT_EQUAL("Same suffix hex_", perf_pmu__match(&test_pmu, "pmuname_ab23"), true);481 TEST_ASSERT_EQUAL("No suffix hex_", perf_pmu__match(&test_pmu, "pmuname"), true);482 TEST_ASSERT_EQUAL("Underscore hex_", perf_pmu__match(&test_pmu, "pmuname_"), true);483 TEST_ASSERT_EQUAL("Substring hex_", perf_pmu__match(&test_pmu, "pmuna"), false);484 485 test_pmu.name = "pmuname10";486 TEST_ASSERT_EQUAL("Diff suffix", perf_pmu__match(&test_pmu, "pmuname2"), false);487 TEST_ASSERT_EQUAL("Sub suffix", perf_pmu__match(&test_pmu, "pmuname1"), true);488 TEST_ASSERT_EQUAL("Same suffix", perf_pmu__match(&test_pmu, "pmuname10"), true);489 TEST_ASSERT_EQUAL("No suffix", perf_pmu__match(&test_pmu, "pmuname"), true);490 TEST_ASSERT_EQUAL("Underscore", perf_pmu__match(&test_pmu, "pmuname_"), false);491 TEST_ASSERT_EQUAL("Substring", perf_pmu__match(&test_pmu, "pmuna"), false);492 493 test_pmu.name = "pmunameab23";494 TEST_ASSERT_EQUAL("Diff suffix hex", perf_pmu__match(&test_pmu, "pmuname2"), false);495 TEST_ASSERT_EQUAL("Sub suffix hex", perf_pmu__match(&test_pmu, "pmunameab"), true);496 TEST_ASSERT_EQUAL("Same suffix hex", perf_pmu__match(&test_pmu, "pmunameab23"), true);497 TEST_ASSERT_EQUAL("No suffix hex", perf_pmu__match(&test_pmu, "pmuname"), true);498 TEST_ASSERT_EQUAL("Underscore hex", perf_pmu__match(&test_pmu, "pmuname_"), false);499 TEST_ASSERT_EQUAL("Substring hex", perf_pmu__match(&test_pmu, "pmuna"), false);500 501 /*502 * 2 hex chars or less are not considered suffixes so it shouldn't be503 * possible to wildcard by skipping the suffix. Therefore there are more504 * false results here than above.505 */506 test_pmu.name = "pmuname_a3";507 TEST_ASSERT_EQUAL("Diff suffix 2 hex_", perf_pmu__match(&test_pmu, "pmuname_2"), false);508 /*509 * This one should be false, but because pmuname_a3 ends in 3 which is510 * decimal, it's not possible to determine if it's a short hex suffix or511 * a normal decimal suffix following text. And we want to match on any512 * length of decimal suffix. Run the test anyway and expect the wrong513 * result. And slightly fuzzy matching shouldn't do too much harm.514 */515 TEST_ASSERT_EQUAL("Sub suffix 2 hex_", perf_pmu__match(&test_pmu, "pmuname_a"), true);516 TEST_ASSERT_EQUAL("Same suffix 2 hex_", perf_pmu__match(&test_pmu, "pmuname_a3"), true);517 TEST_ASSERT_EQUAL("No suffix 2 hex_", perf_pmu__match(&test_pmu, "pmuname"), false);518 TEST_ASSERT_EQUAL("Underscore 2 hex_", perf_pmu__match(&test_pmu, "pmuname_"), false);519 TEST_ASSERT_EQUAL("Substring 2 hex_", perf_pmu__match(&test_pmu, "pmuna"), false);520 521 test_pmu.name = "pmuname_5";522 TEST_ASSERT_EQUAL("Glob 1", perf_pmu__match(&test_pmu, "pmu*"), true);523 TEST_ASSERT_EQUAL("Glob 2", perf_pmu__match(&test_pmu, "nomatch*"), false);524 TEST_ASSERT_EQUAL("Seq 1", perf_pmu__match(&test_pmu, "pmuname_[12345]"), true);525 TEST_ASSERT_EQUAL("Seq 2", perf_pmu__match(&test_pmu, "pmuname_[67890]"), false);526 TEST_ASSERT_EQUAL("? 1", perf_pmu__match(&test_pmu, "pmuname_?"), true);527 TEST_ASSERT_EQUAL("? 2", perf_pmu__match(&test_pmu, "pmuname_1?"), false);528 529 return TEST_OK;530}531 532static struct test_case tests__pmu[] = {533 TEST_CASE("Parsing with PMU format directory", pmu_format),534 TEST_CASE("Parsing with PMU event", pmu_events),535 TEST_CASE("PMU event names", pmu_event_names),536 TEST_CASE("PMU name combining", name_len),537 TEST_CASE("PMU name comparison", name_cmp),538 TEST_CASE("PMU cmdline match", pmu_match),539 { .name = NULL, }540};541 542struct test_suite suite__pmu = {543 .desc = "Sysfs PMU tests",544 .test_cases = tests__pmu,545};546