Skip to content

Commit 007d017

Browse files
committed
In prerequisites capture the inferred ACPI C state information
Some platforms support more ACPI C-states than others. There is no checks to go with this right now, but just capture information in case of problems. Signed-off-by: Mario Limonciello <superm1@kernel.org>
1 parent 1bba3fe commit 007d017

File tree

2 files changed

+89
-0
lines changed

2 files changed

+89
-0
lines changed

src/amd_debug/prerequisites.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,6 +1031,21 @@ def capture_acpi(self):
10311031
shutil.rmtree(tmpd)
10321032
return True
10331033

1034+
def capture_cstates(self):
1035+
"""Capture ACPI C state information for the first CPU (assumes the same for all CPUs)"""
1036+
base = os.path.join("/", "sys", "bus", "cpu", "devices", "cpu0", "cpuidle")
1037+
paths = {}
1038+
for root, _dirs, files in os.walk(base, topdown=False):
1039+
for fname in files:
1040+
target = os.path.join(root, fname)
1041+
with open(target, "rb") as f:
1042+
paths[target] = f.read()
1043+
debug_str = "ACPI C-state information\n"
1044+
for path, data in paths.items():
1045+
prefix = "│ " if path != list(paths.keys())[-1] else "└─"
1046+
debug_str += f"{prefix}{path}: {data.decode('utf-8', 'ignore')}"
1047+
self.db.record_debug(debug_str)
1048+
10341049
def capture_battery(self):
10351050
"""Capture battery information"""
10361051
obj = Batteries()
@@ -1268,6 +1283,7 @@ def run(self):
12681283
self.capture_pci_acpi,
12691284
self.capture_edid,
12701285
self.capture_nvidia,
1286+
self.capture_cstates,
12711287
]
12721288
checks = []
12731289

src/test_prerequisites.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2035,3 +2035,76 @@ def test_capture_nvidia_permission_error_on_gpu_file(self, mock_exists, mock_wal
20352035
self.mock_db.record_prereq.assert_called_with(
20362036
"NVIDIA GPU {f} not readable", "👀"
20372037
)
2038+
2039+
@patch("amd_debug.prerequisites.os.walk")
2040+
@patch(
2041+
"builtins.open",
2042+
new_callable=unittest.mock.mock_open,
2043+
read_data=b"C1 state info",
2044+
)
2045+
def test_capture_cstates_single_file(self, mock_open, mock_walk):
2046+
"""Test capture_cstates with a single cpuidle file"""
2047+
mock_walk.return_value = [
2048+
("/sys/bus/cpu/devices/cpu0/cpuidle", [], ["state1"]),
2049+
]
2050+
self.validator.capture_cstates()
2051+
self.mock_db.record_debug.assert_called_with(
2052+
"ACPI C-state information\n└─/sys/bus/cpu/devices/cpu0/cpuidle/state1: C1 state info"
2053+
)
2054+
2055+
@patch("amd_debug.prerequisites.os.walk")
2056+
@patch("builtins.open", new_callable=mock_open)
2057+
def test_capture_cstates_multiple_files(self, mock_open_func, mock_walk):
2058+
"""Test capture_cstates with multiple cpuidle files"""
2059+
# Setup mock file reads for two files
2060+
file_contents = {
2061+
"/sys/bus/cpu/devices/cpu0/cpuidle/state1": b"C1 info",
2062+
"/sys/bus/cpu/devices/cpu0/cpuidle/state2": b"C2 info",
2063+
}
2064+
2065+
def side_effect(path, mode="rb"):
2066+
mock_file = mock_open(read_data=file_contents[path])()
2067+
return mock_file
2068+
2069+
mock_open_func.side_effect = side_effect
2070+
mock_walk.return_value = [
2071+
("/sys/bus/cpu/devices/cpu0/cpuidle", [], ["state1", "state2"]),
2072+
]
2073+
self.validator.capture_cstates()
2074+
# The prefix logic is based on order, so check for both lines
2075+
debug_call = self.mock_db.record_debug.call_args[0][0]
2076+
self.assertIn("/sys/bus/cpu/devices/cpu0/cpuidle/state1: C1 info", debug_call)
2077+
self.assertIn("/sys/bus/cpu/devices/cpu0/cpuidle/state2: C2 info", debug_call)
2078+
self.assertTrue(debug_call.startswith("ACPI C-state information\n"))
2079+
2080+
@patch("amd_debug.prerequisites.os.walk")
2081+
@patch("builtins.open", new_callable=mock_open, read_data=b"")
2082+
def test_capture_cstates_empty_files(self, _mock_open, mock_walk):
2083+
"""Test capture_cstates with empty cpuidle files"""
2084+
mock_walk.return_value = [
2085+
("/sys/bus/cpu/devices/cpu0/cpuidle", [], ["state1"]),
2086+
]
2087+
self.validator.capture_cstates()
2088+
self.mock_db.record_debug.assert_called_with(
2089+
"ACPI C-state information\n└─/sys/bus/cpu/devices/cpu0/cpuidle/state1: "
2090+
)
2091+
2092+
@patch("amd_debug.prerequisites.os.walk")
2093+
@patch("builtins.open", side_effect=PermissionError)
2094+
def test_capture_cstates_permission_error(self, _mock_open, mock_walk):
2095+
"""Test capture_cstates when reading cpuidle files raises PermissionError"""
2096+
mock_walk.return_value = [
2097+
("/sys/bus/cpu/devices/cpu0/cpuidle", [], ["state1"]),
2098+
]
2099+
with self.assertRaises(PermissionError):
2100+
self.validator.capture_cstates()
2101+
self.mock_db.record_debug.assert_not_called()
2102+
2103+
@patch("amd_debug.prerequisites.os.walk")
2104+
def test_capture_cstates_no_files(self, mock_walk):
2105+
"""Test capture_cstates when no cpuidle files are present"""
2106+
mock_walk.return_value = [
2107+
("/sys/bus/cpu/devices/cpu0/cpuidle", [], []),
2108+
]
2109+
self.validator.capture_cstates()
2110+
self.mock_db.record_debug.assert_called_with("ACPI C-state information\n")

0 commit comments

Comments
 (0)