Merge pull request #137 from SuperHouse/feature/better_crash_dumps
Better crash dumps
This commit is contained in:
commit
3ba19d7c4e
19 changed files with 1013 additions and 664 deletions
|
@ -73,6 +73,7 @@
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <xtensa_ops.h>
|
||||||
|
|
||||||
#include "FreeRTOS.h"
|
#include "FreeRTOS.h"
|
||||||
#include "task.h"
|
#include "task.h"
|
||||||
|
@ -87,7 +88,7 @@ char level1_int_disabled;
|
||||||
After tasks start, task stacks are all allocated from the heap and
|
After tasks start, task stacks are all allocated from the heap and
|
||||||
FreeRTOS checks for stack overflow.
|
FreeRTOS checks for stack overflow.
|
||||||
*/
|
*/
|
||||||
static uint32_t xPortSupervisorStackPointer;
|
uint32_t xPortSupervisorStackPointer;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Stack initialization
|
* Stack initialization
|
||||||
|
@ -220,7 +221,7 @@ size_t xPortGetFreeHeapSize( void )
|
||||||
|
|
||||||
uint32_t sp = xPortSupervisorStackPointer;
|
uint32_t sp = xPortSupervisorStackPointer;
|
||||||
if(sp == 0) /* scheduler not started */
|
if(sp == 0) /* scheduler not started */
|
||||||
__asm__ __volatile__ ("mov %0, a1\n" : "=a"(sp));
|
SP(sp);
|
||||||
return sp - brk_val + mi.fordblks;
|
return sp - brk_val + mi.fordblks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,6 +75,9 @@ FLAVOR ?= release # or debug
|
||||||
# Compiler names, etc. assume gdb
|
# Compiler names, etc. assume gdb
|
||||||
CROSS ?= xtensa-lx106-elf-
|
CROSS ?= xtensa-lx106-elf-
|
||||||
|
|
||||||
|
# Path to the filteroutput.py tool
|
||||||
|
FILTEROUTPUT ?= $(ROOT)/utils/filteroutput.py
|
||||||
|
|
||||||
AR = $(CROSS)ar
|
AR = $(CROSS)ar
|
||||||
CC = $(CROSS)gcc
|
CC = $(CROSS)gcc
|
||||||
CPP = $(CROSS)cpp
|
CPP = $(CROSS)cpp
|
||||||
|
@ -385,7 +388,7 @@ size: $(PROGRAM_OUT)
|
||||||
$(Q) $(CROSS)size --format=sysv $(PROGRAM_OUT)
|
$(Q) $(CROSS)size --format=sysv $(PROGRAM_OUT)
|
||||||
|
|
||||||
test: flash
|
test: flash
|
||||||
screen $(ESPPORT) 115200
|
$(FILTEROUTPUT) --port $(ESPPORT) --baud 115200 --elf $(PROGRAM_OUT)
|
||||||
|
|
||||||
# the rebuild target is written like this so it can be run in a parallel build
|
# the rebuild target is written like this so it can be run in a parallel build
|
||||||
# environment without causing weird side effects
|
# environment without causing weird side effects
|
||||||
|
|
|
@ -39,8 +39,6 @@ void user_init(void);
|
||||||
#define RTCMEM_BACKUP_PHY_VER 31
|
#define RTCMEM_BACKUP_PHY_VER 31
|
||||||
#define RTCMEM_SYSTEM_PP_VER 62
|
#define RTCMEM_SYSTEM_PP_VER 62
|
||||||
|
|
||||||
#define halt() while (1) {}
|
|
||||||
|
|
||||||
extern uint32_t _bss_start;
|
extern uint32_t _bss_start;
|
||||||
extern uint32_t _bss_end;
|
extern uint32_t _bss_end;
|
||||||
|
|
||||||
|
@ -63,7 +61,6 @@ static void IRAM set_spi0_divisor(uint32_t divisor);
|
||||||
static void zero_bss(void);
|
static void zero_bss(void);
|
||||||
static void init_networking(sdk_phy_info_t *phy_info, uint8_t *mac_addr);
|
static void init_networking(sdk_phy_info_t *phy_info, uint8_t *mac_addr);
|
||||||
static void init_g_ic(void);
|
static void init_g_ic(void);
|
||||||
static void dump_excinfo(void);
|
|
||||||
static void user_start_phase2(void);
|
static void user_start_phase2(void);
|
||||||
static void dump_flash_sector(uint32_t start_sector, uint32_t length);
|
static void dump_flash_sector(uint32_t start_sector, uint32_t length);
|
||||||
static void dump_flash_config_sectors(uint32_t start_sector);
|
static void dump_flash_config_sectors(uint32_t start_sector);
|
||||||
|
@ -80,11 +77,11 @@ static void IRAM get_otp_mac_address(uint8_t *buf) {
|
||||||
if (!(otp_flags & 0x8000)) {
|
if (!(otp_flags & 0x8000)) {
|
||||||
//FIXME: do we really need this check?
|
//FIXME: do we really need this check?
|
||||||
printf("Firmware ONLY supports ESP8266!!!\n");
|
printf("Firmware ONLY supports ESP8266!!!\n");
|
||||||
halt();
|
abort();
|
||||||
}
|
}
|
||||||
if (otp_id0 == 0 && otp_id1 == 0) {
|
if (otp_id0 == 0 && otp_id1 == 0) {
|
||||||
printf("empty otp\n");
|
printf("empty otp\n");
|
||||||
halt();
|
abort();
|
||||||
}
|
}
|
||||||
if (otp_flags & 0x1000) {
|
if (otp_flags & 0x1000) {
|
||||||
// If bit 12 is set, it indicates that the vendor portion of the MAC
|
// If bit 12 is set, it indicates that the vendor portion of the MAC
|
||||||
|
@ -125,23 +122,6 @@ static void IRAM set_spi0_divisor(uint32_t divisor) {
|
||||||
SPI(0).CTRL0 = SET_FIELD(SPI(0).CTRL0, SPI_CTRL0_CLOCK, clkdiv);
|
SPI(0).CTRL0 = SET_FIELD(SPI(0).CTRL0, SPI_CTRL0_CLOCK, clkdiv);
|
||||||
}
|
}
|
||||||
|
|
||||||
// .text+0x148
|
|
||||||
void IRAM sdk_user_fatal_exception_handler(void) {
|
|
||||||
if (!sdk_NMIIrqIsOn) {
|
|
||||||
vPortEnterCritical();
|
|
||||||
do {
|
|
||||||
DPORT.DPORT0 &= 0xffffffe0;
|
|
||||||
} while (DPORT.DPORT0 & 0x00000001);
|
|
||||||
}
|
|
||||||
Cache_Read_Disable();
|
|
||||||
Cache_Read_Enable(0, 0, 1);
|
|
||||||
dump_excinfo();
|
|
||||||
uart_flush_txfifo(0);
|
|
||||||
uart_flush_txfifo(1);
|
|
||||||
sdk_system_restart_in_nmi();
|
|
||||||
halt();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void IRAM default_putc(char c) {
|
static void IRAM default_putc(char c) {
|
||||||
uart_putc(0, c);
|
uart_putc(0, c);
|
||||||
|
@ -255,7 +235,7 @@ static void zero_bss(void) {
|
||||||
static void init_networking(sdk_phy_info_t *phy_info, uint8_t *mac_addr) {
|
static void init_networking(sdk_phy_info_t *phy_info, uint8_t *mac_addr) {
|
||||||
if (sdk_register_chipv6_phy(phy_info)) {
|
if (sdk_register_chipv6_phy(phy_info)) {
|
||||||
printf("FATAL: sdk_register_chipv6_phy failed");
|
printf("FATAL: sdk_register_chipv6_phy failed");
|
||||||
halt();
|
abort();
|
||||||
}
|
}
|
||||||
uart_set_baud(0, 74906);
|
uart_set_baud(0, 74906);
|
||||||
uart_set_baud(1, 74906);
|
uart_set_baud(1, 74906);
|
||||||
|
@ -311,37 +291,6 @@ static void init_g_ic(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// .Lfunc008 -- .irom0.text+0x2a0
|
|
||||||
static void dump_excinfo(void) {
|
|
||||||
uint32_t exccause, epc1, epc2, epc3, excvaddr, depc, excsave1;
|
|
||||||
uint32_t excinfo[8];
|
|
||||||
|
|
||||||
RSR(exccause, exccause);
|
|
||||||
printf("Fatal exception (%d): \n", (int)exccause);
|
|
||||||
RSR(epc1, epc1);
|
|
||||||
RSR(epc2, epc2);
|
|
||||||
RSR(epc3, epc3);
|
|
||||||
RSR(excvaddr, excvaddr);
|
|
||||||
RSR(depc, depc);
|
|
||||||
RSR(excsave1, excsave1);
|
|
||||||
printf("%s=0x%08x\n", "epc1", epc1);
|
|
||||||
printf("%s=0x%08x\n", "epc2", epc2);
|
|
||||||
printf("%s=0x%08x\n", "epc3", epc3);
|
|
||||||
printf("%s=0x%08x\n", "excvaddr", excvaddr);
|
|
||||||
printf("%s=0x%08x\n", "depc", depc);
|
|
||||||
printf("%s=0x%08x\n", "excsave1", excsave1);
|
|
||||||
sdk_system_rtc_mem_read(0, excinfo, 32); // Why?
|
|
||||||
excinfo[0] = 2;
|
|
||||||
excinfo[1] = exccause;
|
|
||||||
excinfo[2] = epc1;
|
|
||||||
excinfo[3] = epc2;
|
|
||||||
excinfo[4] = epc3;
|
|
||||||
excinfo[5] = excvaddr;
|
|
||||||
excinfo[6] = depc;
|
|
||||||
excinfo[7] = excsave1;
|
|
||||||
sdk_system_rtc_mem_write(0, excinfo, 32);
|
|
||||||
}
|
|
||||||
|
|
||||||
// .irom0.text+0x398
|
// .irom0.text+0x398
|
||||||
void sdk_wdt_init(void) {
|
void sdk_wdt_init(void) {
|
||||||
WDT.CTRL &= ~WDT_CTRL_ENABLE;
|
WDT.CTRL &= ~WDT_CTRL_ENABLE;
|
||||||
|
|
231
core/debug_dumps.c
Normal file
231
core/debug_dumps.c
Normal file
|
@ -0,0 +1,231 @@
|
||||||
|
/* Code for dumping status/debug output/etc, including fatal
|
||||||
|
* exception handling & abort implementation.
|
||||||
|
*
|
||||||
|
* Part of esp-open-rtos
|
||||||
|
*
|
||||||
|
* Partially reverse engineered from MIT licensed Espressif RTOS SDK Copyright (C) Espressif Systems.
|
||||||
|
* Additions Copyright (C) 2015 Superhouse Automation Pty Ltd
|
||||||
|
* BSD Licensed as described in the file LICENSE
|
||||||
|
*/
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <FreeRTOS.h>
|
||||||
|
#include <task.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "debug_dumps.h"
|
||||||
|
#include "common_macros.h"
|
||||||
|
#include "xtensa_ops.h"
|
||||||
|
#include "esp/rom.h"
|
||||||
|
#include "esp/uart.h"
|
||||||
|
#include "espressif/esp_common.h"
|
||||||
|
#include "sdk_internal.h"
|
||||||
|
|
||||||
|
/* Forward declarations */
|
||||||
|
static void IRAM fatal_handler_prelude(void);
|
||||||
|
/* Inner parts of crash handlers */
|
||||||
|
typedef void __attribute__((noreturn)) (*fatal_exception_handler_fn)(uint32_t *sp, bool registers_saved_on_stack);
|
||||||
|
static void __attribute__((noreturn)) standard_fatal_exception_handler_inner(uint32_t *sp, bool registers_saved_on_stack);
|
||||||
|
static void __attribute__((noreturn)) second_fatal_exception_handler_inner(uint32_t *sp, bool registers_saved_on_stack);
|
||||||
|
static void __attribute__((noinline)) __attribute__((noreturn)) abort_handler_inner(uint32_t *caller, uint32_t *sp);
|
||||||
|
|
||||||
|
static IRAM_DATA fatal_exception_handler_fn fatal_exception_handler_inner = standard_fatal_exception_handler_inner;
|
||||||
|
|
||||||
|
/* fatal_exception_handler called from any unhandled user exception
|
||||||
|
*
|
||||||
|
* (similar to a hard fault on other processor architectures)
|
||||||
|
*
|
||||||
|
* This function is run from IRAM, but the majority of the handler
|
||||||
|
* runs from flash after fatal_handler_prelude ensures it is mapped
|
||||||
|
* safely.
|
||||||
|
*/
|
||||||
|
void IRAM __attribute__((noreturn)) fatal_exception_handler(uint32_t *sp, bool registers_saved_on_stack) {
|
||||||
|
fatal_handler_prelude();
|
||||||
|
fatal_exception_handler_fn inner_fn = fatal_exception_handler_inner;
|
||||||
|
inner_fn(sp, registers_saved_on_stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Abort implementation
|
||||||
|
*
|
||||||
|
* Replaces the weak-linked abort implementation provided by newlib libc.
|
||||||
|
*
|
||||||
|
* Disable interrupts, enable flash mapping, dump stack & caller
|
||||||
|
* address, restart.
|
||||||
|
*
|
||||||
|
* This function is run from IRAM, but the majority of the abort
|
||||||
|
* handler runs from flash after fatal_handler_prelude ensures it is
|
||||||
|
* mapped safely.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void IRAM abort(void) {
|
||||||
|
uint32_t *sp, *caller;
|
||||||
|
RETADDR(caller);
|
||||||
|
/* abort() caller is one instruction before our return address */
|
||||||
|
caller = (uint32_t *)((intptr_t)caller - 3);
|
||||||
|
SP(sp);
|
||||||
|
fatal_handler_prelude();
|
||||||
|
abort_handler_inner(caller, sp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dump exception information from special function registers */
|
||||||
|
static void dump_excinfo(void) {
|
||||||
|
uint32_t exccause, epc1, epc2, epc3, excvaddr, depc, excsave1;
|
||||||
|
uint32_t excinfo[8];
|
||||||
|
|
||||||
|
RSR(exccause, exccause);
|
||||||
|
printf("Fatal exception (%d): \n", (int)exccause);
|
||||||
|
RSR(epc1, epc1);
|
||||||
|
RSR(epc2, epc2);
|
||||||
|
RSR(epc3, epc3);
|
||||||
|
RSR(excvaddr, excvaddr);
|
||||||
|
RSR(depc, depc);
|
||||||
|
RSR(excsave1, excsave1);
|
||||||
|
printf("%s=0x%08x\n", "epc1", epc1);
|
||||||
|
printf("%s=0x%08x\n", "epc2", epc2);
|
||||||
|
printf("%s=0x%08x\n", "epc3", epc3);
|
||||||
|
printf("%s=0x%08x\n", "excvaddr", excvaddr);
|
||||||
|
printf("%s=0x%08x\n", "depc", depc);
|
||||||
|
printf("%s=0x%08x\n", "excsave1", excsave1);
|
||||||
|
sdk_system_rtc_mem_read(0, excinfo, 32); // Why?
|
||||||
|
excinfo[0] = 2;
|
||||||
|
excinfo[1] = exccause;
|
||||||
|
excinfo[2] = epc1;
|
||||||
|
excinfo[3] = epc2;
|
||||||
|
excinfo[4] = epc3;
|
||||||
|
excinfo[5] = excvaddr;
|
||||||
|
excinfo[6] = depc;
|
||||||
|
excinfo[7] = excsave1;
|
||||||
|
sdk_system_rtc_mem_write(0, excinfo, 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* dump stack memory (frames above sp) to stdout
|
||||||
|
|
||||||
|
There's a lot of smart stuff we could do while dumping stack
|
||||||
|
but for now we just dump what looks like our stack region.
|
||||||
|
|
||||||
|
Probably dumps more memory than it needs to, the first instance of
|
||||||
|
0xa5a5a5a5 probably constitutes the end of our stack.
|
||||||
|
*/
|
||||||
|
void dump_stack(uint32_t *sp) {
|
||||||
|
printf("\nStack: SP=%p\n", sp);
|
||||||
|
for(uint32_t *p = sp; p < sp + 32; p += 4) {
|
||||||
|
if((intptr_t)p >= 0x3fffc000) {
|
||||||
|
break; /* approximate end of RAM */
|
||||||
|
}
|
||||||
|
printf("%p: %08x %08x %08x %08x\n", p, p[0], p[1], p[2], p[3]);
|
||||||
|
if(p[0] == 0xa5a5a5a5 && p[1] == 0xa5a5a5a5
|
||||||
|
&& p[2] == 0xa5a5a5a5 && p[3] == 0xa5a5a5a5) {
|
||||||
|
break; /* FreeRTOS uses this pattern to mark untouched stack space */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dump normal registers that were stored above 'sp'
|
||||||
|
by the exception handler preamble
|
||||||
|
*/
|
||||||
|
void dump_registers_in_exception_handler(uint32_t *sp) {
|
||||||
|
uint32_t excsave1;
|
||||||
|
uint32_t *saved = sp - (0x50 / sizeof(uint32_t));
|
||||||
|
printf("Registers:\n");
|
||||||
|
RSR(excsave1, excsave1);
|
||||||
|
printf("a0 %08x ", excsave1);
|
||||||
|
printf("a1 %08x ", (intptr_t)sp);
|
||||||
|
for(int a = 2; a < 14; a++) {
|
||||||
|
printf("a%-2d %08x%c", a, saved[a+3], a == 3 || a == 7 || a == 11 ? '\n':' ');
|
||||||
|
}
|
||||||
|
printf("SAR %08x\n", saved[0x13]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __attribute__((noreturn)) post_crash_reset(void) {
|
||||||
|
uart_flush_txfifo(0);
|
||||||
|
uart_flush_txfifo(1);
|
||||||
|
sdk_system_restart_in_nmi();
|
||||||
|
while(1) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prelude ensures exceptions/NMI off and flash is mapped, allowing
|
||||||
|
calls to non-IRAM functions.
|
||||||
|
*/
|
||||||
|
static void IRAM fatal_handler_prelude(void) {
|
||||||
|
if (!sdk_NMIIrqIsOn) {
|
||||||
|
vPortEnterCritical();
|
||||||
|
do {
|
||||||
|
DPORT.DPORT0 &= 0xffffffe0;
|
||||||
|
} while (DPORT.DPORT0 & 0x00000001);
|
||||||
|
}
|
||||||
|
Cache_Read_Disable();
|
||||||
|
Cache_Read_Enable(0, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Main part of fatal exception handler, is run from flash to save
|
||||||
|
some IRAM.
|
||||||
|
*/
|
||||||
|
static void standard_fatal_exception_handler_inner(uint32_t *sp, bool registers_saved_on_stack) {
|
||||||
|
/* Replace the fatal exception handler 'inner' function so we
|
||||||
|
don't end up in a crash loop if this handler crashes. */
|
||||||
|
fatal_exception_handler_inner = second_fatal_exception_handler_inner;
|
||||||
|
dump_excinfo();
|
||||||
|
if (sp) {
|
||||||
|
if (registers_saved_on_stack) {
|
||||||
|
dump_registers_in_exception_handler(sp);
|
||||||
|
}
|
||||||
|
dump_stack(sp);
|
||||||
|
}
|
||||||
|
dump_heapinfo();
|
||||||
|
post_crash_reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is the exception handler that gets called if a crash occurs inside the standard handler,
|
||||||
|
so we don't end up in a crash loop. It doesn't rely on contents of stack or heap.
|
||||||
|
*/
|
||||||
|
static void second_fatal_exception_handler_inner(uint32_t *sp, bool registers_saved_on_stack) {
|
||||||
|
dump_excinfo();
|
||||||
|
printf("Second fatal exception occured inside fatal exception handler. Can't continue.\n");
|
||||||
|
post_crash_reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump_heapinfo(void)
|
||||||
|
{
|
||||||
|
extern char _heap_start;
|
||||||
|
extern uint32_t xPortSupervisorStackPointer;
|
||||||
|
struct mallinfo mi = mallinfo();
|
||||||
|
uint32_t brk_val = (uint32_t) sbrk(0);
|
||||||
|
uint32_t sp = xPortSupervisorStackPointer;
|
||||||
|
if(sp == 0) {
|
||||||
|
SP(sp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Total free heap is all memory that could be allocated via
|
||||||
|
malloc (assuming fragmentation doesn't become a problem) */
|
||||||
|
printf("\nFree Heap: %d\n", sp - brk_val + mi.fordblks);
|
||||||
|
|
||||||
|
/* delta between brk & supervisor sp is the contiguous memory
|
||||||
|
region that is available to be put into heap space via
|
||||||
|
brk(). */
|
||||||
|
printf("_heap_start %p brk 0x%08x supervisor sp 0x%08x sp-brk %d bytes\n",
|
||||||
|
&_heap_start, brk_val, sp, sp-brk_val);
|
||||||
|
|
||||||
|
/* arena/fordblks/uordblks determines the amount of free space
|
||||||
|
inside the heap region already added via brk(). May be
|
||||||
|
fragmented.
|
||||||
|
|
||||||
|
The values in parentheses are the values used internally by
|
||||||
|
nano-mallocr.c, the field names outside parentheses are the
|
||||||
|
POSIX compliant field names of the mallinfo structure.
|
||||||
|
|
||||||
|
"arena" should be equal to brk-_heap_start ie total size available.
|
||||||
|
*/
|
||||||
|
printf("arena (total_size) %d fordblks (free_size) %d uordblocks (used_size) %d\n",
|
||||||
|
mi.arena, mi.fordblks, mi.uordblks);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Main part of abort handler, can be run from flash to save some
|
||||||
|
IRAM.
|
||||||
|
*/
|
||||||
|
static void abort_handler_inner(uint32_t *caller, uint32_t *sp) {
|
||||||
|
printf("abort() invoked at %p.\n", caller);
|
||||||
|
dump_stack(sp);
|
||||||
|
dump_heapinfo();
|
||||||
|
post_crash_reset();
|
||||||
|
}
|
|
@ -9,6 +9,8 @@
|
||||||
|
|
||||||
_xt_isr isr[16];
|
_xt_isr isr[16];
|
||||||
|
|
||||||
|
bool esp_in_isr;
|
||||||
|
|
||||||
void IRAM _xt_isr_attach(uint8_t i, _xt_isr func)
|
void IRAM _xt_isr_attach(uint8_t i, _xt_isr func)
|
||||||
{
|
{
|
||||||
isr[i] = func;
|
isr[i] = func;
|
||||||
|
@ -20,6 +22,8 @@ void IRAM _xt_isr_attach(uint8_t i, _xt_isr func)
|
||||||
*/
|
*/
|
||||||
uint16_t IRAM _xt_isr_handler(uint16_t intset)
|
uint16_t IRAM _xt_isr_handler(uint16_t intset)
|
||||||
{
|
{
|
||||||
|
esp_in_isr = true;
|
||||||
|
|
||||||
/* WDT has highest priority (occasional WDT resets otherwise) */
|
/* WDT has highest priority (occasional WDT resets otherwise) */
|
||||||
if(intset & BIT(INUM_WDT)) {
|
if(intset & BIT(INUM_WDT)) {
|
||||||
_xt_clear_ints(BIT(INUM_WDT));
|
_xt_clear_ints(BIT(INUM_WDT));
|
||||||
|
@ -35,5 +39,7 @@ uint16_t IRAM _xt_isr_handler(uint16_t intset)
|
||||||
intset -= mask;
|
intset -= mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
esp_in_isr = false;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,9 @@ DebugExceptionVector:
|
||||||
.type DebugExceptionVector, @function
|
.type DebugExceptionVector, @function
|
||||||
|
|
||||||
wsr a0, excsave2
|
wsr a0, excsave2
|
||||||
call0 sdk_user_fatal_exception_handler
|
mov a2, a1
|
||||||
|
movi a3, 0
|
||||||
|
call0 fatal_exception_handler
|
||||||
rfi 2
|
rfi 2
|
||||||
|
|
||||||
.org VecBase + 0x20
|
.org VecBase + 0x20
|
||||||
|
@ -81,7 +83,9 @@ KernelExceptionVector:
|
||||||
.type KernelExceptionVector, @function
|
.type KernelExceptionVector, @function
|
||||||
|
|
||||||
break 1, 0
|
break 1, 0
|
||||||
call0 sdk_user_fatal_exception_handler
|
mov a2, a1
|
||||||
|
movi a3, 0
|
||||||
|
call0 fatal_exception_handler
|
||||||
rfe
|
rfe
|
||||||
|
|
||||||
.org VecBase + 0x50
|
.org VecBase + 0x50
|
||||||
|
@ -98,7 +102,9 @@ DoubleExceptionVector:
|
||||||
.type DoubleExceptionVector, @function
|
.type DoubleExceptionVector, @function
|
||||||
|
|
||||||
break 1, 4
|
break 1, 4
|
||||||
call0 sdk_user_fatal_exception_handler
|
mov a2, a1
|
||||||
|
movi a3, 0
|
||||||
|
call0 fatal_exception_handler
|
||||||
|
|
||||||
/* Reset vector at offset 0x80 is unused, as vecbase gets reset to mask ROM
|
/* Reset vector at offset 0x80 is unused, as vecbase gets reset to mask ROM
|
||||||
* vectors on chip reset. */
|
* vectors on chip reset. */
|
||||||
|
@ -251,11 +257,13 @@ LoadStoreErrorHandler:
|
||||||
* will have correct values */
|
* will have correct values */
|
||||||
wsr a0, sar
|
wsr a0, sar
|
||||||
l32i a0, sp, 0
|
l32i a0, sp, 0
|
||||||
l32i a2, sp, 0x08
|
/*l32i a2, sp, 0x08*/
|
||||||
l32i a3, sp, 0x0c
|
l32i a3, sp, 0x0c
|
||||||
l32i a4, sp, 0x10
|
l32i a4, sp, 0x10
|
||||||
rsr a1, excsave1
|
rsr a1, excsave1
|
||||||
call0 sdk_user_fatal_exception_handler
|
mov a2, a1
|
||||||
|
movi a3, 0
|
||||||
|
call0 fatal_exception_handler
|
||||||
|
|
||||||
.balign 4
|
.balign 4
|
||||||
.LSE_assign_a1:
|
.LSE_assign_a1:
|
||||||
|
@ -515,7 +523,9 @@ UserExceptionHandler:
|
||||||
.literal_position
|
.literal_position
|
||||||
.LUserFailOtherExceptionCause:
|
.LUserFailOtherExceptionCause:
|
||||||
break 1, 1
|
break 1, 1
|
||||||
call0 sdk_user_fatal_exception_handler
|
addi a2, a1, 0x50 /* UserExceptionHandler pushes stack down 0x50 */
|
||||||
|
movi a3, 1
|
||||||
|
call0 fatal_exception_handler
|
||||||
|
|
||||||
/* _xt_user_exit is pushed onto the stack as part of the user exception handler,
|
/* _xt_user_exit is pushed onto the stack as part of the user exception handler,
|
||||||
restores same set registers which were saved there and returns from exception */
|
restores same set registers which were saved there and returns from exception */
|
||||||
|
|
|
@ -65,18 +65,16 @@
|
||||||
*/
|
*/
|
||||||
#define IRAM __attribute__((section(".iram1.text")))
|
#define IRAM __attribute__((section(".iram1.text")))
|
||||||
|
|
||||||
/* Use this macro to place read-only data into Instruction RAM (IRAM)
|
/* Use this macro to place data into Instruction RAM (IRAM)
|
||||||
instead of loaded into rodata which resides in DRAM.
|
instead of loaded into rodata which resides in DRAM.
|
||||||
|
|
||||||
|
(IRAM can also be written to as necessary.)
|
||||||
|
|
||||||
This may be useful to free up data RAM. However all data read from
|
This may be useful to free up data RAM. However all data read from
|
||||||
the instruction space must be 32-bit aligned word reads
|
the instruction space must be 32-bit aligned word reads
|
||||||
(non-aligned reads will use an interrupt routine to "fix" them and
|
(non-aligned reads will use an interrupt routine to "fix" them and
|
||||||
still work, but are very slow..
|
still work, but are very slow..
|
||||||
*/
|
*/
|
||||||
#ifdef __cplusplus
|
#define IRAM_DATA __attribute__((section(".iram1.rodata")))
|
||||||
#define IRAM_DATA __attribute__((section(".iram1.rodata")))
|
|
||||||
#else
|
|
||||||
#define IRAM_DATA __attribute__((section(".iram1.rodata"))) const
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
25
core/include/debug_dumps.h
Normal file
25
core/include/debug_dumps.h
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
/* Functions for dumping status/debug output/etc, including fatal
|
||||||
|
* exception handling.
|
||||||
|
*
|
||||||
|
* Part of esp-open-rtos
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015-2016 Superhouse Automation Pty Ltd
|
||||||
|
* BSD Licensed as described in the file LICENSE
|
||||||
|
*/
|
||||||
|
#ifndef _DEBUG_DUMPS_H
|
||||||
|
#define _DEBUG_DUMPS_H
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* Dump stack memory to stdout, starting from stack pointer address sp. */
|
||||||
|
void dump_stack(uint32_t *sp);
|
||||||
|
|
||||||
|
/* Dump heap statistics to stdout */
|
||||||
|
void dump_heapinfo(void);
|
||||||
|
|
||||||
|
/* Called from exception_vectors.S when a fatal exception occurs.
|
||||||
|
|
||||||
|
Probably not useful to be called in other contexts.
|
||||||
|
*/
|
||||||
|
void __attribute__((noreturn)) fatal_exception_handler(uint32_t *sp, bool registers_saved_on_stack);
|
||||||
|
|
||||||
|
#endif
|
|
@ -14,6 +14,20 @@
|
||||||
// GCC macros for reading, writing, and exchanging Xtensa processor special
|
// GCC macros for reading, writing, and exchanging Xtensa processor special
|
||||||
// registers:
|
// registers:
|
||||||
|
|
||||||
|
/* Read stack pointer to variable.
|
||||||
|
*
|
||||||
|
* Note that the compiler will push a stack frame (minimum 16 bytes)
|
||||||
|
* in the prelude of a C function that calls any other functions.
|
||||||
|
*/
|
||||||
|
#define SP(var) asm volatile ("mov %0, a1" : "=r" (var))
|
||||||
|
|
||||||
|
/* Read the function return address to a variable.
|
||||||
|
*
|
||||||
|
* Depends on the containing function being simple enough that a0 is
|
||||||
|
* being used as a working register.
|
||||||
|
*/
|
||||||
|
#define RETADDR(var) asm volatile ("mov %0, a0" : "=r" (var))
|
||||||
|
|
||||||
#define RSR(var, reg) asm volatile ("rsr %0, " #reg : "=r" (var));
|
#define RSR(var, reg) asm volatile ("rsr %0, " #reg : "=r" (var));
|
||||||
#define WSR(var, reg) asm volatile ("wsr %0, " #reg : : "r" (var));
|
#define WSR(var, reg) asm volatile ("wsr %0, " #reg : : "r" (var));
|
||||||
#define XSR(var, reg) asm volatile ("xsr %0, " #reg : "+r" (var));
|
#define XSR(var, reg) asm volatile ("xsr %0, " #reg : "+r" (var));
|
||||||
|
|
|
@ -25,7 +25,7 @@ IRAM caddr_t _sbrk_r (struct _reent *r, int incr)
|
||||||
if (heap_end + incr > stack_ptr)
|
if (heap_end + incr > stack_ptr)
|
||||||
{
|
{
|
||||||
_write (1, "_sbrk: Heap collided with stack\n", 32);
|
_write (1, "_sbrk: Heap collided with stack\n", 32);
|
||||||
while(1) {}
|
abort();
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
heap_end += incr;
|
heap_end += incr;
|
||||||
|
|
|
@ -305,7 +305,7 @@ static void test_system_interaction()
|
||||||
}
|
}
|
||||||
uint32_t ticks = xTaskGetTickCount() - start;
|
uint32_t ticks = xTaskGetTickCount() - start;
|
||||||
printf("Timer interaction test PASSED after %dms.\n", ticks*portTICK_RATE_MS);
|
printf("Timer interaction test PASSED after %dms.\n", ticks*portTICK_RATE_MS);
|
||||||
while(1) {}
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The following "sanity tests" are designed to try to execute every code path
|
/* The following "sanity tests" are designed to try to execute every code path
|
||||||
|
|
|
@ -115,7 +115,7 @@ void http_get_task(void *pvParameters)
|
||||||
strlen(pers))) != 0)
|
strlen(pers))) != 0)
|
||||||
{
|
{
|
||||||
printf(" failed\n ! mbedtls_ctr_drbg_seed returned %d\n", ret);
|
printf(" failed\n ! mbedtls_ctr_drbg_seed returned %d\n", ret);
|
||||||
while(1) {} /* todo: replace with abort() */
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
printf(" ok\n");
|
printf(" ok\n");
|
||||||
|
@ -129,7 +129,7 @@ void http_get_task(void *pvParameters)
|
||||||
if(ret < 0)
|
if(ret < 0)
|
||||||
{
|
{
|
||||||
printf(" failed\n ! mbedtls_x509_crt_parse returned -0x%x\n\n", -ret);
|
printf(" failed\n ! mbedtls_x509_crt_parse returned -0x%x\n\n", -ret);
|
||||||
while(1) {} /* todo: replace with abort() */
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
printf(" ok (%d skipped)\n", ret);
|
printf(" ok (%d skipped)\n", ret);
|
||||||
|
@ -138,7 +138,7 @@ void http_get_task(void *pvParameters)
|
||||||
if((ret = mbedtls_ssl_set_hostname(&ssl, WEB_SERVER)) != 0)
|
if((ret = mbedtls_ssl_set_hostname(&ssl, WEB_SERVER)) != 0)
|
||||||
{
|
{
|
||||||
printf(" failed\n ! mbedtls_ssl_set_hostname returned %d\n\n", ret);
|
printf(" failed\n ! mbedtls_ssl_set_hostname returned %d\n\n", ret);
|
||||||
while(1) {} /* todo: replace with abort() */
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -85,7 +85,7 @@ void tls_server_task(void *pvParameters)
|
||||||
strlen(pers))) != 0)
|
strlen(pers))) != 0)
|
||||||
{
|
{
|
||||||
printf(" failed\n ! mbedtls_ctr_drbg_seed returned %d\n", ret);
|
printf(" failed\n ! mbedtls_ctr_drbg_seed returned %d\n", ret);
|
||||||
while(1) {} /* todo: replace with abort() */
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
printf(" ok\n");
|
printf(" ok\n");
|
||||||
|
@ -99,7 +99,7 @@ void tls_server_task(void *pvParameters)
|
||||||
if(ret < 0)
|
if(ret < 0)
|
||||||
{
|
{
|
||||||
printf(" failed\n ! mbedtls_x509_crt_parse returned -0x%x\n\n", -ret);
|
printf(" failed\n ! mbedtls_x509_crt_parse returned -0x%x\n\n", -ret);
|
||||||
while(1) {} /* todo: replace with abort() */
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
printf(" ok (%d skipped)\n", ret);
|
printf(" ok (%d skipped)\n", ret);
|
||||||
|
@ -109,7 +109,7 @@ void tls_server_task(void *pvParameters)
|
||||||
if(ret != 0)
|
if(ret != 0)
|
||||||
{
|
{
|
||||||
printf(" failed\n ! mbedtls_pk_parse_key returned - 0x%x\n\n", -ret);
|
printf(" failed\n ! mbedtls_pk_parse_key returned - 0x%x\n\n", -ret);
|
||||||
while(1) { } /*todo: replace with abort() */
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
printf(" ok\n");
|
printf(" ok\n");
|
||||||
|
@ -134,7 +134,7 @@ void tls_server_task(void *pvParameters)
|
||||||
if( ( ret = mbedtls_ssl_conf_own_cert( &conf, &srvcert, &pkey ) ) != 0 )
|
if( ( ret = mbedtls_ssl_conf_own_cert( &conf, &srvcert, &pkey ) ) != 0 )
|
||||||
{
|
{
|
||||||
printf( " failed\n ! mbedtls_ssl_conf_own_cert returned %d\n\n", ret );
|
printf( " failed\n ! mbedtls_ssl_conf_own_cert returned %d\n\n", ret );
|
||||||
while(1) { }
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg);
|
mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg);
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -94,7 +94,7 @@ typedef int sys_prot_t;
|
||||||
_Pragma("GCC diagnostic pop") \
|
_Pragma("GCC diagnostic pop") \
|
||||||
} while(0)
|
} while(0)
|
||||||
#define LWIP_PLATFORM_ASSERT(x) do { printf("Assertion \"%s\" failed at line %d in %s\n", \
|
#define LWIP_PLATFORM_ASSERT(x) do { printf("Assertion \"%s\" failed at line %d in %s\n", \
|
||||||
x, __LINE__, __FILE__); while(1) {} } while(0)
|
x, __LINE__, __FILE__); abort(); } while(0)
|
||||||
|
|
||||||
#define LWIP_ERROR(message, expression, handler) do { if (!(expression)) { \
|
#define LWIP_ERROR(message, expression, handler) do { if (!(expression)) { \
|
||||||
printf("Assertion \"%s\" failed at line %d in %s\n", message, __LINE__, __FILE__); \
|
printf("Assertion \"%s\" failed at line %d in %s\n", message, __LINE__, __FILE__); \
|
||||||
|
|
1177
lwip/sys_arch.c
1177
lwip/sys_arch.c
File diff suppressed because it is too large
Load diff
105
utils/filteroutput.py
Executable file
105
utils/filteroutput.py
Executable file
|
@ -0,0 +1,105 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
#
|
||||||
|
# A thin Python wrapper around addr2line, can monitor esp-open-rtos
|
||||||
|
# output and uses gdb to convert any suitable looking hex numbers
|
||||||
|
# found in the output into function and line numbers.
|
||||||
|
#
|
||||||
|
# Works with a serial port if the --port option is supplied.
|
||||||
|
# Otherwise waits for input on stdin.
|
||||||
|
#
|
||||||
|
import serial
|
||||||
|
import argparse
|
||||||
|
import re
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
import subprocess
|
||||||
|
import termios
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
# Try looking up anything in the executable address space
|
||||||
|
RE_EXECADDR = r"(0x)?40([0-9]|[a-z]){6}"
|
||||||
|
|
||||||
|
def find_elf_file():
|
||||||
|
out_files = []
|
||||||
|
for top,_,files in os.walk('.', followlinks=False):
|
||||||
|
for f in files:
|
||||||
|
if f.endswith(".out"):
|
||||||
|
out_files.append(os.path.join(top,f))
|
||||||
|
if len(out_files) == 1:
|
||||||
|
return out_files[0]
|
||||||
|
elif len(out_files) > 1:
|
||||||
|
print("Found multiple .out files: %s. Please specify one with the --elf option." % out_files)
|
||||||
|
else:
|
||||||
|
print("No .out file found under current directory. Please specify one with the --elf option.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description='esp-open-rtos output filter tool', prog='filteroutput')
|
||||||
|
parser.add_argument(
|
||||||
|
'--elf', '-e',
|
||||||
|
help="ELF file (*.out file) to load symbols from (if not supplied, will search for one)"),
|
||||||
|
parser.add_argument(
|
||||||
|
'--port', '-p',
|
||||||
|
help='Serial port to monitor (will monitor stdin if None)',
|
||||||
|
default=None)
|
||||||
|
parser.add_argument(
|
||||||
|
'--baud', '-b',
|
||||||
|
help='Baud rate for serial port',
|
||||||
|
type=int,
|
||||||
|
default=74880)
|
||||||
|
parser.add_argument(
|
||||||
|
'--reset-on-connect', '-r',
|
||||||
|
help='Reset ESP8266 (via DTR) on serial connect. (Linux resets even if not set, except when using NodeMCU-style auto-reset circuit.)',
|
||||||
|
action='store_true')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.elf is None:
|
||||||
|
args.elf = find_elf_file()
|
||||||
|
elif not os.path.exists(args.elf):
|
||||||
|
print("ELF file '%s' not found" % args.elf)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if args.port is not None:
|
||||||
|
print("Opening %s at %dbps..." % (args.port, args.baud))
|
||||||
|
port = serial.Serial(args.port, baudrate=args.baud)
|
||||||
|
if args.reset_on_connect:
|
||||||
|
print("Resetting...")
|
||||||
|
port.setDTR(False)
|
||||||
|
time.sleep(0.1)
|
||||||
|
port.setDTR(True)
|
||||||
|
|
||||||
|
else:
|
||||||
|
print("Reading from stdin...")
|
||||||
|
port = sys.stdin
|
||||||
|
# disable echo
|
||||||
|
try:
|
||||||
|
old_attr = termios.tcgetattr(sys.stdin.fileno())
|
||||||
|
attr = termios.tcgetattr(sys.stdin.fileno())
|
||||||
|
attr[3] = attr[3] & ~termios.ECHO
|
||||||
|
termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, attr)
|
||||||
|
except termios.error:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
line = port.readline()
|
||||||
|
if line == '':
|
||||||
|
break
|
||||||
|
print(line.strip())
|
||||||
|
for match in re.finditer(RE_EXECADDR, line, re.IGNORECASE):
|
||||||
|
addr = match.group(0)
|
||||||
|
if not addr.startswith("0x"):
|
||||||
|
addr = "0x"+addr
|
||||||
|
# keeping addr2line and feeding it addresses on stdin didn't seem to work smoothly
|
||||||
|
addr2line = subprocess.check_output(["xtensa-lx106-elf-addr2line","-pfia","-e","%s" % args.elf, addr], cwd=".").strip()
|
||||||
|
if not addr2line.endswith(": ?? ??:0"):
|
||||||
|
print("\n%s\n" % addr2line.strip())
|
||||||
|
finally:
|
||||||
|
if args.port is None:
|
||||||
|
# restore echo
|
||||||
|
termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, old_attr)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
Loading…
Reference in a new issue