Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions nodescraper/plugins/inband/dmesg/mce_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

_CORRECTABLE_SUMMARY_RE = re.compile(
r"(?P<count>\d+)\s+correctable hardware errors detected in total in (?P<block>\w+) block"
r"(?:\s+on\s+(?P<cpu>CPU\d+))?",
r"(?:\s+on\s+(?P<cpu>CPU:?\d+))?",
re.IGNORECASE,
)

Expand All @@ -52,16 +52,20 @@
)

_MCE_CE_STATUS_RE = re.compile(
r"\[Hardware Error\]:.*?(?P<cpu>CPU\d+).*?MC\d+_STATUS\[[^\]]*\|CE\|[^\]]*\]",
r"\[Hardware Error\]:.*?(?P<cpu>CPU:?\d+).*?MC\d+_STATUS\[[^\]]*\|CE\|[^\]]*\]",
re.IGNORECASE,
)

_MCE_UC_STATUS_RE = re.compile(
r"\[Hardware Error\]:.*?(?P<cpu>CPU\d+).*?MC\d+_STATUS\[[^\]]*\|UC\|[^\]]*\]",
r"\[Hardware Error\]:.*?(?P<cpu>CPU:?\d+).*?MC\d+_STATUS\[[^\]]*\|UC\|[^\]]*\]",
re.IGNORECASE,
)


def _normalize_cpu_label(cpu: str) -> str:
return cpu.replace(":", "")


def _add_count(counts: dict[str, int], part: str, amount: int) -> None:
counts[part] = counts.get(part, 0) + amount

Expand Down Expand Up @@ -120,8 +124,9 @@ def parse_correctable_mce_counts(

summary_match = _CORRECTABLE_SUMMARY_RE.search(line)
if summary_match:
cpu = summary_match.group("cpu")
part = _part_label(
cpu=summary_match.group("cpu"),
cpu=_normalize_cpu_label(cpu) if cpu else None,
block=summary_match.group("block"),
)
_add_count(counts, part, int(summary_match.group("count")))
Expand All @@ -132,7 +137,11 @@ def parse_correctable_mce_counts(
bank = extract_mce_bank_from_line(line)
if bank is not None and bank in ignored:
continue
part = status_match.group("cpu") if status_match.group("cpu") else "unknown"
part = (
_normalize_cpu_label(status_match.group("cpu"))
if status_match.group("cpu")
else "unknown"
)
_add_count(counts, part, 1)

return counts
Expand Down Expand Up @@ -170,7 +179,11 @@ def parse_uncorrectable_mce_counts(
bank = extract_mce_bank_from_line(line)
if bank is not None and bank in ignored:
continue
part = status_match.group("cpu") if status_match.group("cpu") else "unknown"
part = (
_normalize_cpu_label(status_match.group("cpu"))
if status_match.group("cpu")
else "unknown"
)
_add_count(counts, part, 1)

return counts
20 changes: 20 additions & 0 deletions test/unit/plugin/test_dmesg_analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -1043,6 +1043,26 @@ def test_mce_threshold_raises_error_for_gpu(system_info):
assert res.status == ExecutionStatus.ERROR


def test_mce_threshold_raises_error_for_cpu_colon_status(system_info):
dmesg_content = (
"kern :err : 2038-01-19T00:00:00,000000+00:00 "
"[Hardware Error]: CPU:72 (00:00:0) MC60_STATUS[-|CE|Misc]: 0xabc\n"
)

analyzer = DmesgAnalyzer(system_info=system_info)
res = analyzer.analyze_data(
DmesgData(dmesg_content=dmesg_content),
args=DmesgAnalyzerArgs(check_unknown_dmesg_errors=False, mce_threshold=1),
)

threshold_events = [e for e in res.events if e.data.get("mce_threshold") == 1]
assert len(threshold_events) == 1
assert threshold_events[0].priority == EventPriority.ERROR
assert threshold_events[0].data["part"] == "CPU72"
assert threshold_events[0].data["correctable_mce_count"] == 1
assert res.status == ExecutionStatus.ERROR


def test_mce_threshold_not_triggered_below_limit(system_info):
dmesg_content = (
"kern :warn : 2024-06-11T14:30:00,123456+00:00 "
Expand Down
36 changes: 36 additions & 0 deletions test/unit/plugin/test_mce_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,39 @@ def test_parse_correctable_mce_counts_skips_ignored_banks():
counts = parse_correctable_mce_counts(content, ignore_banks=frozenset({1, 2}))

assert counts == {"CPU0": 1}


def test_parse_correctable_mce_counts_cpu_colon_status():
content = (
"kern :err : 2038-01-19T00:00:00,000000+00:00 "
"[Hardware Error]: CPU:72 (00:00:0) MC60_STATUS[-|CE|Misc]: 0xabc\n"
)

counts = parse_correctable_mce_counts(content)

assert counts == {"CPU72": 1}


def test_parse_correctable_mce_counts_both_cpu_formats():
content = (
"[Hardware Error]: CPU0 MC1_STATUS[0x0|CE|]: 0x1\n"
"kern :err : 2038-01-19T00:00:00,000000+00:00 "
"[Hardware Error]: CPU:72 (00:00:0) MC60_STATUS[-|CE|Misc]: 0xabc\n"
"kern :warn : 2024-06-11T14:30:00,123456+00:00 "
"mce: 2 correctable hardware errors detected in total in mc0 block on CPU:1\n"
)

counts = parse_correctable_mce_counts(content)

assert counts == {"CPU0": 1, "CPU72": 1, "CPU1/mc0": 2}


def test_parse_uncorrectable_mce_counts_cpu_colon_status():
content = (
"kern :err : 2038-01-19T00:00:01,000000+00:00 "
"[Hardware Error]: CPU:72 (00:00:0) MC60_STATUS[-|UC|Misc]: 0xdef\n"
)

counts = parse_uncorrectable_mce_counts(content)

assert counts == {"CPU72": 1}