Merge pull request #137 from SuperHouse/feature/better_crash_dumps

Better crash dumps
This commit is contained in:
Angus Gratton 2016-05-17 09:38:19 +10:00
commit 3ba19d7c4e
19 changed files with 1013 additions and 664 deletions

View file

@ -73,6 +73,7 @@
#include <malloc.h>
#include <unistd.h>
#include <stdio.h>
#include <xtensa_ops.h>
#include "FreeRTOS.h"
#include "task.h"
@ -87,7 +88,7 @@ char level1_int_disabled;
After tasks start, task stacks are all allocated from the heap and
FreeRTOS checks for stack overflow.
*/
static uint32_t xPortSupervisorStackPointer;
uint32_t xPortSupervisorStackPointer;
/*
* Stack initialization
@ -220,7 +221,7 @@ size_t xPortGetFreeHeapSize( void )
uint32_t sp = xPortSupervisorStackPointer;
if(sp == 0) /* scheduler not started */
__asm__ __volatile__ ("mov %0, a1\n" : "=a"(sp));
SP(sp);
return sp - brk_val + mi.fordblks;
}

View file

@ -75,6 +75,9 @@ FLAVOR ?= release # or debug
# Compiler names, etc. assume gdb
CROSS ?= xtensa-lx106-elf-
# Path to the filteroutput.py tool
FILTEROUTPUT ?= $(ROOT)/utils/filteroutput.py
AR = $(CROSS)ar
CC = $(CROSS)gcc
CPP = $(CROSS)cpp
@ -385,7 +388,7 @@ size: $(PROGRAM_OUT)
$(Q) $(CROSS)size --format=sysv $(PROGRAM_OUT)
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
# environment without causing weird side effects

View file

@ -39,8 +39,6 @@ void user_init(void);
#define RTCMEM_BACKUP_PHY_VER 31
#define RTCMEM_SYSTEM_PP_VER 62
#define halt() while (1) {}
extern uint32_t _bss_start;
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 init_networking(sdk_phy_info_t *phy_info, uint8_t *mac_addr);
static void init_g_ic(void);
static void dump_excinfo(void);
static void user_start_phase2(void);
static void dump_flash_sector(uint32_t start_sector, uint32_t length);
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)) {
//FIXME: do we really need this check?
printf("Firmware ONLY supports ESP8266!!!\n");
halt();
abort();
}
if (otp_id0 == 0 && otp_id1 == 0) {
printf("empty otp\n");
halt();
abort();
}
if (otp_flags & 0x1000) {
// 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);
}
// .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) {
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) {
if (sdk_register_chipv6_phy(phy_info)) {
printf("FATAL: sdk_register_chipv6_phy failed");
halt();
abort();
}
uart_set_baud(0, 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
void sdk_wdt_init(void) {
WDT.CTRL &= ~WDT_CTRL_ENABLE;

231
core/debug_dumps.c Normal file
View 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();
}

View file

@ -9,6 +9,8 @@
_xt_isr isr[16];
bool esp_in_isr;
void IRAM _xt_isr_attach(uint8_t i, _xt_isr 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)
{
esp_in_isr = true;
/* WDT has highest priority (occasional WDT resets otherwise) */
if(intset & BIT(INUM_WDT)) {
_xt_clear_ints(BIT(INUM_WDT));
@ -35,5 +39,7 @@ uint16_t IRAM _xt_isr_handler(uint16_t intset)
intset -= mask;
}
esp_in_isr = false;
return 0;
}

View file

@ -68,7 +68,9 @@ DebugExceptionVector:
.type DebugExceptionVector, @function
wsr a0, excsave2
call0 sdk_user_fatal_exception_handler
mov a2, a1
movi a3, 0
call0 fatal_exception_handler
rfi 2
.org VecBase + 0x20
@ -81,7 +83,9 @@ KernelExceptionVector:
.type KernelExceptionVector, @function
break 1, 0
call0 sdk_user_fatal_exception_handler
mov a2, a1
movi a3, 0
call0 fatal_exception_handler
rfe
.org VecBase + 0x50
@ -98,7 +102,9 @@ DoubleExceptionVector:
.type DoubleExceptionVector, @function
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
* vectors on chip reset. */
@ -251,11 +257,13 @@ LoadStoreErrorHandler:
* will have correct values */
wsr a0, sar
l32i a0, sp, 0
l32i a2, sp, 0x08
/*l32i a2, sp, 0x08*/
l32i a3, sp, 0x0c
l32i a4, sp, 0x10
rsr a1, excsave1
call0 sdk_user_fatal_exception_handler
mov a2, a1
movi a3, 0
call0 fatal_exception_handler
.balign 4
.LSE_assign_a1:
@ -515,7 +523,9 @@ UserExceptionHandler:
.literal_position
.LUserFailOtherExceptionCause:
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,
restores same set registers which were saved there and returns from exception */

View file

@ -65,18 +65,16 @@
*/
#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.
(IRAM can also be written to as necessary.)
This may be useful to free up data RAM. However all data read from
the instruction space must be 32-bit aligned word reads
(non-aligned reads will use an interrupt routine to "fix" them and
still work, but are very slow..
*/
#ifdef __cplusplus
#define IRAM_DATA __attribute__((section(".iram1.rodata")))
#else
#define IRAM_DATA __attribute__((section(".iram1.rodata"))) const
#endif
#define IRAM_DATA __attribute__((section(".iram1.rodata")))
#endif

View 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

View file

@ -14,6 +14,20 @@
// GCC macros for reading, writing, and exchanging Xtensa processor special
// 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 WSR(var, reg) asm volatile ("wsr %0, " #reg : : "r" (var));
#define XSR(var, reg) asm volatile ("xsr %0, " #reg : "+r" (var));

View file

@ -25,7 +25,7 @@ IRAM caddr_t _sbrk_r (struct _reent *r, int incr)
if (heap_end + incr > stack_ptr)
{
_write (1, "_sbrk: Heap collided with stack\n", 32);
while(1) {}
abort();
}
*/
heap_end += incr;

View file

@ -305,7 +305,7 @@ static void test_system_interaction()
}
uint32_t ticks = xTaskGetTickCount() - start;
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

View file

@ -115,7 +115,7 @@ void http_get_task(void *pvParameters)
strlen(pers))) != 0)
{
printf(" failed\n ! mbedtls_ctr_drbg_seed returned %d\n", ret);
while(1) {} /* todo: replace with abort() */
abort();
}
printf(" ok\n");
@ -129,7 +129,7 @@ void http_get_task(void *pvParameters)
if(ret < 0)
{
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);
@ -138,7 +138,7 @@ void http_get_task(void *pvParameters)
if((ret = mbedtls_ssl_set_hostname(&ssl, WEB_SERVER)) != 0)
{
printf(" failed\n ! mbedtls_ssl_set_hostname returned %d\n\n", ret);
while(1) {} /* todo: replace with abort() */
abort();
}
/*

View file

@ -85,7 +85,7 @@ void tls_server_task(void *pvParameters)
strlen(pers))) != 0)
{
printf(" failed\n ! mbedtls_ctr_drbg_seed returned %d\n", ret);
while(1) {} /* todo: replace with abort() */
abort();
}
printf(" ok\n");
@ -99,7 +99,7 @@ void tls_server_task(void *pvParameters)
if(ret < 0)
{
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);
@ -109,7 +109,7 @@ void tls_server_task(void *pvParameters)
if(ret != 0)
{
printf(" failed\n ! mbedtls_pk_parse_key returned - 0x%x\n\n", -ret);
while(1) { } /*todo: replace with abort() */
abort();
}
printf(" ok\n");
@ -134,7 +134,7 @@ void tls_server_task(void *pvParameters)
if( ( ret = mbedtls_ssl_conf_own_cert( &conf, &srvcert, &pkey ) ) != 0 )
{
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);

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -94,7 +94,7 @@ typedef int sys_prot_t;
_Pragma("GCC diagnostic pop") \
} while(0)
#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)) { \
printf("Assertion \"%s\" failed at line %d in %s\n", message, __LINE__, __FILE__); \

File diff suppressed because it is too large Load diff

105
utils/filteroutput.py Executable file
View 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()