Skip to content

Commit cf5dbad

Browse files
committed
Merge branch 'bugfix/backtrace_from_interrupts_backport_v3.1' into 'release/v3.1'
Make backtrace work across interrupts (backport v3.1) See merge request idf/esp-idf!4182
2 parents 72c88db + 2ccfa6b commit cf5dbad

File tree

2 files changed

+166
-1
lines changed

2 files changed

+166
-1
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Note: Currently, the backtraces must still be checked manually. Therefore,
3+
* these test cases should always pass.
4+
* Todo: Automate the checking of backtrace addresses.
5+
*/
6+
#include <stdlib.h>
7+
#include "unity.h"
8+
#include "freertos/FreeRTOS.h"
9+
#include "freertos/task.h"
10+
#include "freertos/xtensa_api.h"
11+
#include "esp_intr_alloc.h"
12+
13+
#define SW_ISR_LEVEL_1 7
14+
#define SW_ISR_LEVEL_3 29
15+
#define RECUR_DEPTH 3
16+
#define ACTION_ABORT -1
17+
#define ACTION_INT_WDT -2
18+
19+
// Set to (-1) for abort(), (-2) for interrupt watchdog
20+
static int backtrace_trigger_source;
21+
22+
/*
23+
* Recursive functions to generate a longer call stack. When the max specified
24+
* recursion depth is reached, the following actions can be taken.
25+
*/
26+
static void __attribute__((__noinline__)) recursive_func(int recur_depth, int action)
27+
{
28+
if (recur_depth > 1) {
29+
recursive_func(recur_depth - 1, action);
30+
} else if (action >= 0) {
31+
xt_set_intset(1 << action);
32+
} else if (action == ACTION_ABORT) {
33+
abort();
34+
// Todo: abort() causes problems in GDB Stub backtrace due to being 'non returning'.
35+
} else if (action == ACTION_INT_WDT) {
36+
portDISABLE_INTERRUPTS();
37+
while (1) {
38+
;
39+
}
40+
}
41+
}
42+
43+
static void level_three_isr (void *arg)
44+
{
45+
xt_set_intclear(1 << SW_ISR_LEVEL_3); //Clear interrupt
46+
recursive_func(RECUR_DEPTH, backtrace_trigger_source); //Abort at the max recursive depth
47+
}
48+
49+
static void level_one_isr(void *arg)
50+
{
51+
xt_set_intclear(1 << SW_ISR_LEVEL_1); //Clear interrupt
52+
recursive_func(RECUR_DEPTH, SW_ISR_LEVEL_3); //Trigger nested interrupt max recursive depth
53+
}
54+
55+
TEST_CASE("Test backtrace from abort", "[reset_reason][reset=SW_CPU_RESET]")
56+
{
57+
//Allocate level one and three SW interrupts
58+
esp_intr_alloc(ETS_INTERNAL_SW0_INTR_SOURCE, 0, level_one_isr, NULL, NULL); //Level 1 SW intr
59+
esp_intr_alloc(ETS_INTERNAL_SW1_INTR_SOURCE, 0, level_three_isr, NULL, NULL); //Level 3 SW intr
60+
backtrace_trigger_source = ACTION_ABORT;
61+
recursive_func(RECUR_DEPTH, SW_ISR_LEVEL_1); //Trigger lvl 1 SW interrupt at max recursive depth
62+
}
63+
64+
TEST_CASE("Test backtrace from interrupt watchdog timeout", "[reset_reason][reset=Interrupt wdt timeout on CPU0,SW_CPU_RESET]")
65+
{
66+
//Allocate level one and three SW interrupts
67+
esp_intr_alloc(ETS_INTERNAL_SW0_INTR_SOURCE, 0, level_one_isr, NULL, NULL); //Level 1 SW intr
68+
esp_intr_alloc(ETS_INTERNAL_SW1_INTR_SOURCE, 0, level_three_isr, NULL, NULL); //Level 3 SW intr
69+
backtrace_trigger_source = ACTION_INT_WDT;
70+
recursive_func(RECUR_DEPTH, SW_ISR_LEVEL_1); //Trigger lvl 1 SW interrupt at max recursive depth
71+
}

components/freertos/xtensa_vectors.S

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,25 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
103103
#define TASKTCB_XCOREID_OFFSET (0x38+configMAX_TASK_NAME_LEN+3)&~3
104104
.extern pxCurrentTCB
105105

106-
/* Enable stack backtrace across exception/interrupt - see below */
106+
/*
107+
--------------------------------------------------------------------------------
108+
In order for backtracing to be able to trace from the pre-exception stack
109+
across to the exception stack (including nested interrupts), we need to create
110+
a pseudo base-save area to make it appear like the exception dispatcher was
111+
triggered by a CALL4 from the pre-exception code. In reality, the exception
112+
dispatcher uses the same window as pre-exception code, and only CALL0s are
113+
used within the exception dispatcher.
114+
115+
To create the pseudo base-save area, we need to store a copy of the pre-exception's
116+
base save area (a0 to a4) below the exception dispatcher's SP. EXCSAVE_x will
117+
be used to store a copy of the SP that points to the interrupted code's exception
118+
frame just in case the exception dispatcher's SP does not point to the exception
119+
frame (which is the case when switching from task to interrupt stack).
120+
121+
Clearing the pseudo base-save area is uncessary as the interrupt dispatcher
122+
will restore the current SP to that of the pre-exception SP.
123+
--------------------------------------------------------------------------------
124+
*/
107125
#ifdef CONFIG_FREERTOS_INTERRUPT_BACKTRACE
108126
#define XT_DEBUG_BACKTRACE 1
109127
#endif
@@ -202,9 +220,22 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
202220
/* This bit of code provides a nice debug backtrace in the debugger.
203221
It does take a few more instructions, so undef XT_DEBUG_BACKTRACE
204222
if you want to save the cycles.
223+
At this point, the exception frame should have been allocated and filled,
224+
and current sp points to the interrupt stack (for non-nested interrupt)
225+
or below the allocated exception frame (for nested interrupts). Copy the
226+
pre-exception's base save area below the current SP.
205227
*/
206228
#ifdef XT_DEBUG_BACKTRACE
207229
#ifndef __XTENSA_CALL0_ABI__
230+
rsr a0, EXCSAVE_1 + \level - 1 /* Get exception frame pointer stored in EXCSAVE_x */
231+
l32i a3, a0, XT_STK_A0 /* Copy pre-exception a0 (return address) */
232+
s32e a3, a1, -16
233+
l32i a3, a0, XT_STK_A1 /* Copy pre-exception a1 (stack pointer) */
234+
s32e a3, a1, -12
235+
/* Backtracing only needs a0 and a1, no need to create full base save area.
236+
Also need to change current frame's return address to point to pre-exception's
237+
last run instruction.
238+
*/
208239
rsr a0, EPC_1 + \level - 1 /* return address */
209240
movi a4, 0xC0000000 /* constant with top 2 bits set (call size) */
210241
or a0, a0, a4 /* set top 2 bits */
@@ -670,8 +701,16 @@ _xt_user_exc:
670701
#endif
671702
wsr a0, PS
672703

704+
/*
705+
Create pseudo base save area. At this point, sp is still pointing to the
706+
allocated and filled exception stack frame.
707+
*/
673708
#ifdef XT_DEBUG_BACKTRACE
674709
#ifndef __XTENSA_CALL0_ABI__
710+
l32i a3, sp, XT_STK_A0 /* Copy pre-exception a0 (return address) */
711+
s32e a3, sp, -16
712+
l32i a3, sp, XT_STK_A1 /* Copy pre-exception a1 (stack pointer) */
713+
s32e a3, sp, -12
675714
rsr a0, EPC_1 /* return address for debug backtrace */
676715
movi a5, 0xC0000000 /* constant with top 2 bits set (call size) */
677716
rsync /* wait for WSR.PS to complete */
@@ -1086,6 +1125,16 @@ _xt_lowint1:
10861125
movi a0, _xt_user_exit /* save exit point for dispatch */
10871126
s32i a0, sp, XT_STK_EXIT
10881127

1128+
/* EXCSAVE_1 should now be free to use. Use it to keep a copy of the
1129+
current stack pointer that points to the exception frame (XT_STK_FRAME).*/
1130+
#ifdef XT_DEBUG_BACKTRACE
1131+
#ifndef __XTENSA_CALL0_ABI__
1132+
mov a0, sp
1133+
wsr a0, EXCSAVE_1
1134+
#endif
1135+
#endif
1136+
1137+
10891138
/* Save rest of interrupt context and enter RTOS. */
10901139
call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */
10911140

@@ -1166,6 +1215,15 @@ _xt_medint2:
11661215
movi a0, _xt_medint2_exit /* save exit point for dispatch */
11671216
s32i a0, sp, XT_STK_EXIT
11681217

1218+
/* EXCSAVE_2 should now be free to use. Use it to keep a copy of the
1219+
current stack pointer that points to the exception frame (XT_STK_FRAME).*/
1220+
#ifdef XT_DEBUG_BACKTRACE
1221+
#ifndef __XTENSA_CALL0_ABI__
1222+
mov a0, sp
1223+
wsr a0, EXCSAVE_2
1224+
#endif
1225+
#endif
1226+
11691227
/* Save rest of interrupt context and enter RTOS. */
11701228
call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */
11711229

@@ -1237,6 +1295,15 @@ _xt_medint3:
12371295
movi a0, _xt_medint3_exit /* save exit point for dispatch */
12381296
s32i a0, sp, XT_STK_EXIT
12391297

1298+
/* EXCSAVE_3 should now be free to use. Use it to keep a copy of the
1299+
current stack pointer that points to the exception frame (XT_STK_FRAME).*/
1300+
#ifdef XT_DEBUG_BACKTRACE
1301+
#ifndef __XTENSA_CALL0_ABI__
1302+
mov a0, sp
1303+
wsr a0, EXCSAVE_3
1304+
#endif
1305+
#endif
1306+
12401307
/* Save rest of interrupt context and enter RTOS. */
12411308
call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */
12421309

@@ -1307,6 +1374,15 @@ _xt_medint4:
13071374
movi a0, _xt_medint4_exit /* save exit point for dispatch */
13081375
s32i a0, sp, XT_STK_EXIT
13091376

1377+
/* EXCSAVE_4 should now be free to use. Use it to keep a copy of the
1378+
current stack pointer that points to the exception frame (XT_STK_FRAME).*/
1379+
#ifdef XT_DEBUG_BACKTRACE
1380+
#ifndef __XTENSA_CALL0_ABI__
1381+
mov a0, sp
1382+
wsr a0, EXCSAVE_4
1383+
#endif
1384+
#endif
1385+
13101386
/* Save rest of interrupt context and enter RTOS. */
13111387
call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */
13121388

@@ -1377,6 +1453,15 @@ _xt_medint5:
13771453
movi a0, _xt_medint5_exit /* save exit point for dispatch */
13781454
s32i a0, sp, XT_STK_EXIT
13791455

1456+
/* EXCSAVE_5 should now be free to use. Use it to keep a copy of the
1457+
current stack pointer that points to the exception frame (XT_STK_FRAME).*/
1458+
#ifdef XT_DEBUG_BACKTRACE
1459+
#ifndef __XTENSA_CALL0_ABI__
1460+
mov a0, sp
1461+
wsr a0, EXCSAVE_5
1462+
#endif
1463+
#endif
1464+
13801465
/* Save rest of interrupt context and enter RTOS. */
13811466
call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */
13821467

@@ -1447,6 +1532,15 @@ _xt_medint6:
14471532
movi a0, _xt_medint6_exit /* save exit point for dispatch */
14481533
s32i a0, sp, XT_STK_EXIT
14491534

1535+
/* EXCSAVE_6 should now be free to use. Use it to keep a copy of the
1536+
current stack pointer that points to the exception frame (XT_STK_FRAME).*/
1537+
#ifdef XT_DEBUG_BACKTRACE
1538+
#ifndef __XTENSA_CALL0_ABI__
1539+
mov a0, sp
1540+
wsr a0, EXCSAVE_6
1541+
#endif
1542+
#endif
1543+
14501544
/* Save rest of interrupt context and enter RTOS. */
14511545
call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */
14521546

0 commit comments

Comments
 (0)