Tweaks to the Remote GDB interface to make it usable

This commit is contained in:
TerryE 2018-02-14 01:00:15 +00:00
parent 4141e69003
commit e00d927a02
5 changed files with 258 additions and 11 deletions

111
.gdbinit Normal file
View File

@ -0,0 +1,111 @@
#
# This is very much a work in progress to show how we can use macros to make the
# GDB interface a lot more useable. For example the next / step commands only
# work if the stepper doesn't leave the current scope. Beyond that you have a
# single hardware breakpoint which can be used as an hb or a wa. You have to
# remember to delete the previous one, so the br macro does this for you.
#
set confirm off
set print null-stop
define br
d
hb $arg0
end
set pagination off
define prTV
if $arg0
set $type = $arg0.tt
set $val = $arg0.value
if $type == 0
# NIL
printf "Nil\n"
end
if $type == 1
# Boolean
printf "Boolean: %u\n", $val->n
end
if $type == 2
# ROTable
printf "ROTable: %p\n", $val->p
end
if $type == 3
# Light Function
printf "Light Func: %p\n",p $arg $val->p
end
if $type == 4
# Light User Data
printf "Light Udata: %p\n", $val->pend
end
if $type == 5
# Number
printf "Boolean: %u\n", $val->n
end
if $type == 6
# TString
set $o = &($val.gc->ts.tsv)
printf "Common header: next = %p, marked = 0x%01x\n", $o->next, $o->marked
printf "String: hash = 0x%08x, len = %u : %s\n", $o->hash, $o->len, (char *)($ts+1)
end
if $type == 7
# Table
__printComHdr $arg0
set $ts = (TString *)$val->p
end
if $type == 8
# Function
set $o = &($val->gc.cl.c)
printf "Common header: next = %p, marked = 0x%01x\n", $o->next, $o->marked
if $o->isC == 0
set $o = &($val->gc.cl.l)
printf "LClosure: nupvalues = %u, gclist = %p, env = %p, p = %p\n", $o->nupvalues, $o->gclist, $o->env, $o->p
else
printf "CClosure: nupvalues = %u, gclist = %p, env = %p, f = %p\np", $o->nupvalues, $o->gclist, $o->env, $o->f
end
end
if $type == 9
# UserData
end
if $type == 10
# Thread
end
end
end
define prL
if L > 0
printf " stack: %u\n", L->top-L->base
printf " hooking: %u, %u, %u, %u, %p\n", L->hookmask, L->allowhook, L->basehookcount, L->hookcount, L->hook
end
end
define dumpstrt
set $st = $arg0
set $i = 0
while $i< $st->size
set $o = &(((TString *)($st->hash[$i]))->tsv)
while $o
if $o->next
printf "Slot: %5i %p %p %08x %02x %4u", $i, $o, $o->next, $o->hash, $o->marked, $o->len
else
printf "Slot: %5i %p %08x %02x %4u", $i, $o, $o->hash, $o->marked, $o->len
end
if $o->marked & 0x80
printf "* %s\n", *(char **)($o+1)
else
printf " %s\n", (char *)($o+1)
end
set $o = &(((TString *)($o->next))->tsv)
end
set $i = $i + 1
end
end
define dumpRAMstrt
dumpstrt &(L->l_G->strt)
end
set history save on
set history size 1000

View File

@ -12,11 +12,19 @@
// This adds the asserts in LUA. It also adds some useful extras to the
// node module. This is all silent in normal operation and so can be enabled
// without any harm (except for the code size increase and slight slowdown)
// Either here for a global change or in the DEFINES variable in the relevant
// Makefile for ony one subdirectory. If you want to use the remote GDB to
// handle breaks and failed assetions then enable DEVELOPMENT_USE GDB
//#define DEVELOPMENT_TOOLS
//#define DEVELOPMENT_USE_GDB
#ifdef DEVELOPMENT_TOOLS
#if defined(LUA_CROSS_COMPILER) || !defined(DEVELOPMENT_USE_GDB)
extern void luaL_assertfail(const char *file, int line, const char *message);
#define lua_assert(x) ((x) ? (void) 0 : luaL_assertfail(__FILE__, __LINE__, #x))
#else
extern void luaL_dbgbreak(void);
#define lua_assert(x) ((x) ? (void) 0 : luaL_dbgbreak())
#endif
#endif
// This enables lots of debug output and changes the serial bit rate. This

View File

@ -44,6 +44,101 @@
#define LUA_USECCLOSURES 0
#define LUA_USELIGHTFUNCTIONS 1
//#define DEBUG_ALLOCATOR
#ifdef DEBUG_ALLOCATOR
/*
** {======================================================================
** Diagnosticd version for realloc. This is enabled only if the
** DEBUG_ALLOCATOR is defined. It is a cutdown version of the allocator
** used in the Lua Test Suite -- a compromise between the ability catch
** most alloc/free errors and overruns and working within the RAM limits
** of the ESP8266 architecture. ONLY FOR HEAVY HACKERS
** =======================================================================
*/
#define this_realloc debug_realloc
#define ASSERT(s) if (!(s)) {asm ("break 0,0" ::);}
#define MARK 0x55 /* 01010101 (a nice pattern) */
#define MARK4 0x55555555
#define MARKSIZE 4 /* size of marks after each block */
#define fillmem(mem,size) memset(mem, -MARK, size)
typedef struct Memcontrol { /* memory-allocator control variables */
uint32_t numblocks;
uint32_t total;
uint32_t maxmem;
uint32_t memlimit;
} Memcontrol;
static Memcontrol mc = {0,0,0,32768};
typedef union MemHeader {
L_Umaxalign a; /* ensures maximum alignment for Header */
struct {
size_t size;
int mark;
} d;
} MemHeader;
static void freeblock (MemHeader *block) {
if (block) {
size_t size = block->d.size;
fillmem(block, sizeof(MemHeader) + size + MARKSIZE); /* erase block */
c_free(block); /* actually free block */
mc.numblocks--; /* update counts */
mc.total -= size;
}
}
void *debug_realloc (void *b, size_t oldsize, size_t size) {
MemHeader *block = cast(MemHeader *, b);
int i;
if (block == NULL) {
oldsize = 0;
} else {
block--; /* go to real header */
ASSERT(block->d.mark == MARK4);
ASSERT(oldsize == block->d.size);
for (i = 0; i < MARKSIZE; i++) /* check marks after block */
ASSERT (cast(char *, b)[oldsize + i] == MARK);
}
if (size == 0) {
freeblock(block);
return NULL;
} else if (size > oldsize && mc.total+size-oldsize > mc.memlimit)
return NULL; /* fake a memory allocation error */
else {
MemHeader *newblock;
int i;
size_t commonsize = (oldsize < size) ? oldsize : size;
size_t realsize = sizeof(MemHeader) + size + MARKSIZE;
newblock = cast(MemHeader *, c_malloc(realsize)); /* alloc a new block */
if (newblock == NULL)
return NULL; /* really out of memory? */
if (block) {
memcpy(newblock + 1, block + 1, commonsize); /* copy old contents */
freeblock(block); /* erase (and check) old copy */
}
/* initialize new part of the block with something weird */
fillmem(cast(char *, newblock + 1) + commonsize, size - commonsize);
/* initialize marks after block */
newblock->d.mark = MARK4;
newblock->d.size = size;
for (i = 0; i < MARKSIZE; i++)
cast(char *, newblock + 1)[size + i] = MARK;
mc.total += size;
if (mc.total > mc.maxmem)
mc.maxmem = mc.total;
mc.numblocks++;
return (newblock + 1);
}
}
/* }====================================================================== */
#else
#define this_realloc(p,os,s) c_realloc(p,s)
#endif
/*
** {======================================================
** Error-report functions
@ -786,8 +881,12 @@ static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {
void *nptr;
if (nsize == 0) {
#ifdef DEBUG_ALLOCATOR
return (void *)this_realloc(ptr, osize, nsize);
#else
c_free(ptr);
return NULL;
#endif
}
if (L != NULL && (mode & EGC_ALWAYS)) /* always collect memory if requested */
luaC_fullgc(L);
@ -798,18 +897,45 @@ static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {
if(G(L)->memlimit > 0 && (mode & EGC_ON_MEM_LIMIT) && l_check_memlimit(L, nsize - osize))
return NULL;
}
nptr = (void *)c_realloc(ptr, nsize);
nptr = (void *)this_realloc(ptr, osize, nsize);
if (nptr == NULL && L != NULL && (mode & EGC_ON_ALLOC_FAILURE)) {
luaC_fullgc(L); /* emergency full collection. */
nptr = (void *)c_realloc(ptr, nsize); /* try allocation again */
nptr = (void *)this_realloc(ptr, osize, nsize); /* try allocation again */
}
return nptr;
}
LUALIB_API void luaL_assertfail(const char *file, int line, const char *message) {
dbg_printf("ASSERT@%s(%d): %s\n", file, line, message);
#if defined(LUA_CROSS_COMPILER)
exit(1);
#endif
}
#ifdef DEVELOPMENT_USE_GDB
/*
* This is a simple stub used by lua_assert() if DEVELOPMENT_USE_GDB is defined.
* Instead of crashing out with an assert error, this hook starts the GDB remote
* stub if not already running and then issues a break. The rationale here is
* that when testing the developer migght be using screen/PuTTY to work ineractively
* with the Lua Interpreter via UART0. However if an assert triggers, then there
* is the option to exit the interactive session and start the Xtensa remote GDB
* which will then sync up with the remote GDB client to allow forensics of the error.
*/
extern void gdbstub_init(void);
LUALIB_API void luaL_dbgbreak(void) {
static int repeat_entry = 0;
if (repeat_entry == 0) {
dbg_printf("Start up the gdb stub if not already started\n");
gdbstub_init();
repeat_entry = 1;
}
asm("break 0,0" ::);
}
#endif
static int panic (lua_State *L) {
(void)L; /* to avoid warnings */
#if defined(LUA_USE_STDIO)

View File

@ -2,7 +2,8 @@
* This module, when enabled with the LUA_USE_MODULES_GDBSTUB define causes
* the gdbstub code to be included and enabled to handle all fatal exceptions.
* This allows you to use the lx106 gdb to catch the exception and then poke
* around. You can't continue from an exception (at least not easily).
* around. You can continue from a break, but attempting to continue from an
* exception usually fails.
*
* This should not be included in production builds as any exception will
* put the nodemcu into a state where it is waiting for serial input and it has
@ -41,8 +42,8 @@ static int lgdbstub_open(lua_State *L) {
static const LUA_REG_TYPE gdbstub_map[] = {
{ LSTRKEY( "brk" ), LFUNCVAL( lgdbstub_break ) },
{ LSTRKEY( "gdboutput" ), LFUNCVAL( lgdbstub_gdboutput) },
{ LSTRKEY( "open" ), LFUNCVAL( lgdbstub_open) },
{ LNILKEY, LNILVAL }
};
NODEMCU_MODULE(GDBSTUB, "gdbstub", gdbstub_map, lgdbstub_open);
NODEMCU_MODULE(GDBSTUB, "gdbstub", gdbstub_map, NULL);

View File

@ -40,7 +40,6 @@
static exception_handler_fn load_store_handler;
void load_non_32_wide_handler (struct exception_frame *ef, uint32_t cause)
{
/* If this is not EXCCAUSE_LOAD_STORE_ERROR you're doing it wrong! */
@ -73,13 +72,15 @@ void load_non_32_wide_handler (struct exception_frame *ef, uint32_t cause)
{
die:
/* Turns out we couldn't fix this, so try and chain to the handler
* that was set by the SDK. If none then trigger a system break instead
* and hang if the break doesn't get handled. This is effectively
* what would happen if the default handler was installed. */
* that was set. (This is typically a remote GDB break). If none
* then trigger a system break instead and hang if the break doesn't
* get handled. This is effectively what would happen if the default
* handler was installed. */
if (load_store_handler) {
load_store_handler(ef, cause);
return;
}
asm ("break 1, 1");
asm ("break 1, 1");
while (1) {}
}