mirror of
https://github.com/espressif/esp-idf.git
synced 2025-07-30 10:47:19 +02:00
feat(esp_system): Print backtrace for both CPUs when cache error does not determine CPU
This commit is contained in:
committed by
BOT
parent
6cbb5af66e
commit
3f82f6e93b
@ -340,6 +340,7 @@ static inline void print_cache_err_details(const void *f)
|
|||||||
break;
|
break;
|
||||||
case EXTMEM_DCACHE_WRITE_FLASH_ST:
|
case EXTMEM_DCACHE_WRITE_FLASH_ST:
|
||||||
panic_print_str("Write back error occurred while dcache tries to write back to flash\r\n");
|
panic_print_str("Write back error occurred while dcache tries to write back to flash\r\n");
|
||||||
|
panic_print_str("The following backtrace may not indicate the code that caused Cache invalid access\r\n");
|
||||||
break;
|
break;
|
||||||
case EXTMEM_MMU_ENTRY_FAULT_ST:
|
case EXTMEM_MMU_ENTRY_FAULT_ST:
|
||||||
vaddr = REG_READ(EXTMEM_CACHE_MMU_FAULT_VADDR_REG);
|
vaddr = REG_READ(EXTMEM_CACHE_MMU_FAULT_VADDR_REG);
|
||||||
|
@ -151,9 +151,20 @@ static void panic_handler(void *frame, bool pseudo_excause)
|
|||||||
busy_wait();
|
busy_wait();
|
||||||
} else if (panic_get_cause(frame) == PANIC_RSN_INTWDT_CPU1 && core_id == 0) {
|
} else if (panic_get_cause(frame) == PANIC_RSN_INTWDT_CPU1 && core_id == 0) {
|
||||||
busy_wait();
|
busy_wait();
|
||||||
} else if (panic_get_cause(frame) == PANIC_RSN_CACHEERR && core_id != esp_cache_err_get_cpuid()) {
|
} else if (panic_get_cause(frame) == PANIC_RSN_CACHEERR) {
|
||||||
g_exc_frames[core_id] = NULL; // Only print the backtrace for the offending core
|
// The invalid cache access interrupt calls to the panic handler.
|
||||||
busy_wait();
|
// When the cache interrupt happens, we can not determine the CPU where the
|
||||||
|
// invalid cache access has occurred.
|
||||||
|
if (esp_cache_err_get_cpuid() == -1) {
|
||||||
|
// We can not determine the CPU where the invalid cache access has occurred.
|
||||||
|
// Print backtraces for both CPUs.
|
||||||
|
if (core_id != 0) {
|
||||||
|
busy_wait();
|
||||||
|
}
|
||||||
|
} else if (core_id != esp_cache_err_get_cpuid()) {
|
||||||
|
g_exc_frames[core_id] = NULL; // Only print the backtrace for the offending core
|
||||||
|
busy_wait();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
||||||
else if (panic_get_cause(frame) == ETS_ASSIST_DEBUG_INUM &&
|
else if (panic_get_cause(frame) == ETS_ASSIST_DEBUG_INUM &&
|
||||||
@ -183,7 +194,7 @@ static void panic_handler(void *frame, bool pseudo_excause)
|
|||||||
#if __XTENSA__
|
#if __XTENSA__
|
||||||
if (!(esp_ptr_executable(esp_cpu_pc_to_addr(panic_get_address(frame))) && (panic_get_address(frame) & 0xC0000000U))) {
|
if (!(esp_ptr_executable(esp_cpu_pc_to_addr(panic_get_address(frame))) && (panic_get_address(frame) & 0xC0000000U))) {
|
||||||
/* Xtensa ABI sets the 2 MSBs of the PC according to the windowed call size
|
/* Xtensa ABI sets the 2 MSBs of the PC according to the windowed call size
|
||||||
* Incase the PC is invalid, GDB will fail to translate addresses to function names
|
* In case the PC is invalid, GDB will fail to translate addresses to function names
|
||||||
* Hence replacing the PC to a placeholder address in case of invalid PC
|
* Hence replacing the PC to a placeholder address in case of invalid PC
|
||||||
*/
|
*/
|
||||||
panic_set_address(frame, (uint32_t)&_invalid_pc_placeholder);
|
panic_set_address(frame, (uint32_t)&_invalid_pc_placeholder);
|
||||||
|
@ -59,6 +59,10 @@ void test_assert(void);
|
|||||||
|
|
||||||
void test_assert_cache_disabled(void);
|
void test_assert_cache_disabled(void);
|
||||||
|
|
||||||
|
void test_assert_cache_write_back_error_can_print_backtrace(void);
|
||||||
|
|
||||||
|
void test_assert_cache_write_back_error_can_print_backtrace2(void);
|
||||||
|
|
||||||
void test_illegal_access(void);
|
void test_illegal_access(void);
|
||||||
|
|
||||||
void test_capture_dram(void);
|
void test_capture_dram(void);
|
||||||
|
@ -114,6 +114,8 @@ void app_main(void)
|
|||||||
HANDLE_TEST(test_name, test_ub);
|
HANDLE_TEST(test_name, test_ub);
|
||||||
HANDLE_TEST(test_name, test_assert);
|
HANDLE_TEST(test_name, test_assert);
|
||||||
HANDLE_TEST(test_name, test_assert_cache_disabled);
|
HANDLE_TEST(test_name, test_assert_cache_disabled);
|
||||||
|
HANDLE_TEST(test_name, test_assert_cache_write_back_error_can_print_backtrace);
|
||||||
|
HANDLE_TEST(test_name, test_assert_cache_write_back_error_can_print_backtrace2);
|
||||||
#if CONFIG_IDF_TARGET_ESP32
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
HANDLE_TEST(test_name, test_illegal_access);
|
HANDLE_TEST(test_name, test_illegal_access);
|
||||||
#endif
|
#endif
|
||||||
|
@ -7,10 +7,12 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
#include "esp_partition.h"
|
#include "esp_partition.h"
|
||||||
#include "esp_flash.h"
|
#include "esp_flash.h"
|
||||||
#include "esp_system.h"
|
#include "esp_system.h"
|
||||||
|
#include "spi_flash_mmap.h"
|
||||||
|
|
||||||
#include "esp_private/cache_utils.h"
|
#include "esp_private/cache_utils.h"
|
||||||
#include "esp_memory_utils.h"
|
#include "esp_memory_utils.h"
|
||||||
@ -20,6 +22,7 @@
|
|||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
|
|
||||||
#include "hal/mpu_hal.h"
|
#include "hal/mpu_hal.h"
|
||||||
|
#include "rom/cache.h"
|
||||||
|
|
||||||
/* Test utility function */
|
/* Test utility function */
|
||||||
|
|
||||||
@ -177,6 +180,46 @@ void IRAM_ATTR test_assert_cache_disabled(void)
|
|||||||
assert(0);
|
assert(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char TEST_STR[] = "my_tag";
|
||||||
|
void test_assert_cache_write_back_error_can_print_backtrace(void)
|
||||||
|
{
|
||||||
|
printf("1) %p\n", TEST_STR);
|
||||||
|
*(uint32_t*)TEST_STR = 3; // We changed the rodata string.
|
||||||
|
// All chips except ESP32S3 stop execution here and raise a LoadStore error on the line above.
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32S3
|
||||||
|
// On the ESP32S3, the error occurs later when the cache writeback is triggered
|
||||||
|
// (in this test, a direct call to Cache_WriteBack_All).
|
||||||
|
Cache_WriteBack_All(); // Cache writeback triggers the invalid cache access interrupt.
|
||||||
|
#endif
|
||||||
|
// We are testing that the backtrace is printed instead of TG1WDT.
|
||||||
|
printf("2) %p\n", TEST_STR); // never get to this place.
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_assert_cache_write_back_error_can_print_backtrace2(void)
|
||||||
|
{
|
||||||
|
printf("1) %p\n", TEST_STR);
|
||||||
|
*(uint32_t*)TEST_STR = 3; // We changed the rodata string.
|
||||||
|
// All chips except ESP32S3 stop execution here and raise a LoadStore error on the line above.
|
||||||
|
// On the ESP32S3, the error occurs later when the cache writeback is triggered
|
||||||
|
// (in this test, a large range of DRAM is mapped and read, causing an error).
|
||||||
|
uint8_t temp = 0;
|
||||||
|
size_t map_size = SPI_FLASH_SEC_SIZE * 512;
|
||||||
|
const void *map;
|
||||||
|
spi_flash_mmap_handle_t out_handle;
|
||||||
|
esp_err_t err = spi_flash_mmap(0, map_size, SPI_FLASH_MMAP_DATA, &map, &out_handle);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
printf("spi_flash_mmap failed %x\n", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const uint8_t *rodata = map;
|
||||||
|
for (size_t i = 0; i < map_size; i++) {
|
||||||
|
temp = rodata[i];
|
||||||
|
}
|
||||||
|
// Cache writeback triggers the invalid cache access interrupt.
|
||||||
|
// We are testing that the backtrace is printed instead of TG1WDT.
|
||||||
|
printf("2) %p 0x%" PRIx8 " \n", TEST_STR, temp); // never get to this place.
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function overwrites the stack beginning from the valid area continuously towards and beyond
|
* This function overwrites the stack beginning from the valid area continuously towards and beyond
|
||||||
* the end of the stack (stack base) of the current task.
|
* the end of the stack (stack base) of the current task.
|
||||||
|
@ -533,6 +533,42 @@ def test_assert_cache_disabled(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def cache_error_log_check(dut: PanicTestDut) -> None:
|
||||||
|
if dut.is_xtensa:
|
||||||
|
if dut.target == 'esp32s3':
|
||||||
|
dut.expect_exact("Guru Meditation Error: Core / panic'ed (Cache disabled but cached memory region accessed)")
|
||||||
|
dut.expect_exact('Write back error occurred while dcache tries to write back to flash')
|
||||||
|
dut.expect_exact('The following backtrace may not indicate the code that caused Cache invalid access')
|
||||||
|
else:
|
||||||
|
dut.expect_exact("Guru Meditation Error: Core 0 panic'ed (LoadStoreError)")
|
||||||
|
else:
|
||||||
|
dut.expect_exact("Guru Meditation Error: Core 0 panic'ed (Store access fault)")
|
||||||
|
dut.expect_reg_dump(0)
|
||||||
|
if dut.target == 'esp32s3':
|
||||||
|
dut.expect_reg_dump(1)
|
||||||
|
dut.expect_cpu_reset()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.generic
|
||||||
|
@pytest.mark.supported_targets
|
||||||
|
@pytest.mark.parametrize('config', ['panic'], indirect=True)
|
||||||
|
def test_assert_cache_write_back_error_can_print_backtrace(
|
||||||
|
dut: PanicTestDut, config: str, test_func_name: str
|
||||||
|
) -> None:
|
||||||
|
dut.run_test_func(test_func_name)
|
||||||
|
cache_error_log_check(dut)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.generic
|
||||||
|
@pytest.mark.supported_targets
|
||||||
|
@pytest.mark.parametrize('config', ['panic'], indirect=True)
|
||||||
|
def test_assert_cache_write_back_error_can_print_backtrace2(
|
||||||
|
dut: PanicTestDut, config: str, test_func_name: str
|
||||||
|
) -> None:
|
||||||
|
dut.run_test_func(test_func_name)
|
||||||
|
cache_error_log_check(dut)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.esp32
|
@pytest.mark.esp32
|
||||||
@pytest.mark.generic
|
@pytest.mark.generic
|
||||||
@pytest.mark.parametrize('config', ['panic_delay'], indirect=True)
|
@pytest.mark.parametrize('config', ['panic_delay'], indirect=True)
|
||||||
|
Reference in New Issue
Block a user