File Index
Files and directories
Sign in
FILE

hwmon_pmu.c

File preview with syntax highlighting.

linux-6.19.14/tools/perf/tests/hwmon_pmu.c C
  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
 14static 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. */
 38static 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 */
 61static 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
139err_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
150static 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
211out:
212	parse_events_error__exit(&err);
213	evlist__delete(evlist);
214	return ret;
215}
216
217static 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
241static 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
247static 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
253static 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
347static 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
354struct test_suite suite__hwmon_pmu = {
355	.desc = "Hwmon PMU",
356	.test_cases = tests__hwmon_pmu,
357};
~/ Read-only file preview.