Linux Audio

Check our new training course

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