mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
498 lines
14 KiB
Diff
498 lines
14 KiB
Diff
From c579b54b04653e07114755d17a3999edceb3843c Mon Sep 17 00:00:00 2001
|
|
From: Juan Carrano <j.carrano@fu-berlin.de>
|
|
Date: Mon, 14 May 2018 15:19:14 +0200
|
|
Subject: [PATCH 6/7] Cleanup test module.
|
|
|
|
This patch makes several improvements to the test module intended
|
|
to allow testing inside embedded environments:
|
|
|
|
- It does not rely in the standalone intepreter (lua.c).
|
|
- Removes all global variables from the test code.
|
|
- It allows the user to use the state's extra space (the vanilla
|
|
ltest does not reserve any space for the user).
|
|
- Makes the instrumented test allocator use the user supply
|
|
allocator instead of malloc/free.
|
|
- The user must provide a panic function. ltests.c no longer sets
|
|
it's own panic function.
|
|
- Loading of the test modules is controlled by the LUA_DEBUG
|
|
macro.
|
|
|
|
This changes should enable heavily customized lua deployments
|
|
(such as would be found in an embedded microprocessor) to be
|
|
tested with minimal modifications.
|
|
---
|
|
linit.c | 4 ++-
|
|
ltests.c | 89 ++++++++++++++++++++++++++--------------------
|
|
ltests.h | 105 ++++++++++++++++++++++++++++++++++++-------------------
|
|
lua.c | 36 +++++++++++++++++--
|
|
lua.h | 7 +++-
|
|
5 files changed, 163 insertions(+), 78 deletions(-)
|
|
|
|
diff --git a/linit.c b/linit.c
|
|
index 897ae352..c7ecd387 100644
|
|
--- a/linit.c
|
|
+++ b/linit.c
|
|
@@ -50,7 +50,9 @@ static const luaL_Reg loadedlibs[] = {
|
|
{LUA_MATHLIBNAME, luaopen_math},
|
|
{LUA_UTF8LIBNAME, luaopen_utf8},
|
|
{LUA_DBLIBNAME, luaopen_debug},
|
|
-#if defined(LUA_COMPAT_BITLIB)
|
|
+#if defined(LUA_DEBUG)
|
|
+ {LUA_TESTLIBNAME, luaB_opentests},
|
|
+#elif defined(LUA_COMPAT_BITLIB) /* No COMPAT and DEBUG at the same time */
|
|
{LUA_BITLIBNAME, luaopen_bit32},
|
|
#endif
|
|
{NULL, NULL}
|
|
diff --git a/ltests.c b/ltests.c
|
|
index 6dba514a..3ca77d2c 100644
|
|
--- a/ltests.c
|
|
+++ b/ltests.c
|
|
@@ -4,7 +4,6 @@
|
|
** See Copyright Notice in lua.h
|
|
*/
|
|
|
|
-#define ltests_c
|
|
#define LUA_CORE
|
|
|
|
#include "lprefix.h"
|
|
@@ -40,12 +39,6 @@
|
|
#if defined(LUA_DEBUG)
|
|
|
|
|
|
-void *l_Trick = 0;
|
|
-
|
|
-
|
|
-int islocked = 0;
|
|
-
|
|
-
|
|
#define obj_at(L,k) (L->ci->func + (k))
|
|
|
|
|
|
@@ -64,14 +57,6 @@ static void pushobject (lua_State *L, const TValue *o) {
|
|
api_incr_top(L);
|
|
}
|
|
|
|
-
|
|
-static int tpanic (lua_State *L) {
|
|
- fprintf(stderr, "PANIC: unprotected error in call to Lua API (%s)\n",
|
|
- lua_tostring(L, -1));
|
|
- return (exit(EXIT_FAILURE), 0); /* do not return to Lua */
|
|
-}
|
|
-
|
|
-
|
|
/*
|
|
** {======================================================================
|
|
** Controlled version for realloc.
|
|
@@ -103,11 +88,6 @@ typedef union Header {
|
|
|
|
#endif
|
|
|
|
-
|
|
-Memcontrol l_memcontrol =
|
|
- {0L, 0L, 0L, 0L, {0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L}};
|
|
-
|
|
-
|
|
static void freeblock (Memcontrol *mc, Header *block) {
|
|
if (block) {
|
|
size_t size = block->d.size;
|
|
@@ -116,16 +96,17 @@ static void freeblock (Memcontrol *mc, Header *block) {
|
|
lua_assert(*(cast(char *, block + 1) + size + i) == MARK);
|
|
mc->objcount[block->d.type]--;
|
|
fillmem(block, sizeof(Header) + size + MARKSIZE); /* erase block */
|
|
- free(block); /* actually free block */
|
|
+ mc->alloc_f(mc->alloc_ud, block, size, 0); /* actually free block */
|
|
mc->numblocks--; /* update counts */
|
|
mc->total -= size;
|
|
}
|
|
}
|
|
|
|
|
|
-void *debug_realloc (void *ud, void *b, size_t oldsize, size_t size) {
|
|
+static void *debug_realloc (void *ud, void *b, size_t oldsize, size_t size) {
|
|
Memcontrol *mc = cast(Memcontrol *, ud);
|
|
Header *block = cast(Header *, b);
|
|
+
|
|
int type;
|
|
if (mc->memlimit == 0) { /* first time? */
|
|
char *limit = getenv("MEMLIMIT"); /* initialize memory limit */
|
|
@@ -152,7 +133,9 @@ void *debug_realloc (void *ud, void *b, size_t oldsize, size_t size) {
|
|
size_t commonsize = (oldsize < size) ? oldsize : size;
|
|
size_t realsize = sizeof(Header) + size + MARKSIZE;
|
|
if (realsize < size) return NULL; /* arithmetic overflow! */
|
|
- newblock = cast(Header *, malloc(realsize)); /* alloc a new block */
|
|
+ /* alloc a new block */
|
|
+ newblock = cast(Header *, mc->alloc_f(mc->alloc_ud, NULL, oldsize, realsize));
|
|
+
|
|
if (newblock == NULL) return NULL; /* really out of memory? */
|
|
if (block) {
|
|
memcpy(newblock + 1, block + 1, commonsize); /* copy old contents */
|
|
@@ -578,18 +561,26 @@ static int get_limits (lua_State *L) {
|
|
return 1;
|
|
}
|
|
|
|
+static Memcontrol *get_memcontrol(lua_State *L)
|
|
+{
|
|
+ void *ud;
|
|
+
|
|
+ lua_getallocf(L, &ud);
|
|
+
|
|
+ return (Memcontrol *)ud;
|
|
+}
|
|
|
|
static int mem_query (lua_State *L) {
|
|
if (lua_isnone(L, 1)) {
|
|
- lua_pushinteger(L, l_memcontrol.total);
|
|
- lua_pushinteger(L, l_memcontrol.numblocks);
|
|
- lua_pushinteger(L, l_memcontrol.maxmem);
|
|
+ lua_pushinteger(L, get_memcontrol(L)->total);
|
|
+ lua_pushinteger(L, get_memcontrol(L)->numblocks);
|
|
+ lua_pushinteger(L, get_memcontrol(L)->maxmem);
|
|
return 3;
|
|
}
|
|
else if (lua_isnumber(L, 1)) {
|
|
unsigned long limit = cast(unsigned long, luaL_checkinteger(L, 1));
|
|
if (limit == 0) limit = ULONG_MAX;
|
|
- l_memcontrol.memlimit = limit;
|
|
+ get_memcontrol(L)->memlimit = limit;
|
|
return 0;
|
|
}
|
|
else {
|
|
@@ -597,7 +588,7 @@ static int mem_query (lua_State *L) {
|
|
int i;
|
|
for (i = LUA_NUMTAGS - 1; i >= 0; i--) {
|
|
if (strcmp(t, ttypename(i)) == 0) {
|
|
- lua_pushinteger(L, l_memcontrol.objcount[i]);
|
|
+ lua_pushinteger(L, get_memcontrol(L)->objcount[i]);
|
|
return 1;
|
|
}
|
|
}
|
|
@@ -608,9 +599,9 @@ static int mem_query (lua_State *L) {
|
|
|
|
static int settrick (lua_State *L) {
|
|
if (ttisnil(obj_at(L, 1)))
|
|
- l_Trick = NULL;
|
|
+ gettrick(L) = NULL;
|
|
else
|
|
- l_Trick = gcvalue(obj_at(L, 1));
|
|
+ gettrick(L) = gcvalue(obj_at(L, 1));
|
|
return 0;
|
|
}
|
|
|
|
@@ -827,13 +818,22 @@ static int num2int (lua_State *L) {
|
|
return 1;
|
|
}
|
|
|
|
+/* ugly way of getting the panic function without changing it.
|
|
+ */
|
|
+static lua_CFunction lua_getpanic (lua_State *L)
|
|
+{
|
|
+ lua_CFunction panicf = lua_atpanic(L, NULL);
|
|
+ lua_atpanic(L, panicf);
|
|
+
|
|
+ return panicf;
|
|
+}
|
|
|
|
static int newstate (lua_State *L) {
|
|
void *ud;
|
|
lua_Alloc f = lua_getallocf(L, &ud);
|
|
lua_State *L1 = lua_newstate(f, ud);
|
|
if (L1) {
|
|
- lua_atpanic(L1, tpanic);
|
|
+ lua_atpanic(L1, lua_getpanic(L));
|
|
lua_pushlightuserdata(L, L1);
|
|
}
|
|
else
|
|
@@ -1549,19 +1549,32 @@ static const struct luaL_Reg tests_funcs[] = {
|
|
};
|
|
|
|
|
|
-static void checkfinalmem (void) {
|
|
- lua_assert(l_memcontrol.numblocks == 0);
|
|
- lua_assert(l_memcontrol.total == 0);
|
|
+void luaB_init_memcontrol(Memcontrol *mc, lua_Alloc f, void *ud)
|
|
+{
|
|
+ memset(mc, 0, sizeof(*mc));
|
|
+
|
|
+ mc->alloc_f = f;
|
|
+ mc->alloc_ud = ud;
|
|
+}
|
|
+
|
|
+lua_State * luaB_newstate(Memcontrol *mc)
|
|
+{
|
|
+ return lua_newstate(debug_realloc, mc);
|
|
}
|
|
|
|
+void luaB_close(lua_State *L)
|
|
+{
|
|
+ Memcontrol * l_memcontrol = get_memcontrol(L);
|
|
+
|
|
+ lua_close(L);
|
|
+
|
|
+ lua_assert(l_memcontrol->numblocks == 0);
|
|
+ lua_assert(l_memcontrol->total == 0);
|
|
+}
|
|
|
|
int luaB_opentests (lua_State *L) {
|
|
void *ud;
|
|
- lua_atpanic(L, &tpanic);
|
|
- atexit(checkfinalmem);
|
|
lua_assert(lua_getallocf(L, &ud) == debug_realloc);
|
|
- lua_assert(ud == cast(void *, &l_memcontrol));
|
|
- lua_setallocf(L, lua_getallocf(L, NULL), ud);
|
|
luaL_newlib(L, tests_funcs);
|
|
return 1;
|
|
}
|
|
diff --git a/ltests.h b/ltests.h
|
|
index 9d26fcb0..8e10670b 100644
|
|
--- a/ltests.h
|
|
+++ b/ltests.h
|
|
@@ -4,12 +4,18 @@
|
|
** See Copyright Notice in lua.h
|
|
*/
|
|
|
|
+
|
|
#ifndef ltests_h
|
|
#define ltests_h
|
|
|
|
+/*
|
|
+** The whole module only makes sense with LUA_DEBUG on
|
|
+*/
|
|
+#if defined(LUA_DEBUG)
|
|
|
|
#include <stdlib.h>
|
|
|
|
+
|
|
/* test Lua with no compatibility code */
|
|
#undef LUA_COMPAT_MATHLIB
|
|
#undef LUA_COMPAT_IPAIRS
|
|
@@ -24,9 +30,6 @@
|
|
#undef LUA_COMPAT_MODULE
|
|
|
|
|
|
-#define LUA_DEBUG
|
|
-
|
|
-
|
|
/* turn on assertions */
|
|
#undef NDEBUG
|
|
#include <assert.h>
|
|
@@ -48,6 +51,9 @@
|
|
|
|
/* memory-allocator control variables */
|
|
typedef struct Memcontrol {
|
|
+ lua_Alloc alloc_f;
|
|
+ void * alloc_ud;
|
|
+
|
|
unsigned long numblocks;
|
|
unsigned long total;
|
|
unsigned long maxmem;
|
|
@@ -55,16 +61,6 @@ typedef struct Memcontrol {
|
|
unsigned long objcount[LUA_NUMTAGS];
|
|
} Memcontrol;
|
|
|
|
-LUA_API Memcontrol l_memcontrol;
|
|
-
|
|
-
|
|
-/*
|
|
-** generic variable for debug tricks
|
|
-*/
|
|
-extern void *l_Trick;
|
|
-
|
|
-
|
|
-
|
|
/*
|
|
** Function to traverse and check all memory used by Lua
|
|
*/
|
|
@@ -74,38 +70,72 @@ int lua_checkmemory (lua_State *L);
|
|
/* test for lock/unlock */
|
|
|
|
struct L_EXTRA { int lock; int *plock; };
|
|
+
|
|
+enum {LUA_OLDEXTRASPACE = LUA_EXTRASPACE };
|
|
#undef LUA_EXTRASPACE
|
|
-#define LUA_EXTRASPACE sizeof(struct L_EXTRA)
|
|
-#define getlock(l) cast(struct L_EXTRA*, lua_getextraspace(l))
|
|
+
|
|
+struct COMP_L_EXTRA {
|
|
+ char user_extraspace[LUA_OLDEXTRASPACE];
|
|
+ struct L_EXTRA debug_extraspace;
|
|
+ void *l_Trick; /* generic variable for debug tricks */
|
|
+};
|
|
+
|
|
+#define LUA_EXTRASPACE sizeof(struct COMP_L_EXTRA)
|
|
+
|
|
+#define getlock(l) \
|
|
+ (cast(struct COMP_L_EXTRA*, lua_getextraspace(l))->debug_extraspace)
|
|
+
|
|
+#define gettrick(l) \
|
|
+ (cast(struct COMP_L_EXTRA*, lua_getextraspace(l))->l_Trick)
|
|
+
|
|
#define luai_userstateopen(l) \
|
|
- (getlock(l)->lock = 0, getlock(l)->plock = &(getlock(l)->lock))
|
|
+ (gettrick(l) = 0, getlock(l).lock = 0, getlock(l).plock = &(getlock(l).lock))
|
|
#define luai_userstateclose(l) \
|
|
- lua_assert(getlock(l)->lock == 1 && getlock(l)->plock == &(getlock(l)->lock))
|
|
+ lua_assert(getlock(l).lock == 1 && getlock(l).plock == &(getlock(l).lock))
|
|
#define luai_userstatethread(l,l1) \
|
|
- lua_assert(getlock(l1)->plock == getlock(l)->plock)
|
|
+ lua_assert(getlock(l1).plock == getlock(l).plock)
|
|
#define luai_userstatefree(l,l1) \
|
|
- lua_assert(getlock(l)->plock == getlock(l1)->plock)
|
|
-#define lua_lock(l) lua_assert((*getlock(l)->plock)++ == 0)
|
|
-#define lua_unlock(l) lua_assert(--(*getlock(l)->plock) == 0)
|
|
+ lua_assert(getlock(l).plock == getlock(l1).plock)
|
|
+#define lua_lock(l) lua_assert((*getlock(l).plock)++ == 0)
|
|
+#define lua_unlock(l) lua_assert(--(*getlock(l).plock) == 0)
|
|
|
|
|
|
|
|
+/* Load the test library and assert that the intepreter is correctly set up
|
|
+ * for testing.
|
|
+ */
|
|
LUA_API int luaB_opentests (lua_State *L);
|
|
|
|
-LUA_API void *debug_realloc (void *ud, void *block,
|
|
- size_t osize, size_t nsize);
|
|
-
|
|
-#if defined(lua_c)
|
|
-#define luaL_newstate() lua_newstate(debug_realloc, &l_memcontrol)
|
|
-#define luaL_openlibs(L) \
|
|
- { (luaL_openlibs)(L); \
|
|
- luaL_requiref(L, "T", luaB_opentests, 1); \
|
|
- lua_pop(L, 1); }
|
|
-#endif
|
|
-
|
|
-
|
|
-
|
|
-/* change some sizes to give some bugs a chance */
|
|
+/* Initialize the control block for the test allocator.
|
|
+ * The test allocator is a wrapper around the user supplied allocator that
|
|
+ * records diagnostic and debug information.
|
|
+ *
|
|
+ * It uses a Memcontrol structure as the "ud" userdata pointer. Inside this
|
|
+ * structure the "real" allocator is stored and will be called to perform the
|
|
+ * actual memory operations.
|
|
+ *
|
|
+ * Set f and ud to your application's allocator.
|
|
+ */
|
|
+LUA_API void luaB_init_memcontrol(Memcontrol *mc, lua_Alloc f, void *ud);
|
|
+
|
|
+/* Create a new state with a specially instrumented allocator.
|
|
+ *
|
|
+ * You must supply a properly initialized Memcontrol structure.
|
|
+ * */
|
|
+LUA_API lua_State * luaB_newstate(Memcontrol *mc);
|
|
+
|
|
+/* Close the lua state and check that all memory has been freed.
|
|
+ */
|
|
+LUA_API void luaB_close(lua_State *L);
|
|
+
|
|
+#define LUA_TESTLIBNAME "T"
|
|
+
|
|
+/* Change some sizes to give some bugs a chance
|
|
+ * Activate this macro to make tests harder.
|
|
+ * This is not enabled my default because the user may want only the
|
|
+ * functionality of the test module.
|
|
+ */
|
|
+#ifdef DEBUG_OVERRIDE_SIZES
|
|
|
|
#undef LUAL_BUFFERSIZE
|
|
#define LUAL_BUFFERSIZE 23
|
|
@@ -125,5 +155,8 @@ LUA_API void *debug_realloc (void *ud, void *block,
|
|
#define STRCACHE_N 23
|
|
#define STRCACHE_M 5
|
|
|
|
-#endif
|
|
+#endif /* DEBUG_OVERRIDE_SIZES */
|
|
+
|
|
+#endif /* LUA_DEBUG */
|
|
|
|
+#endif /* ltests_h */
|
|
diff --git a/lua.c b/lua.c
|
|
index 62de0f58..a41cd305 100644
|
|
--- a/lua.c
|
|
+++ b/lua.c
|
|
@@ -592,21 +592,53 @@ static int pmain (lua_State *L) {
|
|
return 1;
|
|
}
|
|
|
|
+static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {
|
|
+ (void)ud; (void)osize; /* not used */
|
|
+ if (nsize == 0) {
|
|
+ free(ptr);
|
|
+ return NULL;
|
|
+ }
|
|
+ else
|
|
+ return realloc(ptr, nsize);
|
|
+}
|
|
+
|
|
+static int panic (lua_State *L) {
|
|
+ lua_writestringerror("PANIC: unprotected error in call to Lua API (%s)\n",
|
|
+ lua_tostring(L, -1));
|
|
+ return 0; /* return to Lua to abort */
|
|
+}
|
|
|
|
int main (int argc, char **argv) {
|
|
int status, result;
|
|
- lua_State *L = luaL_newstate(); /* create state */
|
|
+ lua_State *L;
|
|
+
|
|
+ #ifdef LUA_DEBUG
|
|
+ Memcontrol mc;
|
|
+ luaB_init_memcontrol(&mc, l_alloc, NULL);
|
|
+ L = luaB_newstate(&mc);
|
|
+ #else
|
|
+ L = lua_newstate(l_alloc, NULL);
|
|
+ #endif /* LUA_DEBUG */
|
|
+
|
|
if (L == NULL) {
|
|
l_message(argv[0], "cannot create state: not enough memory");
|
|
return EXIT_FAILURE;
|
|
}
|
|
+
|
|
+ lua_atpanic(L, &panic);
|
|
lua_pushcfunction(L, &pmain); /* to call 'pmain' in protected mode */
|
|
lua_pushinteger(L, argc); /* 1st argument */
|
|
lua_pushlightuserdata(L, argv); /* 2nd argument */
|
|
status = lua_pcall(L, 2, 1, 0); /* do the call */
|
|
result = lua_toboolean(L, -1); /* get result */
|
|
report(L, status);
|
|
- lua_close(L);
|
|
+
|
|
+ #ifdef LUA_DEBUG
|
|
+ luaB_close(L);
|
|
+ #else
|
|
+ lua_close(L);
|
|
+ #endif /* LUA_DEBUG */
|
|
+
|
|
return (result && status == LUA_OK) ? EXIT_SUCCESS : EXIT_FAILURE;
|
|
}
|
|
|
|
diff --git a/lua.h b/lua.h
|
|
index fc4e2388..b40f0d5c 100644
|
|
--- a/lua.h
|
|
+++ b/lua.h
|
|
@@ -124,7 +124,6 @@ typedef int (*lua_Writer) (lua_State *L, const void *p, size_t sz, void *ud);
|
|
typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);
|
|
|
|
|
|
-
|
|
/*
|
|
** generic extra include file
|
|
*/
|
|
@@ -132,6 +131,12 @@ typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);
|
|
#include LUA_USER_H
|
|
#endif
|
|
|
|
+/* (optional) test module
|
|
+ *
|
|
+ * Note that the ltests.h file will do nothing if LUA_DEBUG is not defined.
|
|
+ * This is done this way to avoid having a conditional dependency.
|
|
+ */
|
|
+#include "ltests.h"
|
|
|
|
/*
|
|
** RCS ident string
|
|
--
|
|
2.25.1
|
|
|