Skip to content

Commit ef5f294

Browse files
committed
crash_report.py: add a crash report
This can be used by CIs to augment the commit log. Signed-off-by: Luis Chamberlain <[email protected]>
1 parent 3f180ab commit ef5f294

File tree

1 file changed

+109
-0
lines changed

1 file changed

+109
-0
lines changed
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
#!/usr/bin/env python3
2+
import os
3+
import re
4+
from pathlib import Path
5+
6+
CRASH_DIR = Path("crashes")
7+
ANSI_ESCAPE_RE = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])")
8+
9+
10+
def clean_lines(text):
11+
lines = text.splitlines()
12+
cleaned = []
13+
for line in lines:
14+
line = ANSI_ESCAPE_RE.sub("", line)
15+
line = re.sub(r"[^\x20-\x7E\t]", "", line)
16+
if line.strip():
17+
cleaned.append(line)
18+
return "\n".join(cleaned)
19+
20+
21+
def collect_host_logs(host_path):
22+
entries = []
23+
files = sorted(host_path.iterdir())
24+
seen_base = set()
25+
26+
# First pass: collect decoded crash files
27+
for file in files:
28+
if not file.is_file():
29+
continue
30+
fname = file.name
31+
32+
# Look for decoded crash variants first
33+
for suffix in [".crash", ".crash_and_corruption"]:
34+
if fname.endswith(".decoded" + suffix):
35+
# Store the original filename (without .decoded) in seen_base
36+
base = fname.replace(".decoded" + suffix, suffix)
37+
seen_base.add(base)
38+
entries.append(
39+
{
40+
"type": "crash",
41+
"file": fname,
42+
"content": clean_lines(file.read_text(errors="replace")),
43+
}
44+
)
45+
46+
# Second pass: collect remaining files (non-decoded crashes and other types)
47+
for file in files:
48+
if not file.is_file():
49+
continue
50+
fname = file.name
51+
52+
# Handle undecoded crash files only if we didn't already see their decoded version
53+
for suffix in [".crash", ".crash_and_corruption"]:
54+
if fname.endswith(suffix) and not fname.endswith(".decoded" + suffix):
55+
if fname in seen_base:
56+
continue # decoded version already handled
57+
58+
seen_base.add(fname)
59+
entries.append(
60+
{
61+
"type": "crash",
62+
"file": fname,
63+
"content": clean_lines(file.read_text(errors="replace")),
64+
}
65+
)
66+
67+
# Warnings or corruption always go in
68+
if fname.endswith(".warning"):
69+
entries.append(
70+
{
71+
"type": "warning",
72+
"file": fname,
73+
"content": clean_lines(file.read_text(errors="replace")),
74+
}
75+
)
76+
elif fname.endswith(".corruption"):
77+
entries.append(
78+
{
79+
"type": "corruption",
80+
"file": fname,
81+
"content": clean_lines(file.read_text(errors="replace")),
82+
}
83+
)
84+
85+
return entries
86+
87+
88+
def generate_commit_log():
89+
print("# Kernel crash report summary\n")
90+
for host_dir in sorted(CRASH_DIR.iterdir()):
91+
if not host_dir.is_dir():
92+
continue
93+
logs = collect_host_logs(host_dir)
94+
if not logs:
95+
continue
96+
print(f"## Host: {host_dir.name}\n")
97+
for entry in logs:
98+
tag = entry["type"].upper()
99+
print(f"### [{tag}] {entry['file']}")
100+
print("```")
101+
print(entry["content"])
102+
print("```\n")
103+
104+
105+
if __name__ == "__main__":
106+
if not CRASH_DIR.exists():
107+
print(f"No crashes, filesystem corruption isues, or kernel warnings were detected on this run.")
108+
exit(0)
109+
generate_commit_log()

0 commit comments

Comments
 (0)