brintos

brintos / linux-shallow public Read only

0
0
Text · 16.8 KiB · be18506 Raw
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