Browse Source

Tweaks to the Remote GDB interface to make it usable

TerryE 6 years ago
parent
commit
e00d927a02
5 changed files with 258 additions and 11 deletions
  1. 111 0
      .gdbinit
  2. 9 1
      app/include/user_config.h
  3. 128 2
      app/lua/lauxlib.c
  4. 4 3
      app/modules/gdbstub.c
  5. 6 5
      app/user/user_exceptions.c

+ 111 - 0
.gdbinit

@@ -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
+

+ 9 - 1
app/include/user_config.h

@@ -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

+ 128 - 2
app/lua/lauxlib.c

@@ -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)

+ 4 - 3
app/modules/gdbstub.c

@@ -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);

+ 6 - 5
app/user/user_exceptions.c

@@ -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) {}
   }