hwmon_pmu.c 8.3 KB

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