[analyze_memory] Attribute main.cpp setup()/loop() to esphome core (#16033)

This commit is contained in:
J. Nick Koston
2026-04-28 21:06:54 -05:00
committed by GitHub
parent c3bd38af77
commit 592486ae9a
2 changed files with 56 additions and 6 deletions
+13 -6
View File
@@ -793,8 +793,11 @@ class MemoryAnalyzer:
"""Scan ESPHome source object files to map extern "C" symbols to components.
When no linker map file is available, this uses ``nm`` to scan ``.o`` files
under ``src/esphome/`` and build a symbol-to-component mapping. This catches
``extern "C"`` functions and other symbols that lack C++ namespace prefixes.
under ``src/`` (including ``src/main.cpp.o`` and everything beneath
``src/esphome/``) and build a symbol-to-component mapping. This catches
``extern "C"`` functions, the ESPHome-generated ``setup()``/``loop()``
entry points in ``main.cpp``, and other symbols that lack C++ namespace
prefixes.
Skips scanning if ``_source_symbol_map`` was already populated by
``_parse_map_file()``.
@@ -806,12 +809,12 @@ class MemoryAnalyzer:
if obj_dir is None:
return
# Find ESPHome source object files
esphome_src_dir = obj_dir / "src" / "esphome"
if not esphome_src_dir.is_dir():
# Scan all ESPHome-owned source object files: src/main.cpp.o and src/esphome/...
src_dir = obj_dir / "src"
if not src_dir.is_dir():
return
obj_files = sorted(esphome_src_dir.rglob("*.o"))
obj_files = sorted(src_dir.rglob("*.o"))
if not obj_files:
return
@@ -1064,6 +1067,10 @@ class MemoryAnalyzer:
if component_name in self.external_components:
return f"{_COMPONENT_PREFIX_EXTERNAL}{component_name}"
# ESPHome-generated entry point: src/main.cpp.o (contains setup()/loop())
if len(parts) >= 2 and parts[-2:] == ("src", "main.cpp.o"):
return _COMPONENT_CORE
# ESPHome core: src/esphome/core/... or src/esphome/...
if "core" in parts and "esphome" in parts:
return _COMPONENT_CORE
@@ -0,0 +1,43 @@
"""Tests for source-file-to-component attribution in memory analyzer."""
from unittest.mock import patch
from esphome.analyze_memory import MemoryAnalyzer
def _make_analyzer(external_components: set[str] | None = None) -> MemoryAnalyzer:
"""Create a MemoryAnalyzer with mocked dependencies."""
with patch.object(MemoryAnalyzer, "__init__", lambda self, *a, **kw: None):
analyzer = MemoryAnalyzer.__new__(MemoryAnalyzer)
analyzer.external_components = external_components or set()
analyzer._lib_hash_to_name = {}
return analyzer
def test_source_file_to_component_main_cpp_relative() -> None:
"""ESPHome-generated src/main.cpp.o (nm path form) attributes to core."""
analyzer = _make_analyzer()
assert analyzer._source_file_to_component("src/main.cpp.o") == "[esphome]core"
def test_source_file_to_component_main_cpp_pioenvs_path() -> None:
"""Linker map paths like .pioenvs/<env>/src/main.cpp.o attribute to core."""
analyzer = _make_analyzer()
result = analyzer._source_file_to_component(".pioenvs/drivewaygate/src/main.cpp.o")
assert result == "[esphome]core"
def test_source_file_to_component_esphome_core() -> None:
"""Sources under src/esphome/core/ attribute to core."""
analyzer = _make_analyzer()
result = analyzer._source_file_to_component("src/esphome/core/application.cpp.o")
assert result == "[esphome]core"
def test_source_file_to_component_known_component() -> None:
"""Known ESPHome components attribute to their component name."""
analyzer = _make_analyzer()
result = analyzer._source_file_to_component(
"src/esphome/components/wifi/wifi_component.cpp.o"
)
assert result == "[esphome]wifi"