mirror of
https://github.com/esphome/esphome.git
synced 2026-08-26 16:10:29 +00:00
Toolchain-suffixed idedata cache, surfaced idedata failures, and per-dump decode warnings
This commit is contained in:
@@ -107,7 +107,11 @@ def run_compile(config: ConfigType, verbose: bool) -> int:
|
||||
return rc
|
||||
|
||||
_print_size_summary(build_dir)
|
||||
get_idedata()
|
||||
if get_idedata() is None:
|
||||
_LOGGER.warning(
|
||||
"Could not generate idedata from %s",
|
||||
build_dir / "compile_commands.json",
|
||||
)
|
||||
return 0
|
||||
|
||||
|
||||
@@ -218,7 +222,9 @@ def get_idedata() -> dict | None:
|
||||
return load_or_build_idedata(
|
||||
get_build_dir() / "compile_commands.json",
|
||||
get_elf_path(),
|
||||
CORE.relative_internal_path("idedata", f"{CORE.name}.json"),
|
||||
# Suffixed so a platformio->arduino->platformio round trip on one
|
||||
# config never serves the other toolchain's cache shape
|
||||
CORE.relative_internal_path("idedata", f"{CORE.name}.arduino.json"),
|
||||
# The compile DB's commands carry the same ccache prefix the ninja
|
||||
# rules were generated with
|
||||
launcher=str(ccache) if ccache else None,
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import functools
|
||||
import logging
|
||||
import math
|
||||
from pathlib import Path
|
||||
import platform
|
||||
import re
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
@@ -603,10 +604,21 @@ ESP8266_EXCEPTION_CODES = {
|
||||
}
|
||||
|
||||
|
||||
@functools.cache
|
||||
def _warn_missing_decode_tool(path: str) -> None:
|
||||
# Cached so a stack dump of dozens of addresses warns once, not per line
|
||||
_LOGGER.warning("Cannot decode crash addresses: %s missing", path)
|
||||
_DECODE_WARNED_AT: dict[str, float] = {}
|
||||
|
||||
|
||||
def _warn_decode_problem(key: str, message: str, *args) -> None:
|
||||
"""Warn, deduplicated per stack dump but not per process.
|
||||
|
||||
A dump decodes dozens of addresses in a burst; one warning per burst is
|
||||
enough. A long-running dashboard must still warn on the next dump, so
|
||||
the suppression expires instead of living for the process lifetime.
|
||||
"""
|
||||
now = time.monotonic()
|
||||
if now - _DECODE_WARNED_AT.get(key, -math.inf) < 30:
|
||||
return
|
||||
_DECODE_WARNED_AT[key] = now
|
||||
_LOGGER.warning(message, *args)
|
||||
|
||||
|
||||
def _decode_pc(config, addr):
|
||||
@@ -617,7 +629,9 @@ def _decode_pc(config, addr):
|
||||
elf = native_toolchain.get_elf_path()
|
||||
missing = addr2line if not addr2line.is_file() else elf
|
||||
if not missing.is_file():
|
||||
_warn_missing_decode_tool(str(missing))
|
||||
_warn_decode_problem(
|
||||
str(missing), "Cannot decode crash addresses: %s missing", missing
|
||||
)
|
||||
return
|
||||
addr2line, elf = str(addr2line), str(elf)
|
||||
else:
|
||||
@@ -632,6 +646,12 @@ def _decode_pc(config, addr):
|
||||
try:
|
||||
translation = subprocess.check_output(command, close_fds=False).decode().strip()
|
||||
except Exception: # noqa: BLE001 # pylint: disable=broad-except
|
||||
if CORE.using_toolchain_arduino:
|
||||
# A present-but-failing addr2line (stale ELF, bad install) must
|
||||
# be visible, matching the missing-tool warning above
|
||||
_warn_decode_problem(
|
||||
"addr2line-failed", "Could not decode crash address %s", addr
|
||||
)
|
||||
_LOGGER.debug("Caught exception for command %s", command, exc_info=1)
|
||||
return
|
||||
|
||||
|
||||
@@ -122,7 +122,7 @@ def test_decode_pc_native_missing_tools_warns_once(
|
||||
"""A stack dump of many addresses produces one missing-tool warning."""
|
||||
from esphome.components import esp8266
|
||||
|
||||
esp8266._warn_missing_decode_tool.cache_clear()
|
||||
esp8266._DECODE_WARNED_AT.clear()
|
||||
with (
|
||||
patch(
|
||||
"esphome.arduino8266.toolchain.get_addr2line_path",
|
||||
@@ -136,4 +136,13 @@ def test_decode_pc_native_missing_tools_warns_once(
|
||||
esp8266._decode_pc({}, "40201234")
|
||||
esp8266._decode_pc({}, "40201238")
|
||||
assert caplog.text.count("Cannot decode crash addresses") == 1
|
||||
esp8266._warn_missing_decode_tool.cache_clear()
|
||||
esp8266._DECODE_WARNED_AT.clear()
|
||||
|
||||
|
||||
def test_resolve_toolchain_rejects_unsupported() -> None:
|
||||
"""ESP8266 rejects a CLI toolchain it cannot serve, like every platform."""
|
||||
from esphome.components.esp8266 import _resolve_toolchain
|
||||
|
||||
CORE.toolchain = Toolchain.SDK_NRF
|
||||
with pytest.raises(cv.Invalid, match="Unsupported toolchain 'sdk-nrf'"):
|
||||
_resolve_toolchain({})
|
||||
|
||||
@@ -212,7 +212,7 @@ def test_get_idedata_delegates(tmp_path: Path) -> None:
|
||||
compile_commands, elf, cache = mock_load.call_args[0]
|
||||
assert compile_commands.name == "compile_commands.json"
|
||||
assert elf.name == "firmware.elf"
|
||||
assert cache.name == "test8266.json"
|
||||
assert cache.name == "test8266.arduino.json"
|
||||
# The exact configured launcher is passed for compile DB parsing
|
||||
assert mock_load.call_args.kwargs["launcher"] == str(Path("/cc/ccache"))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user