mirror of
https://github.com/esphome/esphome.git
synced 2025-10-25 13:13:48 +01:00
merge
This commit is contained in:
@@ -316,6 +316,7 @@ def main():
|
||||
print(
|
||||
"Usage: python -m esphome.analyze_memory <elf_file> [objdump_path] [readelf_path]"
|
||||
)
|
||||
print("\nIf objdump/readelf paths are not provided, you must specify them.")
|
||||
print("\nExample for ESP8266:")
|
||||
print(" python -m esphome.analyze_memory firmware.elf \\")
|
||||
print(
|
||||
@@ -332,6 +333,14 @@ def main():
|
||||
print(
|
||||
" ~/.platformio/packages/toolchain-xtensa-esp-elf/bin/xtensa-esp32-elf-readelf"
|
||||
)
|
||||
print("\nExample for ESP32-C3 (RISC-V):")
|
||||
print(" python -m esphome.analyze_memory firmware.elf \\")
|
||||
print(
|
||||
" ~/.platformio/packages/toolchain-riscv32-esp/bin/riscv32-esp-elf-objdump \\"
|
||||
)
|
||||
print(
|
||||
" ~/.platformio/packages/toolchain-riscv32-esp/bin/riscv32-esp-elf-readelf"
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
elf_file = sys.argv[1]
|
||||
|
||||
@@ -145,7 +145,16 @@ def run_compile(config, verbose):
|
||||
args = []
|
||||
if CONF_COMPILE_PROCESS_LIMIT in config[CONF_ESPHOME]:
|
||||
args += [f"-j{config[CONF_ESPHOME][CONF_COMPILE_PROCESS_LIMIT]}"]
|
||||
return run_platformio_cli_run(config, verbose, *args)
|
||||
result = run_platformio_cli_run(config, verbose, *args)
|
||||
|
||||
# Run memory analysis if enabled
|
||||
if config.get(CONF_ESPHOME, {}).get("analyze_memory", False):
|
||||
try:
|
||||
analyze_memory_usage(config)
|
||||
except Exception as e:
|
||||
_LOGGER.warning("Failed to analyze memory usage: %s", e)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def _run_idedata(config):
|
||||
@@ -374,3 +383,93 @@ class IDEData:
|
||||
return f"{self.cc_path[:-7]}addr2line.exe"
|
||||
|
||||
return f"{self.cc_path[:-3]}addr2line"
|
||||
|
||||
@property
|
||||
def objdump_path(self) -> str:
|
||||
# replace gcc at end with objdump
|
||||
|
||||
# Windows
|
||||
if self.cc_path.endswith(".exe"):
|
||||
return f"{self.cc_path[:-7]}objdump.exe"
|
||||
|
||||
return f"{self.cc_path[:-3]}objdump"
|
||||
|
||||
@property
|
||||
def readelf_path(self) -> str:
|
||||
# replace gcc at end with readelf
|
||||
|
||||
# Windows
|
||||
if self.cc_path.endswith(".exe"):
|
||||
return f"{self.cc_path[:-7]}readelf.exe"
|
||||
|
||||
return f"{self.cc_path[:-3]}readelf"
|
||||
|
||||
|
||||
def analyze_memory_usage(config: dict[str, Any]) -> None:
|
||||
"""Analyze memory usage by component after compilation."""
|
||||
# Lazy import to avoid overhead when not needed
|
||||
from esphome.analyze_memory import MemoryAnalyzer
|
||||
|
||||
idedata = get_idedata(config)
|
||||
|
||||
# Get paths to tools
|
||||
elf_path = idedata.firmware_elf_path
|
||||
objdump_path = idedata.objdump_path
|
||||
readelf_path = idedata.readelf_path
|
||||
|
||||
# Debug logging
|
||||
_LOGGER.debug("ELF path from idedata: %s", elf_path)
|
||||
|
||||
# Check if file exists
|
||||
if not Path(elf_path).exists():
|
||||
# Try alternate path
|
||||
alt_path = Path(CORE.relative_build_path(".pioenvs", CORE.name, "firmware.elf"))
|
||||
if alt_path.exists():
|
||||
elf_path = str(alt_path)
|
||||
_LOGGER.debug("Using alternate ELF path: %s", elf_path)
|
||||
else:
|
||||
_LOGGER.warning("ELF file not found at %s or %s", elf_path, alt_path)
|
||||
return
|
||||
|
||||
# Extract external components from config
|
||||
external_components = set()
|
||||
|
||||
# Get the list of built-in ESPHome components
|
||||
from esphome.analyze_memory import get_esphome_components
|
||||
|
||||
builtin_components = get_esphome_components()
|
||||
|
||||
# Special non-component keys that appear in configs
|
||||
NON_COMPONENT_KEYS = {
|
||||
CONF_ESPHOME,
|
||||
"substitutions",
|
||||
"packages",
|
||||
"globals",
|
||||
"<<",
|
||||
}
|
||||
|
||||
# Check all top-level keys in config
|
||||
for key in config:
|
||||
if key not in builtin_components and key not in NON_COMPONENT_KEYS:
|
||||
# This is an external component
|
||||
external_components.add(key)
|
||||
|
||||
_LOGGER.debug("Detected external components: %s", external_components)
|
||||
|
||||
# Create analyzer and run analysis
|
||||
analyzer = MemoryAnalyzer(elf_path, objdump_path, readelf_path, external_components)
|
||||
analyzer.analyze()
|
||||
|
||||
# Generate and print report
|
||||
report = analyzer.generate_report()
|
||||
_LOGGER.info("\n%s", report)
|
||||
|
||||
# Optionally save to file
|
||||
if config.get(CONF_ESPHOME, {}).get("memory_report_file"):
|
||||
report_file = Path(config[CONF_ESPHOME]["memory_report_file"])
|
||||
if report_file.suffix == ".json":
|
||||
report_file.write_text(analyzer.to_json())
|
||||
_LOGGER.info("Memory report saved to %s", report_file)
|
||||
else:
|
||||
report_file.write_text(report)
|
||||
_LOGGER.info("Memory report saved to %s", report_file)
|
||||
|
||||
Reference in New Issue
Block a user