test(panic_handler): Added unit test to verify panic handler can halt

This test verifies that the panic handler can indeed halt when
configured to print and halt instead of rebboting.
This commit is contained in:
Sudeep Mohanty
2025-08-13 12:17:35 +02:00
parent 36eaa2c4a1
commit f1ab53eda0
5 changed files with 38 additions and 11 deletions

View File

@@ -83,6 +83,10 @@ void test_coredump_summary(void);
void test_panic_print_backtrace(void); void test_panic_print_backtrace(void);
#if CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT
void test_panic_halt(void);
#endif /* CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT */
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -126,6 +126,9 @@ void app_main(void)
HANDLE_TEST(test_name, test_tcb_corrupted); HANDLE_TEST(test_name, test_tcb_corrupted);
HANDLE_TEST(test_name, test_panic_handler_stuck0); HANDLE_TEST(test_name, test_panic_handler_stuck0);
HANDLE_TEST(test_name, test_panic_handler_crash0); HANDLE_TEST(test_name, test_panic_handler_crash0);
#if CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT
HANDLE_TEST(test_name, test_panic_halt);
#endif /* CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT */
#if CONFIG_ESP_SYSTEM_USE_FRAME_POINTER #if CONFIG_ESP_SYSTEM_USE_FRAME_POINTER
HANDLE_TEST(test_name, test_panic_print_backtrace); HANDLE_TEST(test_name, test_panic_print_backtrace);
#endif #endif

View File

@@ -445,3 +445,12 @@ void test_panic_print_backtrace(void)
} }
#endif #endif
#if CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT
void test_panic_halt(void)
{
printf("Triggering panic. Device should print 'CPU halted.' and stop.\n");
fflush(stdout);
assert(0);
}
#endif /* CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT */

View File

@@ -2,12 +2,9 @@
# SPDX-License-Identifier: CC0-1.0 # SPDX-License-Identifier: CC0-1.0
import itertools import itertools
import re import re
from collections.abc import Sequence
from re import Pattern
from typing import Any from typing import Any
from typing import List
from typing import Optional
from typing import Pattern
from typing import Sequence
from typing import Union
import pexpect import pexpect
import pytest import pytest
@@ -48,6 +45,7 @@ CONFIGS = list(
CONFIG_PANIC = list(itertools.chain(itertools.product(['panic'], ['supported_targets']))) CONFIG_PANIC = list(itertools.chain(itertools.product(['panic'], ['supported_targets'])))
CONFIG_PANIC_DUAL_CORE = list(itertools.chain(itertools.product(['panic'], TARGETS_DUAL_CORE))) CONFIG_PANIC_DUAL_CORE = list(itertools.chain(itertools.product(['panic'], TARGETS_DUAL_CORE)))
CONFIG_PANIC_HALT = list(itertools.chain(itertools.product(['panic_halt'], TARGETS_ALL)))
CONFIGS_BACKTRACE = list( CONFIGS_BACKTRACE = list(
itertools.chain( itertools.chain(
@@ -105,11 +103,11 @@ CONFIG_COREDUMP_SUMMARY_FLASH_ENCRYPTED = list(
PANIC_ABORT_PREFIX = 'Panic reason: ' PANIC_ABORT_PREFIX = 'Panic reason: '
def get_default_backtrace(config: str) -> List[str]: def get_default_backtrace(config: str) -> list[str]:
return [config, 'app_main', 'main_task', 'vPortTaskWrapper'] return [config, 'app_main', 'main_task', 'vPortTaskWrapper']
def expect_coredump_flash_write_logs(dut: PanicTestDut, config: str, check_cpu_reset: Optional[bool] = True) -> None: def expect_coredump_flash_write_logs(dut: PanicTestDut, config: str, check_cpu_reset: bool | None = True) -> None:
dut.expect_exact('Save core dump to flash...') dut.expect_exact('Save core dump to flash...')
if 'extram_stack' in config: if 'extram_stack' in config:
dut.expect_exact('Backing up stack @') dut.expect_exact('Backing up stack @')
@@ -120,7 +118,7 @@ def expect_coredump_flash_write_logs(dut: PanicTestDut, config: str, check_cpu_r
dut.expect_cpu_reset() dut.expect_cpu_reset()
def expect_coredump_uart_write_logs(dut: PanicTestDut, check_cpu_reset: Optional[bool] = True) -> Any: def expect_coredump_uart_write_logs(dut: PanicTestDut, check_cpu_reset: bool | None = True) -> Any:
# ================= CORE DUMP START ================= # ================= CORE DUMP START =================
# B8AAAMAEgAGAAAAXAEAAAAAAABkAAAA # B8AAAMAEgAGAAAAXAEAAAAAAABkAAAA
# ... # ...
@@ -144,9 +142,9 @@ def expect_coredump_uart_write_logs(dut: PanicTestDut, check_cpu_reset: Optional
def common_test( def common_test(
dut: PanicTestDut, dut: PanicTestDut,
config: str, config: str,
expected_backtrace: Optional[List[str]] = None, expected_backtrace: list[str] | None = None,
check_cpu_reset: Optional[bool] = True, check_cpu_reset: bool | None = True,
expected_coredump: Optional[Sequence[Union[str, Pattern[Any]]]] = None, expected_coredump: Sequence[str | Pattern[Any]] | None = None,
) -> None: ) -> None:
if 'gdbstub' in config: if 'gdbstub' in config:
if 'coredump' in config: if 'coredump' in config:
@@ -1245,3 +1243,11 @@ def test_panic_print_backtrace(dut: PanicTestDut, config: str, test_func_name: s
coredump_pattern = re.compile(PANIC_ABORT_PREFIX + regex_pattern.decode('utf-8')) coredump_pattern = re.compile(PANIC_ABORT_PREFIX + regex_pattern.decode('utf-8'))
common_test(dut, config, expected_backtrace=None, expected_coredump=[coredump_pattern]) common_test(dut, config, expected_backtrace=None, expected_coredump=[coredump_pattern])
@pytest.mark.generic
@idf_parametrize('config, target', CONFIG_PANIC_HALT, indirect=['config', 'target'])
def test_panic_halt(dut: PanicTestDut) -> None:
dut.run_test_func('test_panic_halt')
dut.expect_exact('CPU halted.', timeout=30)
dut.expect_none(dut.REBOOT, timeout=3)

View File

@@ -0,0 +1,5 @@
# Panic halt CI config
CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT=y
CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT=n
CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT=n
CONFIG_ESP_SYSTEM_PANIC_GDBSTUB=n