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
45 changes: 37 additions & 8 deletions logspec/errors/kbuild.py
Original file line number Diff line number Diff line change
Expand Up @@ -492,9 +492,11 @@ def _parse(self, text):
"""Parses a log fragment looking for a generic Kbuild error
and updates the object with the extracted information.

Strategy: if a target was specified, search for errors _after_
the first appearance of the `target' string in the log. To
search for these errors, look for unindented lines.
Strategy: if a target was specified, search for errors at or
after the first appearance of the `target' string in the log.
When text follows the target on the same line, keep that entire
line so diagnostics containing the target are not truncated.
To search for these errors, look for unindented lines.

Parameters:
text (str): the text log containing the modpost error
Expand All @@ -505,11 +507,28 @@ def _parse(self, text):
self.error_type = "kbuild.other"
end = 0
if self.target:
match = re.search(self.target, text)
if not match:
target_match = re.search(re.escape(self.target), text)
if not target_match:
return end

line_start = text.rfind('\n', 0, target_match.start()) + 1
line_end = text.find('\n', target_match.end())
if line_end == -1:
line_end = len(text)

# A diagnostic can contain the target followed by the actual
# failure, for example: "cp: cannot create '<target>': File
# exists". Start at the beginning of that line instead of
# returning only the text after the target. If the target ends
# the line (as it commonly does in a command), start at the next
# line and retain the existing command-exclusion behaviour.
trailing_text = text[target_match.end():line_end].strip()
search_start = (
line_start if trailing_text else min(line_end + 1, len(text))
)

summary_strings = []
match = re.finditer(r'^[^\s]+.*$', text[match.end():], flags=re.MULTILINE)
match = re.finditer(r'^[^\s]+.*$', text[search_start:], flags=re.MULTILINE)
for m in match:
current_match = m.group()
self._report += f"{current_match}\n"
Expand All @@ -523,7 +542,7 @@ def _parse(self, text):
generic_error_match = re.search(fr'{TIMESTAMP}(.*error:.*)', current_match)
if generic_error_match:
summary_strings.append(generic_error_match.group(1))
end = m.end()
end = search_start + m.end()
if summary_strings:
self.error_summary = " ".join([string for string in summary_strings if string])
return end
Expand Down Expand Up @@ -629,7 +648,11 @@ def find_kbuild_error(text):
None if no error report was found.
"""
end = 0
match = re.search(r'make.*?: \*\*\* (?P<error_str>.*)', text)
match = re.search(
fr'^{TIMESTAMP}make.*?: \*\*\* (?P<error_str>.*)',
text,
flags=re.MULTILINE,
)
if not match:
return None
error_str = match.group('error_str')
Expand Down Expand Up @@ -658,6 +681,12 @@ def find_kbuild_error(text):
# Catch-all condition for non-specific errors
error = KbuildGenericError(script=script, target=target)
error.parse(error_text)
if isinstance(error, KbuildGenericError) and not error._report:
# Some generic failures have no preceding diagnostic or target
# occurrence. The Make failure is still useful context and must
# not result in an empty report body.
line_start = text.rfind('\n', 0, start) + 1
error._report = f"{text[line_start:end]}\n"
else:
# Unrecognized error, these are marked as unknown and not parsed
error = KbuildUnknownError(error_str)
Expand Down
7 changes: 7 additions & 0 deletions tests/logs/index.txt
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,13 @@
- kbuild_024.log: Device Tree Compiler fatal file error
FATAL ERROR: Couldn't open "arch/arm64/boot/dts/qcom/missing.dtsi": No such file or directory

- kbuild_025.log: Generic generated-header failure without a diagnostic
make[5]: *** [/tmp/kci/linux/arch/x86/Makefile:269: arch/x86/include/generated/asm/cpufeaturemasks.h] Error 1

- kbuild_026.log: Generic diagnostic containing the target path
cp: cannot create regular file '/tmp/kci/artifacts/build/kselftest/arm64/signal/fake_sigreturn_bad_magic': File exists



./linux_boot

Expand Down
3 changes: 3 additions & 0 deletions tests/logs/kbuild/kbuild_025.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
00:00:15 make --silent --keep-going --jobs=8 O=/tmp/kci/artifacts/build ARCH=x86_64 HOSTCC=clang CC=clang
00:13:13 make[5]: *** [/tmp/kci/linux/arch/x86/Makefile:269: arch/x86/include/generated/asm/cpufeaturemasks.h] Error 1
00:13:13 make[5]: Target 'prepare' not remade because of errors.
5 changes: 5 additions & 0 deletions tests/logs/kbuild/kbuild_026.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
04:41:39 CC acct_syscall
04:41:39 CC tags_test
04:41:43 cp: cannot create regular file '/tmp/kci/artifacts/build/kselftest/arm64/signal/fake_sigreturn_bad_magic': File exists
04:41:43 make[2]: *** [Makefile:21: /tmp/kci/artifacts/build/kselftest/arm64/signal/fake_sigreturn_bad_magic] Error 1
04:41:43 make[2]: Target 'all' not remade because of errors.
51 changes: 51 additions & 0 deletions tests/test_kbuild.py
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,32 @@
"target": "arch/arm64/boot/dts/qcom/example.dtb"
}
]
}),

# Generic generated-header failure without an earlier target occurrence.
('kbuild_025.log',
'kbuild',
{
"errors": [
{
"error_type": "kbuild.other",
"script": "/tmp/kci/linux/arch/x86/Makefile:269",
"target": "arch/x86/include/generated/asm/cpufeaturemasks.h"
}
]
}),

# Generic diagnostic containing the complete target path.
('kbuild_026.log',
'kbuild',
{
"errors": [
{
"error_type": "kbuild.other",
"script": "Makefile:21",
"target": "/tmp/kci/artifacts/build/kselftest/arm64/signal/fake_sigreturn_bad_magic"
}
]
})
])
def test_kbuild(log_file, parser_id, expected):
Expand Down Expand Up @@ -604,3 +630,28 @@ def test_kbuild_dtc_check_report():
"288.8-296.3\n"
"ERROR: Input tree has errors, aborting (use -f to force output)\n"
)


def test_kbuild_generic_make_fallback_report():
log_file = os.path.join(LOG_DIR, 'kbuild_025.log')
parsed_data = load_parser_and_parse_log(
log_file, 'kbuild', tests.setup.PARSER_DEFS_FILE
)

assert parsed_data['errors'][0]._report == (
"00:13:13 make[5]: *** [/tmp/kci/linux/arch/x86/Makefile:269: "
"arch/x86/include/generated/asm/cpufeaturemasks.h] Error 1\n"
)


def test_kbuild_generic_preserves_diagnostic_line():
log_file = os.path.join(LOG_DIR, 'kbuild_026.log')
parsed_data = load_parser_and_parse_log(
log_file, 'kbuild', tests.setup.PARSER_DEFS_FILE
)

assert parsed_data['errors'][0]._report == (
"04:41:43 cp: cannot create regular file "
"'/tmp/kci/artifacts/build/kselftest/arm64/signal/"
"fake_sigreturn_bad_magic': File exists\n"
)
Loading