diff --git a/builder/frameworks/espidf.py b/builder/frameworks/espidf.py index 1cc9adef4..74fb88074 100644 --- a/builder/frameworks/espidf.py +++ b/builder/frameworks/espidf.py @@ -1679,6 +1679,7 @@ def _get_installed_uv_packages(python_exe_path): # https://github.com/platformio/platform-espressif32/issues/635 "cryptography": "~=44.0.0", "pyparsing": ">=3.1.0,<4", + "pydantic": "~=2.11.10", "idf-component-manager": "~=2.2", "esp-idf-kconfig": "~=2.5.0" } diff --git a/builder/main.py b/builder/main.py index cc4fc5f28..f9f8a3694 100644 --- a/builder/main.py +++ b/builder/main.py @@ -650,6 +650,132 @@ def firmware_metrics(target, source, env): print(f'Make sure esp-idf-size is installed: uv pip install --python "{PYTHON_EXE}" esp-idf-size') +def coredump_analysis(target, source, env): + """ + Custom target to run esp-coredump with support for command line parameters. + Usage: pio run -t coredump -- [esp-coredump arguments] + + Args: + target: SCons target + source: SCons source + env: SCons environment object + """ + if terminal_cp != "utf-8": + print("Coredump analysis can not be shown. Set the terminal codepage to \"utf-8\"") + return + + elf_file = str(Path(env.subst("$BUILD_DIR")) / (env.subst("$PROGNAME") + ".elf")) + if not Path(elf_file).is_file(): + # elf file can be in project dir + elf_file = str(Path(get_project_dir()) / (env.subst("$PROGNAME") + ".elf")) + + if not Path(elf_file).is_file(): + print(f"Error: ELF file not found: {elf_file}") + print("Make sure the project is built first with 'pio run'") + return + + try: + cmd = [PYTHON_EXE, "-m", "esp_coredump"] + + # Command Line Parameter, after -- + cli_args = [] + if "--" in sys.argv: + dash_index = sys.argv.index("--") + if dash_index + 1 < len(sys.argv): + cli_args = sys.argv[dash_index + 1:] + + # Add CLI arguments or use defaults + if cli_args: + cmd.extend(cli_args) + # ELF file should be at the end as positional argument + if not any(arg.endswith('.elf') for arg in cli_args): + cmd.append(elf_file) + else: + # Default arguments if none provided + # Parameters from platformio.ini + extra_args = env.GetProjectOption("custom_esp_coredump_args", "") + if extra_args: + args = shlex.split(extra_args) + cmd.extend(args) + # Ensure ELF is last positional if not present + if not any(a.endswith(".elf") for a in args): + cmd.append(elf_file) + else: + # Prefer an explicit core file if configured or present; else read from flash + core_file = env.GetProjectOption("custom_esp_coredump_corefile", "") + if not core_file: + for name in ("coredump.bin", "coredump.b64"): + cand = Path(get_project_dir()) / name + if cand.is_file(): + core_file = str(cand) + break + + # Global options + cmd.extend(["--chip", mcu]) + upload_port = env.subst("$UPLOAD_PORT") + if upload_port: + cmd.extend(["--port", upload_port]) + + # Subcommand and arguments + cmd.append("info_corefile") + if core_file: + cmd.extend(["--core", core_file]) + if core_file.lower().endswith(".b64"): + cmd.extend(["--core-format", "b64"]) + # ELF is the required positional + cmd.append(elf_file) + + # Set up ESP-IDF environment variables and ensure required packages are installed + coredump_env = os.environ.copy() + + # Check if ESP-IDF packages are available, install if missing + _framework_pkg_dir = platform.get_package_dir("framework-espidf") + _rom_elfs_dir = platform.get_package_dir("tool-esp-rom-elfs") + + # Install framework-espidf if not available + if not _framework_pkg_dir or not os.path.isdir(_framework_pkg_dir): + print("ESP-IDF framework not found, installing...") + try: + platform.install_package("framework-espidf") + _framework_pkg_dir = platform.get_package_dir("framework-espidf") + except Exception as e: + print(f"Warning: Failed to install framework-espidf: {e}") + + # Install tool-esp-rom-elfs if not available + if not _rom_elfs_dir or not os.path.isdir(_rom_elfs_dir): + print("ESP ROM ELFs tool not found, installing...") + try: + platform.install_package("tool-esp-rom-elfs") + _rom_elfs_dir = platform.get_package_dir("tool-esp-rom-elfs") + except Exception as e: + print(f"Warning: Failed to install tool-esp-rom-elfs: {e}") + + # Set environment variables if packages are available + if _framework_pkg_dir and os.path.isdir(_framework_pkg_dir): + coredump_env['IDF_PATH'] = str(Path(_framework_pkg_dir).resolve()) + if _rom_elfs_dir and os.path.isdir(_rom_elfs_dir): + coredump_env['ESP_ROM_ELF_DIR'] = str(Path(_rom_elfs_dir).resolve()) + + # Debug-Info if wanted + if env.GetProjectOption("custom_esp_coredump_verbose", False): + print(f"Running command: {' '.join(cmd)}") + if 'IDF_PATH' in coredump_env: + print(f"IDF_PATH: {coredump_env['IDF_PATH']}") + print(f"ESP_ROM_ELF_DIR: {coredump_env.get('ESP_ROM_ELF_DIR', 'Not set')}") + + # Execute esp-coredump with ESP-IDF environment + result = subprocess.run(cmd, check=False, capture_output=False, env=coredump_env) + + if result.returncode != 0: + print(f"Warning: esp-coredump exited with code {result.returncode}") + + except FileNotFoundError: + print("Error: Python executable not found.") + print("Check your Python installation.") + except Exception as e: + print(f"Error: Failed to run coredump analysis: {e}") + print(f'Make sure esp-coredump is installed: uv pip install --python "{PYTHON_EXE}" esp-coredump') + # # Target: Build executable and linkable firmware or FS image # @@ -931,6 +1057,27 @@ def firmware_metrics(target, source, env): always_build=True, ) +# Register Custom Target for coredump analysis +env.AddCustomTarget( + name="coredump", + dependencies="$BUILD_DIR/${PROGNAME}.elf", + actions=coredump_analysis, + title="Coredump Analysis", + description="Analyze coredumps using esp-coredump " + "(supports CLI args after --)", + always_build=True, +) + +# Additional Target without Build-Dependency when already compiled +env.AddCustomTarget( + name="coredump-only", + dependencies=None, + actions=coredump_analysis, + title="Coredump Analysis (No Build)", + description="Analyze coredumps without building first", + always_build=True, +) + # Override memory inspection behavior env.SConscript("sizedata.py", exports="env") diff --git a/builder/penv_setup.py b/builder/penv_setup.py index 8477035e9..7196f1217 100644 --- a/builder/penv_setup.py +++ b/builder/penv_setup.py @@ -55,7 +55,8 @@ "ecdsa": ">=0.19.1", "bitstring": ">=4.3.1", "reedsolo": ">=1.5.3,<1.8", - "esp-idf-size": ">=2.0.0" + "esp-idf-size": ">=2.0.0", + "esp-coredump": ">=1.14.0" }