xref: /linux/tools/perf/tests/hwmon_pmu.c (revision 531ee0fd4836994fc99f22cc79ef94554f2ae54e)
18c329057SIan Rogers // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
28c329057SIan Rogers #include "debug.h"
3*531ee0fdSIan Rogers #include "evlist.h"
48c329057SIan Rogers #include "hwmon_pmu.h"
5*531ee0fdSIan Rogers #include "parse-events.h"
68c329057SIan Rogers #include "tests.h"
7*531ee0fdSIan Rogers #include <fcntl.h>
8*531ee0fdSIan Rogers #include <sys/stat.h>
98c329057SIan Rogers #include <linux/compiler.h>
108c329057SIan Rogers #include <linux/kernel.h>
118c329057SIan Rogers #include <linux/string.h>
128c329057SIan Rogers 
13*531ee0fdSIan Rogers static const struct test_event {
14*531ee0fdSIan Rogers 	const char *name;
15*531ee0fdSIan Rogers 	const char *alias;
16*531ee0fdSIan Rogers 	long config;
17*531ee0fdSIan Rogers } test_events[] = {
18*531ee0fdSIan Rogers 	{
19*531ee0fdSIan Rogers 		"temp_test_hwmon_event1",
20*531ee0fdSIan Rogers 		"temp1",
21*531ee0fdSIan Rogers 		0xA0001,
22*531ee0fdSIan Rogers 	},
23*531ee0fdSIan Rogers 	{
24*531ee0fdSIan Rogers 		"temp_test_hwmon_event2",
25*531ee0fdSIan Rogers 		"temp2",
26*531ee0fdSIan Rogers 		0xA0002,
27*531ee0fdSIan Rogers 	},
28*531ee0fdSIan Rogers };
29*531ee0fdSIan Rogers 
30*531ee0fdSIan Rogers /* Cleanup test PMU directory. */
31*531ee0fdSIan Rogers static int test_pmu_put(const char *dir, struct perf_pmu *hwm)
32*531ee0fdSIan Rogers {
33*531ee0fdSIan Rogers 	char buf[PATH_MAX + 20];
34*531ee0fdSIan Rogers 	int ret;
35*531ee0fdSIan Rogers 
36*531ee0fdSIan Rogers 	if (scnprintf(buf, sizeof(buf), "rm -fr %s", dir) < 0) {
37*531ee0fdSIan Rogers 		pr_err("Failure to set up buffer for \"%s\"\n", dir);
38*531ee0fdSIan Rogers 		return -EINVAL;
39*531ee0fdSIan Rogers 	}
40*531ee0fdSIan Rogers 	ret = system(buf);
41*531ee0fdSIan Rogers 	if (ret)
42*531ee0fdSIan Rogers 		pr_err("Failure to \"%s\"\n", buf);
43*531ee0fdSIan Rogers 
44*531ee0fdSIan Rogers 	perf_pmu__delete(hwm);
45*531ee0fdSIan Rogers 	return ret;
46*531ee0fdSIan Rogers }
47*531ee0fdSIan Rogers 
48*531ee0fdSIan Rogers /*
49*531ee0fdSIan Rogers  * Prepare test PMU directory data, normally exported by kernel at
50*531ee0fdSIan Rogers  * /sys/class/hwmon/hwmon<number>/. Give as input a buffer to hold the file
51*531ee0fdSIan Rogers  * path, the result is PMU loaded using that directory.
52*531ee0fdSIan Rogers  */
53*531ee0fdSIan Rogers static struct perf_pmu *test_pmu_get(char *dir, size_t sz)
54*531ee0fdSIan Rogers {
55*531ee0fdSIan Rogers 	const char *test_hwmon_name_nl = "A test hwmon PMU\n";
56*531ee0fdSIan Rogers 	const char *test_hwmon_name = "A test hwmon PMU";
57*531ee0fdSIan Rogers 	/* Simulated hwmon items. */
58*531ee0fdSIan Rogers 	const struct test_item {
59*531ee0fdSIan Rogers 		const char *name;
60*531ee0fdSIan Rogers 		const char *value;
61*531ee0fdSIan Rogers 	} test_items[] = {
62*531ee0fdSIan Rogers 		{ "temp1_label", "test hwmon event1\n", },
63*531ee0fdSIan Rogers 		{ "temp1_input", "40000\n", },
64*531ee0fdSIan Rogers 		{ "temp2_label", "test hwmon event2\n", },
65*531ee0fdSIan Rogers 		{ "temp2_input", "50000\n", },
66*531ee0fdSIan Rogers 	};
67*531ee0fdSIan Rogers 	int dirfd, file;
68*531ee0fdSIan Rogers 	struct perf_pmu *hwm = NULL;
69*531ee0fdSIan Rogers 	ssize_t len;
70*531ee0fdSIan Rogers 
71*531ee0fdSIan Rogers 	/* Create equivalent of sysfs mount point. */
72*531ee0fdSIan Rogers 	scnprintf(dir, sz, "/tmp/perf-hwmon-pmu-test-XXXXXX");
73*531ee0fdSIan Rogers 	if (!mkdtemp(dir)) {
74*531ee0fdSIan Rogers 		pr_err("mkdtemp failed\n");
75*531ee0fdSIan Rogers 		dir[0] = '\0';
76*531ee0fdSIan Rogers 		return NULL;
77*531ee0fdSIan Rogers 	}
78*531ee0fdSIan Rogers 	dirfd = open(dir, O_DIRECTORY);
79*531ee0fdSIan Rogers 	if (dirfd < 0) {
80*531ee0fdSIan Rogers 		pr_err("Failed to open test directory \"%s\"\n", dir);
81*531ee0fdSIan Rogers 		goto err_out;
82*531ee0fdSIan Rogers 	}
83*531ee0fdSIan Rogers 
84*531ee0fdSIan Rogers 	/* Create the test hwmon directory and give it a name. */
85*531ee0fdSIan Rogers 	if (mkdirat(dirfd, "hwmon1234", 0755) < 0) {
86*531ee0fdSIan Rogers 		pr_err("Failed to mkdir hwmon directory\n");
87*531ee0fdSIan Rogers 		goto err_out;
88*531ee0fdSIan Rogers 	}
89*531ee0fdSIan Rogers 	file = openat(dirfd, "hwmon1234/name", O_WRONLY | O_CREAT, 0600);
90*531ee0fdSIan Rogers 	if (!file) {
91*531ee0fdSIan Rogers 		pr_err("Failed to open for writing file \"name\"\n");
92*531ee0fdSIan Rogers 		goto err_out;
93*531ee0fdSIan Rogers 	}
94*531ee0fdSIan Rogers 	len = strlen(test_hwmon_name_nl);
95*531ee0fdSIan Rogers 	if (write(file, test_hwmon_name_nl, len) < len) {
96*531ee0fdSIan Rogers 		close(file);
97*531ee0fdSIan Rogers 		pr_err("Failed to write to 'name' file\n");
98*531ee0fdSIan Rogers 		goto err_out;
99*531ee0fdSIan Rogers 	}
100*531ee0fdSIan Rogers 	close(file);
101*531ee0fdSIan Rogers 
102*531ee0fdSIan Rogers 	/* Create test hwmon files. */
103*531ee0fdSIan Rogers 	for (size_t i = 0; i < ARRAY_SIZE(test_items); i++) {
104*531ee0fdSIan Rogers 		const struct test_item *item = &test_items[i];
105*531ee0fdSIan Rogers 
106*531ee0fdSIan Rogers 		file = openat(dirfd, item->name, O_WRONLY | O_CREAT, 0600);
107*531ee0fdSIan Rogers 		if (!file) {
108*531ee0fdSIan Rogers 			pr_err("Failed to open for writing file \"%s\"\n", item->name);
109*531ee0fdSIan Rogers 			goto err_out;
110*531ee0fdSIan Rogers 		}
111*531ee0fdSIan Rogers 
112*531ee0fdSIan Rogers 		if (write(file, item->value, strlen(item->value)) < 0) {
113*531ee0fdSIan Rogers 			pr_err("Failed to write to file \"%s\"\n", item->name);
114*531ee0fdSIan Rogers 			close(file);
115*531ee0fdSIan Rogers 			goto err_out;
116*531ee0fdSIan Rogers 		}
117*531ee0fdSIan Rogers 		close(file);
118*531ee0fdSIan Rogers 	}
119*531ee0fdSIan Rogers 
120*531ee0fdSIan Rogers 	/* Make the PMU reading the files created above. */
121*531ee0fdSIan Rogers 	hwm = perf_pmus__add_test_hwmon_pmu(dirfd, "hwmon1234", test_hwmon_name);
122*531ee0fdSIan Rogers 	if (!hwm)
123*531ee0fdSIan Rogers 		pr_err("Test hwmon creation failed\n");
124*531ee0fdSIan Rogers 
125*531ee0fdSIan Rogers err_out:
126*531ee0fdSIan Rogers 	if (!hwm) {
127*531ee0fdSIan Rogers 		test_pmu_put(dir, hwm);
128*531ee0fdSIan Rogers 		if (dirfd >= 0)
129*531ee0fdSIan Rogers 			close(dirfd);
130*531ee0fdSIan Rogers 	}
131*531ee0fdSIan Rogers 	return hwm;
132*531ee0fdSIan Rogers }
133*531ee0fdSIan Rogers 
134*531ee0fdSIan Rogers static int do_test(size_t i, bool with_pmu, bool with_alias)
135*531ee0fdSIan Rogers {
136*531ee0fdSIan Rogers 	const char *test_event = with_alias ? test_events[i].alias : test_events[i].name;
137*531ee0fdSIan Rogers 	struct evlist *evlist = evlist__new();
138*531ee0fdSIan Rogers 	struct evsel *evsel;
139*531ee0fdSIan Rogers 	struct parse_events_error err;
140*531ee0fdSIan Rogers 	int ret;
141*531ee0fdSIan Rogers 	char str[128];
142*531ee0fdSIan Rogers 	bool found = false;
143*531ee0fdSIan Rogers 
144*531ee0fdSIan Rogers 	if (!evlist) {
145*531ee0fdSIan Rogers 		pr_err("evlist allocation failed\n");
146*531ee0fdSIan Rogers 		return TEST_FAIL;
147*531ee0fdSIan Rogers 	}
148*531ee0fdSIan Rogers 
149*531ee0fdSIan Rogers 	if (with_pmu)
150*531ee0fdSIan Rogers 		snprintf(str, sizeof(str), "/%s/", test_event);
151*531ee0fdSIan Rogers 	else
152*531ee0fdSIan Rogers 		strlcpy(str, test_event, sizeof(str));
153*531ee0fdSIan Rogers 
154*531ee0fdSIan Rogers 	pr_debug("Testing '%s'\n", str);
155*531ee0fdSIan Rogers 	parse_events_error__init(&err);
156*531ee0fdSIan Rogers 	ret = parse_events(evlist, str, &err);
157*531ee0fdSIan Rogers 	if (ret) {
158*531ee0fdSIan Rogers 		evlist__delete(evlist);
159*531ee0fdSIan Rogers 
160*531ee0fdSIan Rogers 		pr_debug("FAILED %s:%d failed to parse event '%s', err %d\n",
161*531ee0fdSIan Rogers 			 __FILE__, __LINE__, str, ret);
162*531ee0fdSIan Rogers 		parse_events_error__print(&err, str);
163*531ee0fdSIan Rogers 		ret = TEST_FAIL;
164*531ee0fdSIan Rogers 		goto out;
165*531ee0fdSIan Rogers 	}
166*531ee0fdSIan Rogers 
167*531ee0fdSIan Rogers 	ret = TEST_OK;
168*531ee0fdSIan Rogers 	if (with_pmu ? (evlist->core.nr_entries != 1) : (evlist->core.nr_entries < 1)) {
169*531ee0fdSIan Rogers 		pr_debug("FAILED %s:%d Unexpected number of events for '%s' of %d\n",
170*531ee0fdSIan Rogers 			 __FILE__, __LINE__, str, evlist->core.nr_entries);
171*531ee0fdSIan Rogers 		ret = TEST_FAIL;
172*531ee0fdSIan Rogers 		goto out;
173*531ee0fdSIan Rogers 	}
174*531ee0fdSIan Rogers 
175*531ee0fdSIan Rogers 	evlist__for_each_entry(evlist, evsel) {
176*531ee0fdSIan Rogers 		if (!perf_pmu__is_hwmon(evsel->pmu))
177*531ee0fdSIan Rogers 			continue;
178*531ee0fdSIan Rogers 
179*531ee0fdSIan Rogers 		if (evsel->core.attr.config != (u64)test_events[i].config) {
180*531ee0fdSIan Rogers 			pr_debug("FAILED %s:%d Unexpected config for '%s', %lld != %ld\n",
181*531ee0fdSIan Rogers 				__FILE__, __LINE__, str,
182*531ee0fdSIan Rogers 				evsel->core.attr.config,
183*531ee0fdSIan Rogers 				test_events[i].config);
184*531ee0fdSIan Rogers 			ret = TEST_FAIL;
185*531ee0fdSIan Rogers 			goto out;
186*531ee0fdSIan Rogers 		}
187*531ee0fdSIan Rogers 		found = true;
188*531ee0fdSIan Rogers 	}
189*531ee0fdSIan Rogers 
190*531ee0fdSIan Rogers 	if (!found) {
191*531ee0fdSIan Rogers 		pr_debug("FAILED %s:%d Didn't find hwmon event '%s' in parsed evsels\n",
192*531ee0fdSIan Rogers 			 __FILE__, __LINE__, str);
193*531ee0fdSIan Rogers 		ret = TEST_FAIL;
194*531ee0fdSIan Rogers 	}
195*531ee0fdSIan Rogers 
196*531ee0fdSIan Rogers out:
197*531ee0fdSIan Rogers 	evlist__delete(evlist);
198*531ee0fdSIan Rogers 	return ret;
199*531ee0fdSIan Rogers }
200*531ee0fdSIan Rogers 
201*531ee0fdSIan Rogers static int test__hwmon_pmu(bool with_pmu)
202*531ee0fdSIan Rogers {
203*531ee0fdSIan Rogers 	char dir[PATH_MAX];
204*531ee0fdSIan Rogers 	struct perf_pmu *pmu = test_pmu_get(dir, sizeof(dir));
205*531ee0fdSIan Rogers 	int ret = TEST_OK;
206*531ee0fdSIan Rogers 
207*531ee0fdSIan Rogers 	if (!pmu)
208*531ee0fdSIan Rogers 		return TEST_FAIL;
209*531ee0fdSIan Rogers 
210*531ee0fdSIan Rogers 	for (size_t i = 0; i < ARRAY_SIZE(test_events); i++) {
211*531ee0fdSIan Rogers 		ret = do_test(i, with_pmu, /*with_alias=*/false);
212*531ee0fdSIan Rogers 
213*531ee0fdSIan Rogers 		if (ret != TEST_OK)
214*531ee0fdSIan Rogers 			break;
215*531ee0fdSIan Rogers 
216*531ee0fdSIan Rogers 		ret = do_test(i, with_pmu, /*with_alias=*/true);
217*531ee0fdSIan Rogers 
218*531ee0fdSIan Rogers 		if (ret != TEST_OK)
219*531ee0fdSIan Rogers 			break;
220*531ee0fdSIan Rogers 	}
221*531ee0fdSIan Rogers 	test_pmu_put(dir, pmu);
222*531ee0fdSIan Rogers 	return ret;
223*531ee0fdSIan Rogers }
224*531ee0fdSIan Rogers 
225*531ee0fdSIan Rogers static int test__hwmon_pmu_without_pmu(struct test_suite *test __maybe_unused,
226*531ee0fdSIan Rogers 				      int subtest __maybe_unused)
227*531ee0fdSIan Rogers {
228*531ee0fdSIan Rogers 	return test__hwmon_pmu(/*with_pmu=*/false);
229*531ee0fdSIan Rogers }
230*531ee0fdSIan Rogers 
231*531ee0fdSIan Rogers static int test__hwmon_pmu_with_pmu(struct test_suite *test __maybe_unused,
232*531ee0fdSIan Rogers 				   int subtest __maybe_unused)
233*531ee0fdSIan Rogers {
234*531ee0fdSIan Rogers 	return test__hwmon_pmu(/*with_pmu=*/false);
235*531ee0fdSIan Rogers }
236*531ee0fdSIan Rogers 
2378c329057SIan Rogers static int test__parse_hwmon_filename(struct test_suite *test __maybe_unused,
2388c329057SIan Rogers 				      int subtest __maybe_unused)
2398c329057SIan Rogers {
2408c329057SIan Rogers 	const struct hwmon_parse_test {
2418c329057SIan Rogers 		const char *filename;
2428c329057SIan Rogers 		enum hwmon_type type;
2438c329057SIan Rogers 		int number;
2448c329057SIan Rogers 		enum hwmon_item item;
2458c329057SIan Rogers 		bool alarm;
2468c329057SIan Rogers 		bool parse_ok;
2478c329057SIan Rogers 	} tests[] = {
2488c329057SIan Rogers 		{
2498c329057SIan Rogers 			.filename = "cpu0_accuracy",
2508c329057SIan Rogers 			.type = HWMON_TYPE_CPU,
2518c329057SIan Rogers 			.number = 0,
2528c329057SIan Rogers 			.item = HWMON_ITEM_ACCURACY,
2538c329057SIan Rogers 			.alarm = false,
2548c329057SIan Rogers 			.parse_ok = true,
2558c329057SIan Rogers 		},
2568c329057SIan Rogers 		{
2578c329057SIan Rogers 			.filename = "temp1_input",
2588c329057SIan Rogers 			.type = HWMON_TYPE_TEMP,
2598c329057SIan Rogers 			.number = 1,
2608c329057SIan Rogers 			.item = HWMON_ITEM_INPUT,
2618c329057SIan Rogers 			.alarm = false,
2628c329057SIan Rogers 			.parse_ok = true,
2638c329057SIan Rogers 		},
2648c329057SIan Rogers 		{
2658c329057SIan Rogers 			.filename = "fan2_vid",
2668c329057SIan Rogers 			.type = HWMON_TYPE_FAN,
2678c329057SIan Rogers 			.number = 2,
2688c329057SIan Rogers 			.item = HWMON_ITEM_VID,
2698c329057SIan Rogers 			.alarm = false,
2708c329057SIan Rogers 			.parse_ok = true,
2718c329057SIan Rogers 		},
2728c329057SIan Rogers 		{
2738c329057SIan Rogers 			.filename = "power3_crit_alarm",
2748c329057SIan Rogers 			.type = HWMON_TYPE_POWER,
2758c329057SIan Rogers 			.number = 3,
2768c329057SIan Rogers 			.item = HWMON_ITEM_CRIT,
2778c329057SIan Rogers 			.alarm = true,
2788c329057SIan Rogers 			.parse_ok = true,
2798c329057SIan Rogers 		},
2808c329057SIan Rogers 		{
2818c329057SIan Rogers 			.filename = "intrusion4_average_interval_min_alarm",
2828c329057SIan Rogers 			.type = HWMON_TYPE_INTRUSION,
2838c329057SIan Rogers 			.number = 4,
2848c329057SIan Rogers 			.item = HWMON_ITEM_AVERAGE_INTERVAL_MIN,
2858c329057SIan Rogers 			.alarm = true,
2868c329057SIan Rogers 			.parse_ok = true,
2878c329057SIan Rogers 		},
2888c329057SIan Rogers 		{
2898c329057SIan Rogers 			.filename = "badtype5_baditem",
2908c329057SIan Rogers 			.type = HWMON_TYPE_NONE,
2918c329057SIan Rogers 			.number = 5,
2928c329057SIan Rogers 			.item = HWMON_ITEM_NONE,
2938c329057SIan Rogers 			.alarm = false,
2948c329057SIan Rogers 			.parse_ok = false,
2958c329057SIan Rogers 		},
2968c329057SIan Rogers 		{
2978c329057SIan Rogers 			.filename = "humidity6_baditem",
2988c329057SIan Rogers 			.type = HWMON_TYPE_NONE,
2998c329057SIan Rogers 			.number = 6,
3008c329057SIan Rogers 			.item = HWMON_ITEM_NONE,
3018c329057SIan Rogers 			.alarm = false,
3028c329057SIan Rogers 			.parse_ok = false,
3038c329057SIan Rogers 		},
3048c329057SIan Rogers 	};
3058c329057SIan Rogers 
3068c329057SIan Rogers 	for (size_t i = 0; i < ARRAY_SIZE(tests); i++) {
3078c329057SIan Rogers 		enum hwmon_type type;
3088c329057SIan Rogers 		int number;
3098c329057SIan Rogers 		enum hwmon_item item;
3108c329057SIan Rogers 		bool alarm;
3118c329057SIan Rogers 
3128c329057SIan Rogers 		TEST_ASSERT_EQUAL("parse_hwmon_filename",
3138c329057SIan Rogers 				parse_hwmon_filename(
3148c329057SIan Rogers 					tests[i].filename,
3158c329057SIan Rogers 					&type,
3168c329057SIan Rogers 					&number,
3178c329057SIan Rogers 					&item,
3188c329057SIan Rogers 					&alarm),
3198c329057SIan Rogers 				tests[i].parse_ok
3208c329057SIan Rogers 			);
3218c329057SIan Rogers 		if (tests[i].parse_ok) {
3228c329057SIan Rogers 			TEST_ASSERT_EQUAL("parse_hwmon_filename type", type, tests[i].type);
3238c329057SIan Rogers 			TEST_ASSERT_EQUAL("parse_hwmon_filename number", number, tests[i].number);
3248c329057SIan Rogers 			TEST_ASSERT_EQUAL("parse_hwmon_filename item", item, tests[i].item);
3258c329057SIan Rogers 			TEST_ASSERT_EQUAL("parse_hwmon_filename alarm", alarm, tests[i].alarm);
3268c329057SIan Rogers 		}
3278c329057SIan Rogers 	}
3288c329057SIan Rogers 	return TEST_OK;
3298c329057SIan Rogers }
3308c329057SIan Rogers 
3318c329057SIan Rogers static struct test_case tests__hwmon_pmu[] = {
3328c329057SIan Rogers 	TEST_CASE("Basic parsing test", parse_hwmon_filename),
333*531ee0fdSIan Rogers 	TEST_CASE("Parsing without PMU name", hwmon_pmu_without_pmu),
334*531ee0fdSIan Rogers 	TEST_CASE("Parsing with PMU name", hwmon_pmu_with_pmu),
3358c329057SIan Rogers 	{	.name = NULL, }
3368c329057SIan Rogers };
3378c329057SIan Rogers 
3388c329057SIan Rogers struct test_suite suite__hwmon_pmu = {
3398c329057SIan Rogers 	.desc = "Hwmon PMU",
3408c329057SIan Rogers 	.test_cases = tests__hwmon_pmu,
3418c329057SIan Rogers };
342