arch/xtensa: Print backtrace on assertions.

Signed-off-by: Abdelatif Guettouche <abdelatif.guettouche@espressif.com>
This commit is contained in:
Abdelatif Guettouche 2020-11-12 09:45:00 +00:00 committed by Alan Carvalho de Assis
parent 759b63d4be
commit 9d28687b6f
6 changed files with 270 additions and 4 deletions

@ -94,6 +94,20 @@ config XTENSA_CP_INITSET
is provided by CONFIG_XTENSA_CP_INITSET. Each bit corresponds to one
coprocessor with the same bit layout as for the CPENABLE register.
config XTENSA_DUMPBT_ON_ASSERT
bool "Dump backtrace on assertions"
default y
depends on DEBUG_ALERT
---help---
Enable a backtrace dump on assertions.
config XTENSA_BTDEPTH
int "Backtrace depth"
default 50
depends on XTENSA_DUMPBT_ON_ASSERT
---help---
This is the depth of the backtrace.
config XTENSA_USE_SEPARATE_IMEM
bool "Use a separate heap for internal memory"
default n

@ -361,5 +361,9 @@ void xtensa_pminitialize(void);
void up_stack_color(FAR void *stackbase, size_t nbytes);
#endif
#ifdef CONFIG_XTENSA_DUMPBT_ON_ASSERT
void xtensa_backtrace_start(uint32_t *pc, uint32_t *sp, uint32_t *next_pc);
#endif
#endif /* __ASSEMBLY__ */
#endif /* __ARCH_XTENSA_SRC_COMMON_XTENSA_H */

@ -0,0 +1,103 @@
/****************************************************************************
* arch/xtensa/src/common/xtensa_dumpstate.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <arch/xtensa/core.h>
#include <arch/xtensa/xtensa_specregs.h>
#include "xtensa_abi.h"
/*
* This is how the stack looks like when calling the function below:
*
* High Addr
* ..................
* | i-3 BSA |
* | i-1 locals | Function B
* .................. i-1 SP
* | i-2 BSA |
* | i locals | Function A (Start of backtrace)
* ------------------ i SP
* | i-1 BSA |
* | i+1 locals | Backtracing function (e.g. xtensa_btdump())
* ------------------ i+1 SP
* | i BSA |
* | i+2 locals | xtensa_backtrace_start()
* ------------------ i+2 SP
* | i+1 BSA |
* | i+3 locals | xtensa_window_spill()
* ------------------ i+3 SP
* .................. Low Addr
*/
/****************************************************************************
* Name: xtensa_backtrace_start
*
* Description:
* Get the first frame of the current stack's backtrace
*
* Given the following function call flow (B -> A -> X -> xtensa_backtrace_start),
* this function will do the following:
* - Flush CPU registers and window frames onto the current stack
* - Return PC and SP of function A (i.e. start of the stack's backtrace)
* - Return PC of function B (i.e. next_pc)
*
* Input Parameters:
* pc - PC of the first frame in the backtrace
* sp - SP of the first frame in the backtrace
* next_pc - PC of the first frame's caller
*
* C callable as:
* void xtensa_backtrace_start(uint32_t *pc, uint32_t *sp, uint32_t *next_pc)
*
****************************************************************************/
.section .iram1, "ax"
.global xtensa_backtrace_start
.type xtensa_backtrace_start, @function
.align 4
xtensa_backtrace_start:
ENTRY(32)
call8 xtensa_window_spill /* Spill registers onto stack (excluding this
* function) */
/* a2, a3, a4 should be out arguments for i SP, i PC, i-1 PC respectively.
* Use a5 and a6 as scratch.
*/
l32e a5, sp, -16 /* Get i PC, which is ret addres of i+1 */
s32i a5, a2, 0 /* Store i PC to arg *pc */
l32e a6, sp, -12 /* Get i+1 SP. Used to access i BS */
l32e a5, a6, -12 /* Get i SP */
s32i a5, a3, 0 /* Store i SP to arg *sp */
l32e a5, a6, -16 /* Get i-1 PC, which is ret address of i */
s32i a5, a4, 0 /* Store i-1 PC to arg *next_pc */
RET0
.size xtensa_backtrace_start, . - xtensa_backtrace_start

@ -32,12 +32,12 @@
#include <nuttx/irq.h>
#include <nuttx/arch.h>
#include <arch/xtensa/xtensa_corebits.h>
#include <arch/board/board.h>
#include <arch/chip/core-isa.h>
#include "sched/sched.h"
#include "xtensa.h"
#include "chip_macros.h"
#include "chip_memory.h"
#ifdef CONFIG_DEBUG_ALERT
@ -150,6 +150,109 @@ static inline void xtensa_registerdump(void)
#endif
}
#ifdef CONFIG_XTENSA_DUMPBT_ON_ASSERT
/****************************************************************************
* Name: xtensa_getcause
****************************************************************************/
static inline uint32_t xtensa_getcause(void)
{
uint32_t cause;
__asm__ __volatile__
(
"rsr %0, EXCCAUSE" : "=r"(cause)
);
return cause;
}
/****************************************************************************
* Name: stackpc
****************************************************************************/
static inline uint32_t stackpc(uint32_t pc)
{
if (pc & 0x80000000)
{
/* Top two bits of a0 (return address) specify window increment.
* Overwrite to map to address space.
*/
pc = (pc & 0x3fffffff) | 0x40000000;
}
/* Minus 3 to get PC of previous instruction (i.e. instruction executed
* before return address).
*/
return pc - 3;
}
/****************************************************************************
* Name: corruptedframe
****************************************************************************/
static inline bool corruptedframe(uint32_t pc, uint32_t sp)
{
return !(xtensa_ptr_exec((void *)stackpc(pc)) || xtensa_sp_sane(sp));
}
/****************************************************************************
* Name: nextframe
****************************************************************************/
static bool nextframe(uint32_t *pc, uint32_t *sp, uint32_t *npc)
{
/* Use frame(i - 1)'s base save area located below frame(i)'s sp to get
* frame(i - 1)'s sp and frame(i - 2)'s pc. Base save area consists of
* 4 words under SP.
*/
void *bsa = (void *)*sp;
*pc = *npc;
*npc = *((uint32_t *)(bsa - 16));
*sp = *((uint32_t *)(bsa - 12));
return !corruptedframe(*pc, *sp);
}
/****************************************************************************
* Name: xtensa_btdump
****************************************************************************/
static inline void xtensa_btdump(void)
{
uint32_t pc;
uint32_t sp;
uint32_t npc;
int i;
bool corrupted = false;
xtensa_backtrace_start(&pc, &sp, &npc);
_alert("Backtrace0: %x:%x\n", stackpc(pc), sp);
corrupted = corruptedframe(pc, sp) &&
!(xtensa_getcause() == EXCCAUSE_INSTR_PROHIBITED);
for (i = 1; i <= CONFIG_XTENSA_BTDEPTH && npc != 0 && !corrupted; i++)
{
if (!nextframe(&pc, &sp, &npc))
{
corrupted = true;
}
_alert("Backtrace%d: %x:%x\n", i, stackpc(pc), sp);
}
_alert("BACKTRACE %s\n",
(corrupted ? "CORRUPTED!" : (npc == 0 ? "Done":"CONTINUES...")));
}
#endif /* CONFIG_XTENSA_DUMPBT_ON_ASSERT */
/****************************************************************************
* Public Functions
****************************************************************************/
@ -179,6 +282,12 @@ void xtensa_dumpstate(void)
xtensa_registerdump();
/* Dump the backtrace */
#ifdef CONFIG_XTENSA_DUMPBT_ON_ASSERT
xtensa_btdump();
#endif
/* Get the limits on the user stack memory */
ustackbase = (uint32_t)rtcb->adj_stack_ptr;
@ -267,4 +376,4 @@ void xtensa_dumpstate(void)
up_showtasks();
}
#endif /* CONFIG_ARCH_STACKDUMP */
#endif /* CONFIG_DEBUG_ALERT */

@ -65,6 +65,10 @@ ifeq ($(CONFIG_DEBUG_ALERT),y)
CMN_CSRCS += xtensa_dumpstate.c
endif
ifeq ($(CONFIG_XTENSA_DUMPBT_ON_ASSERT),y)
CMN_ASRCS += xtensa_backtrace.S
endif
ifeq ($(CONFIG_SPINLOCK),y)
CMN_CSRCS += xtensa_testset.c
endif
@ -165,7 +169,6 @@ CHIP_CSRCS += esp32_wtd.c
endif
endif
ifeq ($(CONFIG_ARCH_USE_MODULE_TEXT),y)
CHIP_CSRCS += esp32_modtext.c
CMN_ASRCS += xtensa_loadstore.S

@ -794,6 +794,19 @@ extern int rom_i2c_writeReg(int block, int block_id, int reg_add,
* Inline Functions
****************************************************************************/
/****************************************************************************
* Name: esp32_sp_dram
*
* Description:
* Check if the stack pointer is in DRAM.
*
****************************************************************************/
static inline bool IRAM_ATTR esp32_sp_dram(uint32_t sp)
{
return (sp >= SOC_DRAM_LOW + 0x10 && sp < SOC_DRAM_HIGH - 0x10);
}
/****************************************************************************
* Name: esp32_ptr_extram
*
@ -808,4 +821,24 @@ static inline bool IRAM_ATTR esp32_ptr_extram(const void *p)
(intptr_t)p < SOC_EXTRAM_DATA_HIGH);
}
/****************************************************************************
* Name: esp32_ptr_exec
*
* Description:
* Check if the pointer is within an executable range.
*
****************************************************************************/
static inline bool IRAM_ATTR esp32_ptr_exec(const void *p)
{
intptr_t ip = (intptr_t)p;
return (ip >= SOC_IROM_LOW && ip < SOC_IROM_HIGH)
|| (ip >= SOC_IRAM_LOW && ip < SOC_IRAM_HIGH)
|| (ip >= SOC_IROM_MASK_LOW && ip < SOC_IROM_MASK_HIGH)
#if defined(SOC_CACHE_APP_LOW) && !defined(CONFIG_SMP)
|| (ip >= SOC_CACHE_APP_LOW && ip < SOC_CACHE_APP_HIGH)
#endif
|| (ip >= SOC_RTC_IRAM_LOW && ip < SOC_RTC_IRAM_HIGH);
}
#endif /* __ARCH_XTENSA_SRC_ESP32_HARDWARE_ESP32_SOC_H */