Demote bulk stack-word decode misses to debug, count built components per pattern

This commit is contained in:
J. Nick Koston
2026-08-25 08:37:33 -05:00
parent 2ee448a7a8
commit a0dcb6a5f5
2 changed files with 16 additions and 10 deletions
+9 -5
View File
@@ -644,7 +644,10 @@ def _warn_decode_problem(key: str, message: str, *args) -> bool:
return True
def _decode_pc(config: ConfigType, addr: str) -> None:
def _decode_pc(config: ConfigType, addr: str, *, bulk: bool = False) -> None:
"""Decode one crash address. ``bulk``: the caller is scanning every
8-hex stack word, most of which are not code addresses -- unmappable
ones log at debug so real frames are not buried."""
if (native_toolchain := native_toolchain_module()) is not None:
addr2line = native_toolchain.get_addr2line_path()
elf = native_toolchain.get_elf_path()
@@ -686,9 +689,10 @@ def _decode_pc(config: ConfigType, addr: str) -> None:
return
if "?? ??:0" in translation:
# A stale or mismatched ELF: mark it, or the frame silently reads
# as merely unmappable
_LOGGER.warning("Not decoded %s (address not in %s)", addr, elf)
# A named register that fails to decode is confusing silence; a
# bulk stack word failing is the expected common case
log = _LOGGER.debug if bulk else _LOGGER.warning
log("Not decoded %s (address not in %s)", addr, elf)
return
translation = translation.replace(" at ??:?", "").replace(":?", "")
_LOGGER.warning("Decoded %s", translation)
@@ -758,6 +762,6 @@ def process_stacktrace(config: ConfigType, line: str, backtrace_state: bool) ->
if backtrace_state:
for addr in re.finditer(STACKTRACE_ESP8266_BACKTRACE_PC_RE, line):
_decode_pc(config, addr.group())
_decode_pc(config, addr.group(), bulk=True)
return backtrace_state
+7 -5
View File
@@ -1062,10 +1062,10 @@ def test_components(
# toolchain build.
include_validate = esphome_command != "compile"
# Find all component tests; remember which patterns (wildcards
# included) matched anything, for the deferred no-tests accounting
# Find all component tests; remember which components each pattern
# (wildcards included) matched, for the deferred no-tests accounting
all_tests = {}
pattern_hits: dict[str, bool] = {}
pattern_components: dict[str, set[str]] = {}
for pattern in component_patterns:
# Skip empty patterns (happens when components list is empty string)
if not pattern:
@@ -1073,7 +1073,7 @@ def test_components(
found = find_component_tests(
tests_dir, pattern, base_only, include_validate=include_validate
)
pattern_hits[pattern] = bool(found)
pattern_components[pattern] = set(found)
all_tests.update(found)
# The flag's contract is "no test matched fails": a fully blank pattern
@@ -1203,10 +1203,12 @@ def test_components(
# legitimately match nothing. Failing is deferred past the summary
# so a real failure's reproduce commands still print.
built = {c for r in test_results for c in r.components}
# A pattern is silent when it matched no fixture, or when none of
# its matched components produced a build (wildcards included)
silent = [
p
for p in component_patterns
if p and (not pattern_hits.get(p) or ("*" not in p and p not in built))
if p and not (pattern_components.get(p, set()) & built)
]
if silent:
print(f"No tests ran for requested pattern(s): {', '.join(silent)}")