Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/gdbstub continuation crash #1671

Merged
merged 14 commits into from
Apr 14, 2019
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Sming/SmingCore/SimpleTimer.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ extern "C" {
*/
#define MAX_OS_TIMER_INTERVAL_US 268435000

typedef os_timer_func_t* SimpleTimerCallback;

class SimpleTimer
{
public:
Expand Down Expand Up @@ -89,7 +91,7 @@ class SimpleTimer
* @param interrupt Function to be called on timer trigger
* @note Classic c-type callback method
*/
void setCallback(os_timer_func_t callback, void* arg = nullptr)
void setCallback(SimpleTimerCallback callback, void* arg = nullptr)
{
stop();
ets_timer_setfn(&osTimer, callback, arg);
Expand Down
5 changes: 1 addition & 4 deletions Sming/appspecific/gdb/gdb_hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,7 @@ void dumpExceptionInfo()
"***** Fatal exception %u"),
reg.cause);
if(reg.cause <= EXCCAUSE_MAX) {
char name[32];
memcpy_P(name, exceptionNames[reg.cause], sizeof(name));
name[sizeof(name) - 1] = '\0';
m_printf(_F(" (%s)"), name);
m_printf(_F(" (%s)"), exceptionNames[reg.cause]);
}
m_puts("\r\n");

Expand Down
4 changes: 4 additions & 0 deletions Sming/appspecific/gdb/gdbstub-entry.S
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,10 @@ gdbstub_init_debug_entry:
ASATTR_GDBFN
.align 4
gdbstub_icount_ena_single_step:

//Make sure we're running at sufficiently high interrupt level to prevent early triggering
rsil a2,XCHAL_DEBUGLEVEL

movi a3, XCHAL_DEBUGLEVEL //Only count steps in non-debug mode
movi a2, -2
wsr a3, ICOUNTLEVEL
Expand Down
7 changes: 6 additions & 1 deletion Sming/gdb/GdbPacket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,19 @@
// Send the start of a packet; reset checksum calculation.
void ATTR_GDBEXTERNFN GdbPacket::start()
{
#if GDBSTUB_ENABLE_DEBUG
m_puts(_F("> PKT "));
#endif
gdbSendChar('$');
}

void ATTR_GDBEXTERNFN GdbPacket::end()
{
gdbSendChar('#');
writeHexByte(checksum);
debug_i("> PKT [%u]", packetLength);
#if GDBSTUB_ENABLE_DEBUG
m_puts("\r\n");
#endif
}

void ATTR_GDBEXTERNFN GdbPacket::writeChar(char c)
Expand Down
5 changes: 4 additions & 1 deletion Sming/gdb/gdbcmds
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Enable this if you want to log all traffic between GDB and the stub
# set remotelogfile gdb_rsp_logfile.txt
#set remotelogfile gdb_rsp_logfile.txt

# ESP8266 HW limits the number of breakpoints/watchpoints
set remote hardware-breakpoint-limit 1
Expand Down Expand Up @@ -29,6 +29,9 @@ file out/build/app_0.out
# See https://github.com/jcmvbkbc/esp-elf-rom
add-symbol-file out/build/bootrom.elf 0x40000000 -readnow

# Useful for lower-level debugging to see the result of stepping through assembler code
#set disassemble-next-line on

# Change the following to your serial port and baud
#set serial baud 115200
#target remote /dev/ttyUSB0
Expand Down
4 changes: 2 additions & 2 deletions Sming/gdb/gdbhostio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ void ATTR_GDBEXTERNFN gdbHandleHostIo(char* commandBuffer, unsigned cmdLen)
debug_i("File:pread(%d, %u, %u)", fd, count, offset);

packet.writeChar('F');
if(fileSeek(fd, offset, eSO_FileStart) == offset) {
if(fileSeek(fd, offset, eSO_FileStart) == int(offset)) {
count = fileRead(fd, commandBuffer, count);
if(int(count) >= 0) {
packet.writeHexWord16(count);
Expand All @@ -139,7 +139,7 @@ void ATTR_GDBEXTERNFN gdbHandleHostIo(char* commandBuffer, unsigned cmdLen)
debug_i("File:pwrite(%d, %u, %u)", fd, offset, size);

packet.writeChar('F');
if(fileSeek(fd, offset, eSO_FileStart) == offset) {
if(fileSeek(fd, offset, eSO_FileStart) == int(offset)) {
int count = fileWrite(fd, data, size);
if(count >= 0) {
packet.writeHexWord16(count);
Expand Down
13 changes: 2 additions & 11 deletions Sming/gdb/gdbstub-cfg.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,6 @@
#define GDBSTUB_ENABLE_DEBUG 0
#endif

/*
* The HardwareTimer defaults to non-maskable mode, and will continue to operate whilst paused.
* Set this value to 1 to disable during a debugging exception, and restore it afterwards.
* Note that to debug HardwareTimer callback routines, the timer must be initialise in maskable mode.
*/
#ifndef GDBSTUB_PAUSE_HARDWARE_TIMER
#define GDBSTUB_PAUSE_HARDWARE_TIMER 0
#endif

/*
* Espressif provide a patched version of GDB which emits only those registered present in the lx106.
* Set to 0 if an unpatched version of GDB is used.
Expand Down Expand Up @@ -142,7 +133,7 @@
* If undefined, calls will do nothing and return -1.
*/
#ifndef GDBSTUB_ENABLE_SYSCALL
#define GDBSTUB_ENABLE_SYSCALL 1
#define GDBSTUB_ENABLE_SYSCALL 0
#endif

/*
Expand Down Expand Up @@ -178,7 +169,7 @@
* Set to 0 to wait indefinitely.
*/
#ifndef GDBSTUB_UART_READ_TIMEOUT
#define GDBSTUB_UART_READ_TIMEOUT 2000
#define GDBSTUB_UART_READ_TIMEOUT 0
#endif

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, make GDBSTUB_ENABLE_SYSCALL to be 0 by default. This will help visual debuggers behave as expected. Allow also that setting to be changed during compilation so that adventurous developers can try it.

Copy link
Contributor Author

@mikee47 mikee47 Apr 14, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To keep the instructions simpler, how about we do this?

To build for normal (command-line GDB) use: make flash
To build for eclipse: make flash ENABLE_CONSOLE=0

The problem is specific to the LiveDebug sample.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use ENABLE_SYS_CONSOLE instead of ENABLE_CONSOLE. In addition I would prefer the syscall stuff to be disabled by default. For the LiveSample we can enable it by default by writing in the Makefile-user.mk.
When ENABLE_SYS_CONSOLE is enabled a warning message should be shown to inform the developers that this might interfere with visual debuggers, like Eclipse. And that it can be disabled using make ENABLE_SYS_CONSOLE=0.
What do you say?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or even better ENABLE_GDB_CONSOLE instead of ENABLE_CONSOLE. The name prompts that this is related to the special GDB console.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Allow also that setting to be changed during compilation so that adventurous developers can try it.

The simplest way is to just edit the gdbstub-cfg.h file directly to make changes as required (same goes for gdbcmds). I also have a note in the readme.md about using USER_CFLAGS to set these values for a particular project - I've done this for the LiveDebug sample as per your suggestions.

Are you propsing we add the abillty to change these directly from the make command line?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you proposing we add the ability to change these directly from the make command line?

Ok, I see. Then can you do the the following?

  1. GDBSTUB_ENABLE_SYSCALL=0 in Sming/gdb/gdbstub-cfg.h
  2. In samples/LiveDebug/Makefile-user.mk add
# Comment the line below if you want to disable the GDB system calls
USER_CFLAGS += -DGDBSTUB_ENABLE_SYSCALL=1
  1. Update the README in LiveDebug to mention that change.

/*
Expand Down
86 changes: 46 additions & 40 deletions Sming/gdb/gdbstub.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,11 @@ static ERRNO_T ATTR_GDBEXTERNFN writeMemoryBlock(uint32_t addr, const void* data
*/
static void ATTR_GDBEXTERNFN sendReason()
{
if(gdb_state.syscall == syscall_active) {
// GDB ignores this whilst processing a system call - we'll send when it's completed via 'F' response
return;
}

gdbFlushUserData();
GdbPacket packet;
packet.writeChar('T');
Expand Down Expand Up @@ -300,12 +305,18 @@ static unsigned ATTR_GDBEXTERNFN readCommand()
while((c = gdbReceiveChar()) != '#') { // end of packet, checksum follows
if(c < 0) {
gdbSendChar('-');
#if GDBSTUB_ENABLE_DEBUG
m_puts("\r\n");
debug_e("CMD TIMEOUT");
#endif
return 0;
}
if(c == '$') {
// Wut, restart packet?
#if GDBSTUB_ENABLE_DEBUG
m_puts("\r\n");
debug_e("Unexpected '$' received");
#endif
checksum = 0;
cmdLen = 0;
continue;
Expand All @@ -318,21 +329,26 @@ static unsigned ATTR_GDBEXTERNFN readCommand()
}
if(cmdLen >= MAX_COMMAND_LENGTH) {
// Received more than the size of the command buffer
#if GDBSTUB_ENABLE_DEBUG
m_puts("\r\n");
debug_e("Command '%c' buffer overflow", commandBuffer[0]);
#endif
return 0;
}
commandBuffer[cmdLen++] = c;
}
commandBuffer[cmdLen] = '\0';

#if GDBSTUB_ENABLE_DEBUG
debug_i("cmd '%c', len %u", commandBuffer[0], cmdLen);
#endif

// Read checksum and verify
char checksumChars[] = {char(gdbReceiveChar()), char(gdbReceiveChar()), '\0'};
const char* ptr = checksumChars;
auto receivedChecksum = GdbPacket::readHexValue(ptr);

#if GDBSTUB_ENABLE_DEBUG
m_puts("\r\n");
debug_i("cmd '%c', len %u", commandBuffer[0], cmdLen);
#endif

if(receivedChecksum == checksum) {
// Acknowledge the command
gdbSendChar('+');
Expand Down Expand Up @@ -423,14 +439,7 @@ static GdbResult ATTR_GDBEXTERNFN handleCommand(unsigned cmdLen)
auto regnum = GdbPacket::readHexValue(data);
auto regPtr = getSavedReg(regnum);
#if GDBSTUB_ENABLE_DEBUG
char regName[16];
/*
* @todo Trying to read flash here sometimes causes a LEVEL1 interrupt exception (even though
* we haven't hooked it?!) According to the docs. we need to look at the INTERRUPT register
* and INTENABLE to determine the actual cause.
*/
regName[0] = '\0';
// memcpy_P(regName, registerNames[regnum], sizeof(regName));
const char* regName = (regPtr == nullptr) ? PSTR("(unsupported)") : registerNames[regnum];
#endif

if(commandChar == 'p') { // read
Expand Down Expand Up @@ -542,14 +551,31 @@ static GdbResult ATTR_GDBEXTERNFN handleCommand(unsigned cmdLen)
#if GDBSTUB_ENABLE_SYSCALL
/*
* A file (or console) I/O request has finished.
*
* This can happen whilst the application is running, or when it has stopped due to
* exception or debug break.
*
* Whilst an I/O request is in progress, GDB will ignore anything we send so if already
* paused we need to call sendReason() again.
*
* If the user hit Ctrl+C then the syscall completes and returns true to tell us this.
*
* Note that GDB expects us to be paused during the entire 'F' request / reply operation,
* but that makes console reading a lot less useful for us so don't actually pause until
* the start of the response packet (indicated by DBGFLAG_PACKET_STARTED).
*/
case 'F':
if(gdb_syscall_complete(data)) {
// Ctrl+C was pressed
sendReason();
return ST_OK;
} else {
} else if(bitRead(gdb_state.flags, DBGFLAG_PACKET_STARTED)) {
// Paused to deal with packet response, continue running normally
return ST_CONT;
} else {
// Paused for some other reason, keep debugging
sendReason();
return ST_OK;
}
#endif

Expand Down Expand Up @@ -618,7 +644,7 @@ static GdbResult ATTR_GDBEXTERNFN handleCommand(unsigned cmdLen)
access = 2; // write
else if(idx == '3')
access = 1; // read
else if(idx == '4')
else // can only be idx == '4' as we've checked above
access = 3; // access
if(len == 1)
mask = 0x3F;
Expand Down Expand Up @@ -701,6 +727,8 @@ static GdbResult ATTR_GDBEXTERNFN handleCommand(unsigned cmdLen)
*/
void ATTR_GDBEXTERNFN commandLoop(bool waitForStart, bool allowDetach)
{
debug_i(">> ENTER CMDLOOP");
slaff marked this conversation as resolved.
Show resolved Hide resolved

bool initiallyAttached = gdb_state.attached;

while(true) {
Expand All @@ -727,6 +755,8 @@ void ATTR_GDBEXTERNFN commandLoop(bool waitForStart, bool allowDetach)
if(gdb_state.attached != initiallyAttached) {
System.queueCallback(TaskCallback(gdb_on_attach), gdb_state.attached);
}

debug_i("<< LEAVE CMDLOOP");
}

/*
Expand Down Expand Up @@ -780,31 +810,14 @@ static void ATTR_GDBEXTERNFN emulLdSt()
}
}

/**
* @brief If the hardware timer is operating using non-maskable interrupts, we must explicitly stop it
* @param pause true to stop the timer, false to resume it
*/
static void pauseHardwareTimer(bool pause)
{
#if GDBSTUB_PAUSE_HARDWARE_TIMER
static bool edgeIntEnable;
if(pause) {
edgeIntEnable = bitRead(READ_PERI_REG(EDGE_INT_ENABLE_REG), 1);
TM1_EDGE_INT_DISABLE();
} else if(edgeIntEnable) {
TM1_EDGE_INT_ENABLE();
}
#endif
}

// Main exception handler
static void __attribute__((noinline)) gdbstub_handle_debug_exception_flash()
{
debug_i(">> DBG 0x%02x, PC = %p", gdb_state.flags, gdbstub_savedRegs.pc);
slaff marked this conversation as resolved.
Show resolved Hide resolved

bool isEnabled = gdb_state.enabled;

if(isEnabled) {
pauseHardwareTimer(true);

bitSet(gdb_state.flags, DBGFLAG_DEBUG_EXCEPTION);

if(singleStepPs >= 0) {
Expand Down Expand Up @@ -849,10 +862,6 @@ static void __attribute__((noinline)) gdbstub_handle_debug_exception_flash()
}

gdb_state.flags = 0;

if(isEnabled) {
pauseHardwareTimer(false);
}
}

// We just caught a debug exception and need to handle it. This is called from an assembly routine in gdbstub-entry.S
Expand All @@ -869,14 +878,12 @@ void gdbstub_handle_exception()
return;
}

pauseHardwareTimer(true);
bitSet(gdb_state.flags, DBGFLAG_SYSTEM_EXCEPTION);

sendReason();
commandLoop(true, false);

gdb_state.flags = 0;
pauseHardwareTimer(false);
}
#endif

Expand All @@ -891,7 +898,6 @@ void ATTR_GDBINIT gdbstub_init()
SD(ENABLE_EXCEPTION_DUMP);
SD(ENABLE_CRASH_DUMP);
SD(GDBSTUB_ENABLE_DEBUG);
SD(GDBSTUB_PAUSE_HARDWARE_TIMER);
SD(GDBSTUB_GDB_PATCHED);
SD(GDBSTUB_USE_OWN_STACK);
SD(GDBSTUB_BREAK_ON_EXCEPTION);
Expand Down
8 changes: 5 additions & 3 deletions Sming/gdb/gdbstub.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@
#ifndef _GDB_GDBSTUB_H_
#define _GDB_GDBSTUB_H_

// Always optimise GDB stub code for size, regardless of application settings
#pragma GCC optimize("Os")

#include "gdbstub-internal.h"
#include <gdb_hooks.h>
#include "BitManipulations.h"
Expand Down Expand Up @@ -67,4 +64,9 @@ extern const uint8_t gdb_exception_signals[];
void gdbstub_init();
void gdbstub_handle_exception();

#if GDBSTUB_ENABLE_DEBUG == 0
// Optimise GDB stub code for size, regardless of application settings
#pragma GCC optimize("Os")
#endif

#endif /* _GDB_GDBSTUB_H_ */
3 changes: 1 addition & 2 deletions Sming/gdb/gdbuart.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,6 @@ static void IRAM_ATTR gdb_uart_callback(uart_t* uart, uint32_t status)
// RX FIFO Full or RX FIFO Timeout ?
if(status & (_BV(UIFF) | _BV(UITO))) {
#if GDBSTUB_ENABLE_UART2
bool isUserData = false;
auto rxbuf = user_uart == nullptr ? nullptr : user_uart->rx_buffer;
#endif

Expand Down Expand Up @@ -361,7 +360,7 @@ static void IRAM_ATTR gdb_uart_callback(uart_t* uart, uint32_t status)
// This event doesn't apply to user uart
bitClear(user_uart_status, UIFF);
bitClear(user_uart_status, UITO);
} else if(rxbuf->getFreeSpace() > UART_RX_FIFO_SIZE + user_uart->rx_headroom) {
} else if(rxbuf->getFreeSpace() > size_t(UART_RX_FIFO_SIZE) + user_uart->rx_headroom) {
bitClear(user_uart_status, UIFF);
}
}
Expand Down
Loading