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