xref: /linux/tools/perf/tests/hwmon_pmu.c (revision 9e906a9dead17d81d6c2687f65e159231d0e3286)
1 // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2 #include "debug.h"
3 #include "evlist.h"
4 #include "hwmon_pmu.h"
5 #include "parse-events.h"
6 #include "tests.h"
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <sys/stat.h>
10 #include <linux/compiler.h>
11 #include <linux/kernel.h>
12 #include <linux/string.h>
13 
14 static const struct test_event {
15 	const char *name;
16 	const char *alias;
17 	union hwmon_pmu_event_key key;
18 } test_events[] = {
19 	{
20 		"temp_test_hwmon_event1",
21 		"temp1",
22 		.key = {
23 			.num = 1,
24 			.type = 10
25 		},
26 	},
27 	{
28 		"temp_test_hwmon_event2",
29 		"temp2",
30 		.key = {
31 			.num = 2,
32 			.type = 10
33 		},
34 	},
35 };
36 
37 /* Cleanup test PMU directory. */
test_pmu_put(const char * dir,struct perf_pmu * hwm)38 static int test_pmu_put(const char *dir, struct perf_pmu *hwm)
39 {
40 	char buf[PATH_MAX + 20];
41 	int ret;
42 
43 	if (scnprintf(buf, sizeof(buf), "rm -fr %s", dir) < 0) {
44 		pr_err("Failure to set up buffer for \"%s\"\n", dir);
45 		return -EINVAL;
46 	}
47 	ret = system(buf);
48 	if (ret)
49 		pr_err("Failure to \"%s\"\n", buf);
50 
51 	list_del(&hwm->list);
52 	perf_pmu__delete(hwm);
53 	return ret;
54 }
55 
56 /*
57  * Prepare test PMU directory data, normally exported by kernel at
58  * /sys/class/hwmon/hwmon<number>/. Give as input a buffer to hold the file
59  * path, the result is PMU loaded using that directory.
60  */
test_pmu_get(char * dir,size_t sz)61 static struct perf_pmu *test_pmu_get(char *dir, size_t sz)
62 {
63 	const char *test_hwmon_name_nl = "A test hwmon PMU\n";
64 	const char *test_hwmon_name = "A test hwmon PMU";
65 	/* Simulated hwmon items. */
66 	const struct test_item {
67 		const char *name;
68 		const char *value;
69 	} test_items[] = {
70 		{ "temp1_label", "test hwmon event1\n", },
71 		{ "temp1_input", "40000\n", },
72 		{ "temp2_label", "test hwmon event2\n", },
73 		{ "temp2_input", "50000\n", },
74 	};
75 	int hwmon_dirfd = -1, test_dirfd = -1, file;
76 	struct perf_pmu *hwm = NULL;
77 	ssize_t len;
78 
79 	/* Create equivalent of sysfs mount point. */
80 	scnprintf(dir, sz, "/tmp/perf-hwmon-pmu-test-XXXXXX");
81 	if (!mkdtemp(dir)) {
82 		pr_err("mkdtemp failed\n");
83 		dir[0] = '\0';
84 		return NULL;
85 	}
86 	test_dirfd = open(dir, O_PATH|O_DIRECTORY);
87 	if (test_dirfd < 0) {
88 		pr_err("Failed to open test directory \"%s\"\n", dir);
89 		goto err_out;
90 	}
91 
92 	/* Create the test hwmon directory and give it a name. */
93 	if (mkdirat(test_dirfd, "hwmon1234", 0755) < 0) {
94 		pr_err("Failed to mkdir hwmon directory\n");
95 		goto err_out;
96 	}
97 	strncat(dir, "/hwmon1234", sz - strlen(dir));
98 	hwmon_dirfd = open(dir, O_PATH|O_DIRECTORY);
99 	if (hwmon_dirfd < 0) {
100 		pr_err("Failed to open test hwmon directory \"%s\"\n", dir);
101 		goto err_out;
102 	}
103 	file = openat(hwmon_dirfd, "name", O_WRONLY | O_CREAT, 0600);
104 	if (file < 0) {
105 		pr_err("Failed to open for writing file \"name\"\n");
106 		goto err_out;
107 	}
108 	len = strlen(test_hwmon_name_nl);
109 	if (write(file, test_hwmon_name_nl, len) < len) {
110 		close(file);
111 		pr_err("Failed to write to 'name' file\n");
112 		goto err_out;
113 	}
114 	close(file);
115 
116 	/* Create test hwmon files. */
117 	for (size_t i = 0; i < ARRAY_SIZE(test_items); i++) {
118 		const struct test_item *item = &test_items[i];
119 
120 		file = openat(hwmon_dirfd, item->name, O_WRONLY | O_CREAT, 0600);
121 		if (file < 0) {
122 			pr_err("Failed to open for writing file \"%s\"\n", item->name);
123 			goto err_out;
124 		}
125 
126 		if (write(file, item->value, strlen(item->value)) < 0) {
127 			pr_err("Failed to write to file \"%s\"\n", item->name);
128 			close(file);
129 			goto err_out;
130 		}
131 		close(file);
132 	}
133 
134 	/* Make the PMU reading the files created above. */
135 	hwm = perf_pmus__add_test_hwmon_pmu(dir, "hwmon1234", test_hwmon_name);
136 	if (!hwm)
137 		pr_err("Test hwmon creation failed\n");
138 
139 err_out:
140 	if (!hwm) {
141 		test_pmu_put(dir, hwm);
142 	}
143 	if (test_dirfd >= 0)
144 		close(test_dirfd);
145 	if (hwmon_dirfd >= 0)
146 		close(hwmon_dirfd);
147 	return hwm;
148 }
149 
do_test(size_t i,bool with_pmu,bool with_alias)150 static int do_test(size_t i, bool with_pmu, bool with_alias)
151 {
152 	const char *test_event = with_alias ? test_events[i].alias : test_events[i].name;
153 	struct evlist *evlist = evlist__new();
154 	struct evsel *evsel;
155 	struct parse_events_error err;
156 	int ret;
157 	char str[128];
158 	bool found = false;
159 
160 	if (!evlist) {
161 		pr_err("evlist allocation failed\n");
162 		return TEST_FAIL;
163 	}
164 
165 	if (with_pmu)
166 		snprintf(str, sizeof(str), "hwmon_a_test_hwmon_pmu/%s/", test_event);
167 	else
168 		strlcpy(str, test_event, sizeof(str));
169 
170 	pr_debug("Testing '%s'\n", str);
171 	parse_events_error__init(&err);
172 	ret = parse_events(evlist, str, &err);
173 	if (ret) {
174 		pr_debug("FAILED %s:%d failed to parse event '%s', err %d\n",
175 			 __FILE__, __LINE__, str, ret);
176 		parse_events_error__print(&err, str);
177 		ret = TEST_FAIL;
178 		goto out;
179 	}
180 
181 	ret = TEST_OK;
182 	if (with_pmu ? (evlist->core.nr_entries != 1) : (evlist->core.nr_entries < 1)) {
183 		pr_debug("FAILED %s:%d Unexpected number of events for '%s' of %d\n",
184 			 __FILE__, __LINE__, str, evlist->core.nr_entries);
185 		ret = TEST_FAIL;
186 		goto out;
187 	}
188 
189 	evlist__for_each_entry(evlist, evsel) {
190 		if (!evsel->pmu || !evsel->pmu->name ||
191 		    strcmp(evsel->pmu->name, "hwmon_a_test_hwmon_pmu"))
192 			continue;
193 
194 		if (evsel->core.attr.config != (u64)test_events[i].key.type_and_num) {
195 			pr_debug("FAILED %s:%d Unexpected config for '%s', %lld != %ld\n",
196 				__FILE__, __LINE__, str,
197 				evsel->core.attr.config,
198 				test_events[i].key.type_and_num);
199 			ret = TEST_FAIL;
200 			goto out;
201 		}
202 		found = true;
203 	}
204 
205 	if (!found) {
206 		pr_debug("FAILED %s:%d Didn't find hwmon event '%s' in parsed evsels\n",
207 			 __FILE__, __LINE__, str);
208 		ret = TEST_FAIL;
209 	}
210 
211 out:
212 	parse_events_error__exit(&err);
213 	evlist__delete(evlist);
214 	return ret;
215 }
216 
test__hwmon_pmu(bool with_pmu)217 static int test__hwmon_pmu(bool with_pmu)
218 {
219 	char dir[PATH_MAX];
220 	struct perf_pmu *pmu = test_pmu_get(dir, sizeof(dir));
221 	int ret = TEST_OK;
222 
223 	if (!pmu)
224 		return TEST_FAIL;
225 
226 	for (size_t i = 0; i < ARRAY_SIZE(test_events); i++) {
227 		ret = do_test(i, with_pmu, /*with_alias=*/false);
228 
229 		if (ret != TEST_OK)
230 			break;
231 
232 		ret = do_test(i, with_pmu, /*with_alias=*/true);
233 
234 		if (ret != TEST_OK)
235 			break;
236 	}
237 	test_pmu_put(dir, pmu);
238 	return ret;
239 }
240 
test__hwmon_pmu_without_pmu(struct test_suite * test __maybe_unused,int subtest __maybe_unused)241 static int test__hwmon_pmu_without_pmu(struct test_suite *test __maybe_unused,
242 				      int subtest __maybe_unused)
243 {
244 	return test__hwmon_pmu(/*with_pmu=*/false);
245 }
246 
test__hwmon_pmu_with_pmu(struct test_suite * test __maybe_unused,int subtest __maybe_unused)247 static int test__hwmon_pmu_with_pmu(struct test_suite *test __maybe_unused,
248 				   int subtest __maybe_unused)
249 {
250 	return test__hwmon_pmu(/*with_pmu=*/true);
251 }
252 
test__parse_hwmon_filename(struct test_suite * test __maybe_unused,int subtest __maybe_unused)253 static int test__parse_hwmon_filename(struct test_suite *test __maybe_unused,
254 				      int subtest __maybe_unused)
255 {
256 	const struct hwmon_parse_test {
257 		const char *filename;
258 		enum hwmon_type type;
259 		int number;
260 		enum hwmon_item item;
261 		bool alarm;
262 		bool parse_ok;
263 	} tests[] = {
264 		{
265 			.filename = "cpu0_accuracy",
266 			.type = HWMON_TYPE_CPU,
267 			.number = 0,
268 			.item = HWMON_ITEM_ACCURACY,
269 			.alarm = false,
270 			.parse_ok = true,
271 		},
272 		{
273 			.filename = "temp1_input",
274 			.type = HWMON_TYPE_TEMP,
275 			.number = 1,
276 			.item = HWMON_ITEM_INPUT,
277 			.alarm = false,
278 			.parse_ok = true,
279 		},
280 		{
281 			.filename = "fan2_vid",
282 			.type = HWMON_TYPE_FAN,
283 			.number = 2,
284 			.item = HWMON_ITEM_VID,
285 			.alarm = false,
286 			.parse_ok = true,
287 		},
288 		{
289 			.filename = "power3_crit_alarm",
290 			.type = HWMON_TYPE_POWER,
291 			.number = 3,
292 			.item = HWMON_ITEM_CRIT,
293 			.alarm = true,
294 			.parse_ok = true,
295 		},
296 		{
297 			.filename = "intrusion4_average_interval_min_alarm",
298 			.type = HWMON_TYPE_INTRUSION,
299 			.number = 4,
300 			.item = HWMON_ITEM_AVERAGE_INTERVAL_MIN,
301 			.alarm = true,
302 			.parse_ok = true,
303 		},
304 		{
305 			.filename = "badtype5_baditem",
306 			.type = HWMON_TYPE_NONE,
307 			.number = 5,
308 			.item = HWMON_ITEM_NONE,
309 			.alarm = false,
310 			.parse_ok = false,
311 		},
312 		{
313 			.filename = "humidity6_baditem",
314 			.type = HWMON_TYPE_NONE,
315 			.number = 6,
316 			.item = HWMON_ITEM_NONE,
317 			.alarm = false,
318 			.parse_ok = false,
319 		},
320 	};
321 
322 	for (size_t i = 0; i < ARRAY_SIZE(tests); i++) {
323 		enum hwmon_type type;
324 		int number;
325 		enum hwmon_item item;
326 		bool alarm;
327 
328 		TEST_ASSERT_EQUAL("parse_hwmon_filename",
329 				parse_hwmon_filename(
330 					tests[i].filename,
331 					&type,
332 					&number,
333 					&item,
334 					&alarm),
335 				tests[i].parse_ok
336 			);
337 		if (tests[i].parse_ok) {
338 			TEST_ASSERT_EQUAL("parse_hwmon_filename type", type, tests[i].type);
339 			TEST_ASSERT_EQUAL("parse_hwmon_filename number", number, tests[i].number);
340 			TEST_ASSERT_EQUAL("parse_hwmon_filename item", item, tests[i].item);
341 			TEST_ASSERT_EQUAL("parse_hwmon_filename alarm", alarm, tests[i].alarm);
342 		}
343 	}
344 	return TEST_OK;
345 }
346 
347 static struct test_case tests__hwmon_pmu[] = {
348 	TEST_CASE("Basic parsing test", parse_hwmon_filename),
349 	TEST_CASE("Parsing without PMU name", hwmon_pmu_without_pmu),
350 	TEST_CASE("Parsing with PMU name", hwmon_pmu_with_pmu),
351 	{	.name = NULL, }
352 };
353 
354 struct test_suite suite__hwmon_pmu = {
355 	.desc = "Hwmon PMU",
356 	.test_cases = tests__hwmon_pmu,
357 };
358