mirror of
https://github.com/raspberrypi/linux.git
synced 2025-12-20 08:42:06 +00:00
perf test 11 hwmon fails on s390 with this error
# ./perf test -Fv 11
--- start ---
---- end ----
11.1: Basic parsing test : Ok
--- start ---
Testing 'temp_test_hwmon_event1'
Using CPUID IBM,3931,704,A01,3.7,002f
temp_test_hwmon_event1 -> hwmon_a_test_hwmon_pmu/temp_test_hwmon_event1/
FAILED tests/hwmon_pmu.c:189 Unexpected config for
'temp_test_hwmon_event1', 292470092988416 != 655361
---- end ----
11.2: Parsing without PMU name : FAILED!
--- start ---
Testing 'hwmon_a_test_hwmon_pmu/temp_test_hwmon_event1/'
FAILED tests/hwmon_pmu.c:189 Unexpected config for
'hwmon_a_test_hwmon_pmu/temp_test_hwmon_event1/',
292470092988416 != 655361
---- end ----
11.3: Parsing with PMU name : FAILED!
#
The root cause is in member test_event::config which is initialized
to 0xA0001 or 655361. During event parsing a long list event parsing
functions are called and end up with this gdb call stack:
#0 hwmon_pmu__config_term (hwm=0x168dfd0, attr=0x3ffffff5ee8,
term=0x168db60, err=0x3ffffff81c8) at util/hwmon_pmu.c:623
#1 hwmon_pmu__config_terms (pmu=0x168dfd0, attr=0x3ffffff5ee8,
terms=0x3ffffff5ea8, err=0x3ffffff81c8) at util/hwmon_pmu.c:662
#2 0x00000000012f870c in perf_pmu__config_terms (pmu=0x168dfd0,
attr=0x3ffffff5ee8, terms=0x3ffffff5ea8, zero=false,
apply_hardcoded=false, err=0x3ffffff81c8) at util/pmu.c:1519
#3 0x00000000012f88a4 in perf_pmu__config (pmu=0x168dfd0, attr=0x3ffffff5ee8,
head_terms=0x3ffffff5ea8, apply_hardcoded=false, err=0x3ffffff81c8)
at util/pmu.c:1545
#4 0x00000000012680c4 in parse_events_add_pmu (parse_state=0x3ffffff7fb8,
list=0x168dc00, pmu=0x168dfd0, const_parsed_terms=0x3ffffff6090,
auto_merge_stats=true, alternate_hw_config=10)
at util/parse-events.c:1508
#5 0x00000000012684c6 in parse_events_multi_pmu_add (parse_state=0x3ffffff7fb8,
event_name=0x168ec10 "temp_test_hwmon_event1", hw_config=10,
const_parsed_terms=0x0, listp=0x3ffffff6230, loc_=0x3ffffff70e0)
at util/parse-events.c:1592
#6 0x00000000012f0e4e in parse_events_parse (_parse_state=0x3ffffff7fb8,
scanner=0x16878c0) at util/parse-events.y:293
#7 0x00000000012695a0 in parse_events__scanner (str=0x3ffffff81d8
"temp_test_hwmon_event1", input=0x0, parse_state=0x3ffffff7fb8)
at util/parse-events.c:1867
#8 0x000000000126a1e8 in __parse_events (evlist=0x168b580,
str=0x3ffffff81d8 "temp_test_hwmon_event1", pmu_filter=0x0,
err=0x3ffffff81c8, fake_pmu=false, warn_if_reordered=true,
fake_tp=false) at util/parse-events.c:2136
#9 0x00000000011e36aa in parse_events (evlist=0x168b580,
str=0x3ffffff81d8 "temp_test_hwmon_event1", err=0x3ffffff81c8)
at /root/linux/tools/perf/util/parse-events.h:41
#10 0x00000000011e3e64 in do_test (i=0, with_pmu=false, with_alias=false)
at tests/hwmon_pmu.c:164
#11 0x00000000011e422c in test__hwmon_pmu (with_pmu=false)
at tests/hwmon_pmu.c:219
#12 0x00000000011e431c in test__hwmon_pmu_without_pmu (test=0x1610368
<suite.hwmon_pmu>, subtest=1) at tests/hwmon_pmu.c:23
where the attr::config is set to value 292470092988416 or 0x10a0000000000
in line 625 of file ./util/hwmon_pmu.c:
attr->config = key.type_and_num;
However member key::type_and_num is defined as union and bit field:
union hwmon_pmu_event_key {
long type_and_num;
struct {
int num :16;
enum hwmon_type type :8;
};
};
s390 is big endian and Intel is little endian architecture.
The events for the hwmon dummy pmu have num = 1 or num = 2 and
type is set to HWMON_TYPE_TEMP (which is 10).
On s390 this assignes member key::type_and_num the value of
0x10a0000000000 (which is 292470092988416) as shown in above
trace output.
Fix this and export the structure/union hwmon_pmu_event_key
so the test shares the same implementation as the event parsing
functions for union and bit fields. This should avoid
endianess issues on all platforms.
Output after:
# ./perf test -F 11
11.1: Basic parsing test : Ok
11.2: Parsing without PMU name : Ok
11.3: Parsing with PMU name : Ok
#
Fixes: 531ee0fd48 ("perf test: Add hwmon "PMU" test")
Signed-off-by: Thomas Richter <tmricht@linux.ibm.com>
Reviewed-by: Ian Rogers <irogers@google.com>
Link: https://lore.kernel.org/r/20250131112400.568975-1-tmricht@linux.ibm.com
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
356 lines
8.3 KiB
C
356 lines
8.3 KiB
C
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
|
|
#include "debug.h"
|
|
#include "evlist.h"
|
|
#include "hwmon_pmu.h"
|
|
#include "parse-events.h"
|
|
#include "tests.h"
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#include <linux/compiler.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/string.h>
|
|
|
|
static const struct test_event {
|
|
const char *name;
|
|
const char *alias;
|
|
union hwmon_pmu_event_key key;
|
|
} test_events[] = {
|
|
{
|
|
"temp_test_hwmon_event1",
|
|
"temp1",
|
|
.key = {
|
|
.num = 1,
|
|
.type = 10
|
|
},
|
|
},
|
|
{
|
|
"temp_test_hwmon_event2",
|
|
"temp2",
|
|
.key = {
|
|
.num = 2,
|
|
.type = 10
|
|
},
|
|
},
|
|
};
|
|
|
|
/* Cleanup test PMU directory. */
|
|
static int test_pmu_put(const char *dir, struct perf_pmu *hwm)
|
|
{
|
|
char buf[PATH_MAX + 20];
|
|
int ret;
|
|
|
|
if (scnprintf(buf, sizeof(buf), "rm -fr %s", dir) < 0) {
|
|
pr_err("Failure to set up buffer for \"%s\"\n", dir);
|
|
return -EINVAL;
|
|
}
|
|
ret = system(buf);
|
|
if (ret)
|
|
pr_err("Failure to \"%s\"\n", buf);
|
|
|
|
list_del(&hwm->list);
|
|
perf_pmu__delete(hwm);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Prepare test PMU directory data, normally exported by kernel at
|
|
* /sys/class/hwmon/hwmon<number>/. Give as input a buffer to hold the file
|
|
* path, the result is PMU loaded using that directory.
|
|
*/
|
|
static struct perf_pmu *test_pmu_get(char *dir, size_t sz)
|
|
{
|
|
const char *test_hwmon_name_nl = "A test hwmon PMU\n";
|
|
const char *test_hwmon_name = "A test hwmon PMU";
|
|
/* Simulated hwmon items. */
|
|
const struct test_item {
|
|
const char *name;
|
|
const char *value;
|
|
} test_items[] = {
|
|
{ "temp1_label", "test hwmon event1\n", },
|
|
{ "temp1_input", "40000\n", },
|
|
{ "temp2_label", "test hwmon event2\n", },
|
|
{ "temp2_input", "50000\n", },
|
|
};
|
|
int hwmon_dirfd = -1, test_dirfd = -1, file;
|
|
struct perf_pmu *hwm = NULL;
|
|
ssize_t len;
|
|
|
|
/* Create equivalent of sysfs mount point. */
|
|
scnprintf(dir, sz, "/tmp/perf-hwmon-pmu-test-XXXXXX");
|
|
if (!mkdtemp(dir)) {
|
|
pr_err("mkdtemp failed\n");
|
|
dir[0] = '\0';
|
|
return NULL;
|
|
}
|
|
test_dirfd = open(dir, O_PATH|O_DIRECTORY);
|
|
if (test_dirfd < 0) {
|
|
pr_err("Failed to open test directory \"%s\"\n", dir);
|
|
goto err_out;
|
|
}
|
|
|
|
/* Create the test hwmon directory and give it a name. */
|
|
if (mkdirat(test_dirfd, "hwmon1234", 0755) < 0) {
|
|
pr_err("Failed to mkdir hwmon directory\n");
|
|
goto err_out;
|
|
}
|
|
hwmon_dirfd = openat(test_dirfd, "hwmon1234", O_DIRECTORY);
|
|
if (hwmon_dirfd < 0) {
|
|
pr_err("Failed to open test hwmon directory \"%s/hwmon1234\"\n", dir);
|
|
goto err_out;
|
|
}
|
|
file = openat(hwmon_dirfd, "name", O_WRONLY | O_CREAT, 0600);
|
|
if (file < 0) {
|
|
pr_err("Failed to open for writing file \"name\"\n");
|
|
goto err_out;
|
|
}
|
|
len = strlen(test_hwmon_name_nl);
|
|
if (write(file, test_hwmon_name_nl, len) < len) {
|
|
close(file);
|
|
pr_err("Failed to write to 'name' file\n");
|
|
goto err_out;
|
|
}
|
|
close(file);
|
|
|
|
/* Create test hwmon files. */
|
|
for (size_t i = 0; i < ARRAY_SIZE(test_items); i++) {
|
|
const struct test_item *item = &test_items[i];
|
|
|
|
file = openat(hwmon_dirfd, item->name, O_WRONLY | O_CREAT, 0600);
|
|
if (file < 0) {
|
|
pr_err("Failed to open for writing file \"%s\"\n", item->name);
|
|
goto err_out;
|
|
}
|
|
|
|
if (write(file, item->value, strlen(item->value)) < 0) {
|
|
pr_err("Failed to write to file \"%s\"\n", item->name);
|
|
close(file);
|
|
goto err_out;
|
|
}
|
|
close(file);
|
|
}
|
|
|
|
/* Make the PMU reading the files created above. */
|
|
hwm = perf_pmus__add_test_hwmon_pmu(hwmon_dirfd, "hwmon1234", test_hwmon_name);
|
|
if (!hwm)
|
|
pr_err("Test hwmon creation failed\n");
|
|
|
|
err_out:
|
|
if (!hwm) {
|
|
test_pmu_put(dir, hwm);
|
|
if (hwmon_dirfd >= 0)
|
|
close(hwmon_dirfd);
|
|
}
|
|
if (test_dirfd >= 0)
|
|
close(test_dirfd);
|
|
return hwm;
|
|
}
|
|
|
|
static int do_test(size_t i, bool with_pmu, bool with_alias)
|
|
{
|
|
const char *test_event = with_alias ? test_events[i].alias : test_events[i].name;
|
|
struct evlist *evlist = evlist__new();
|
|
struct evsel *evsel;
|
|
struct parse_events_error err;
|
|
int ret;
|
|
char str[128];
|
|
bool found = false;
|
|
|
|
if (!evlist) {
|
|
pr_err("evlist allocation failed\n");
|
|
return TEST_FAIL;
|
|
}
|
|
|
|
if (with_pmu)
|
|
snprintf(str, sizeof(str), "hwmon_a_test_hwmon_pmu/%s/", test_event);
|
|
else
|
|
strlcpy(str, test_event, sizeof(str));
|
|
|
|
pr_debug("Testing '%s'\n", str);
|
|
parse_events_error__init(&err);
|
|
ret = parse_events(evlist, str, &err);
|
|
if (ret) {
|
|
pr_debug("FAILED %s:%d failed to parse event '%s', err %d\n",
|
|
__FILE__, __LINE__, str, ret);
|
|
parse_events_error__print(&err, str);
|
|
ret = TEST_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
ret = TEST_OK;
|
|
if (with_pmu ? (evlist->core.nr_entries != 1) : (evlist->core.nr_entries < 1)) {
|
|
pr_debug("FAILED %s:%d Unexpected number of events for '%s' of %d\n",
|
|
__FILE__, __LINE__, str, evlist->core.nr_entries);
|
|
ret = TEST_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
evlist__for_each_entry(evlist, evsel) {
|
|
if (!evsel->pmu || !evsel->pmu->name ||
|
|
strcmp(evsel->pmu->name, "hwmon_a_test_hwmon_pmu"))
|
|
continue;
|
|
|
|
if (evsel->core.attr.config != (u64)test_events[i].key.type_and_num) {
|
|
pr_debug("FAILED %s:%d Unexpected config for '%s', %lld != %ld\n",
|
|
__FILE__, __LINE__, str,
|
|
evsel->core.attr.config,
|
|
test_events[i].key.type_and_num);
|
|
ret = TEST_FAIL;
|
|
goto out;
|
|
}
|
|
found = true;
|
|
}
|
|
|
|
if (!found) {
|
|
pr_debug("FAILED %s:%d Didn't find hwmon event '%s' in parsed evsels\n",
|
|
__FILE__, __LINE__, str);
|
|
ret = TEST_FAIL;
|
|
}
|
|
|
|
out:
|
|
parse_events_error__exit(&err);
|
|
evlist__delete(evlist);
|
|
return ret;
|
|
}
|
|
|
|
static int test__hwmon_pmu(bool with_pmu)
|
|
{
|
|
char dir[PATH_MAX];
|
|
struct perf_pmu *pmu = test_pmu_get(dir, sizeof(dir));
|
|
int ret = TEST_OK;
|
|
|
|
if (!pmu)
|
|
return TEST_FAIL;
|
|
|
|
for (size_t i = 0; i < ARRAY_SIZE(test_events); i++) {
|
|
ret = do_test(i, with_pmu, /*with_alias=*/false);
|
|
|
|
if (ret != TEST_OK)
|
|
break;
|
|
|
|
ret = do_test(i, with_pmu, /*with_alias=*/true);
|
|
|
|
if (ret != TEST_OK)
|
|
break;
|
|
}
|
|
test_pmu_put(dir, pmu);
|
|
return ret;
|
|
}
|
|
|
|
static int test__hwmon_pmu_without_pmu(struct test_suite *test __maybe_unused,
|
|
int subtest __maybe_unused)
|
|
{
|
|
return test__hwmon_pmu(/*with_pmu=*/false);
|
|
}
|
|
|
|
static int test__hwmon_pmu_with_pmu(struct test_suite *test __maybe_unused,
|
|
int subtest __maybe_unused)
|
|
{
|
|
return test__hwmon_pmu(/*with_pmu=*/true);
|
|
}
|
|
|
|
static int test__parse_hwmon_filename(struct test_suite *test __maybe_unused,
|
|
int subtest __maybe_unused)
|
|
{
|
|
const struct hwmon_parse_test {
|
|
const char *filename;
|
|
enum hwmon_type type;
|
|
int number;
|
|
enum hwmon_item item;
|
|
bool alarm;
|
|
bool parse_ok;
|
|
} tests[] = {
|
|
{
|
|
.filename = "cpu0_accuracy",
|
|
.type = HWMON_TYPE_CPU,
|
|
.number = 0,
|
|
.item = HWMON_ITEM_ACCURACY,
|
|
.alarm = false,
|
|
.parse_ok = true,
|
|
},
|
|
{
|
|
.filename = "temp1_input",
|
|
.type = HWMON_TYPE_TEMP,
|
|
.number = 1,
|
|
.item = HWMON_ITEM_INPUT,
|
|
.alarm = false,
|
|
.parse_ok = true,
|
|
},
|
|
{
|
|
.filename = "fan2_vid",
|
|
.type = HWMON_TYPE_FAN,
|
|
.number = 2,
|
|
.item = HWMON_ITEM_VID,
|
|
.alarm = false,
|
|
.parse_ok = true,
|
|
},
|
|
{
|
|
.filename = "power3_crit_alarm",
|
|
.type = HWMON_TYPE_POWER,
|
|
.number = 3,
|
|
.item = HWMON_ITEM_CRIT,
|
|
.alarm = true,
|
|
.parse_ok = true,
|
|
},
|
|
{
|
|
.filename = "intrusion4_average_interval_min_alarm",
|
|
.type = HWMON_TYPE_INTRUSION,
|
|
.number = 4,
|
|
.item = HWMON_ITEM_AVERAGE_INTERVAL_MIN,
|
|
.alarm = true,
|
|
.parse_ok = true,
|
|
},
|
|
{
|
|
.filename = "badtype5_baditem",
|
|
.type = HWMON_TYPE_NONE,
|
|
.number = 5,
|
|
.item = HWMON_ITEM_NONE,
|
|
.alarm = false,
|
|
.parse_ok = false,
|
|
},
|
|
{
|
|
.filename = "humidity6_baditem",
|
|
.type = HWMON_TYPE_NONE,
|
|
.number = 6,
|
|
.item = HWMON_ITEM_NONE,
|
|
.alarm = false,
|
|
.parse_ok = false,
|
|
},
|
|
};
|
|
|
|
for (size_t i = 0; i < ARRAY_SIZE(tests); i++) {
|
|
enum hwmon_type type;
|
|
int number;
|
|
enum hwmon_item item;
|
|
bool alarm;
|
|
|
|
TEST_ASSERT_EQUAL("parse_hwmon_filename",
|
|
parse_hwmon_filename(
|
|
tests[i].filename,
|
|
&type,
|
|
&number,
|
|
&item,
|
|
&alarm),
|
|
tests[i].parse_ok
|
|
);
|
|
if (tests[i].parse_ok) {
|
|
TEST_ASSERT_EQUAL("parse_hwmon_filename type", type, tests[i].type);
|
|
TEST_ASSERT_EQUAL("parse_hwmon_filename number", number, tests[i].number);
|
|
TEST_ASSERT_EQUAL("parse_hwmon_filename item", item, tests[i].item);
|
|
TEST_ASSERT_EQUAL("parse_hwmon_filename alarm", alarm, tests[i].alarm);
|
|
}
|
|
}
|
|
return TEST_OK;
|
|
}
|
|
|
|
static struct test_case tests__hwmon_pmu[] = {
|
|
TEST_CASE("Basic parsing test", parse_hwmon_filename),
|
|
TEST_CASE("Parsing without PMU name", hwmon_pmu_without_pmu),
|
|
TEST_CASE("Parsing with PMU name", hwmon_pmu_with_pmu),
|
|
{ .name = NULL, }
|
|
};
|
|
|
|
struct test_suite suite__hwmon_pmu = {
|
|
.desc = "Hwmon PMU",
|
|
.test_cases = tests__hwmon_pmu,
|
|
};
|