From 5440b42f434ecb6fe7a8e0d19fb8e8baf82e90b7 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Mon, 13 Nov 2017 13:36:52 -0200 Subject: [PATCH] using 'trap' to stop 'luaV_execute' when necessary (tracing and to update its copy of 'base' when the stack is reallocated) --- ldebug.c | 23 +++++++++++++++++++++-- ldo.c | 15 ++++++++++++--- lstate.c | 3 ++- lstate.h | 3 ++- lvm.c | 47 ++++++++++++++++++++++++++++++----------------- 5 files changed, 67 insertions(+), 24 deletions(-) diff --git a/ldebug.c b/ldebug.c index 89b36f07..7323f23a 100644 --- a/ldebug.c +++ b/ldebug.c @@ -1,5 +1,5 @@ /* -** $Id: ldebug.c,v 2.142 2017/11/08 14:50:23 roberto Exp roberto $ +** $Id: ldebug.c,v 2.143 2017/11/13 12:20:51 roberto Exp roberto $ ** Debug Interface ** See Copyright Notice in lua.h */ @@ -107,7 +107,24 @@ static int currentline (CallInfo *ci) { /* -** This function can be called asynchronously (e.g. during a signal). +** This function can be called asynchronously (e.g. during a signal), +** under "reasonable" assumptions. A new 'ci' is completely linked +** in the list before it becomes part of the "active" list, and +** we assume that pointers are atomic (see comment in next function). +** (If we traverse one more item, there is no problem. If we traverse +** one less item, the worst that can happen is that the signal will +** not interrupt the script.) +*/ +static void settraps (CallInfo *ci) { + for (; ci != NULL; ci = ci->previous) + if (isLua(ci)) + ci->u.l.trap = 1; +} + + +/* +** This function can be called asynchronously (e.g. during a signal), +** under "reasonable" assumptions. ** Fields 'oldpc', 'basehookcount', and 'hookcount' (set by ** 'resethookcount') are for debug only, and it is no problem if they ** get arbitrary values (causes at most one wrong hook call). 'hookmask' @@ -126,6 +143,8 @@ LUA_API void lua_sethook (lua_State *L, lua_Hook func, int mask, int count) { L->basehookcount = count; resethookcount(L); L->hookmask = cast_byte(mask); + if (mask & (LUA_MASKLINE | LUA_MASKCOUNT)) + settraps(L->ci); /* to trace inside 'luaV_execute' */ } diff --git a/ldo.c b/ldo.c index 86e77f97..970b890b 100644 --- a/ldo.c +++ b/ldo.c @@ -1,5 +1,5 @@ /* -** $Id: ldo.c,v 2.170 2017/11/07 13:25:26 roberto Exp roberto $ +** $Id: ldo.c,v 2.171 2017/11/13 12:26:30 roberto Exp roberto $ ** Stack and Call structure of Lua ** See Copyright Notice in lua.h */ @@ -159,12 +159,16 @@ int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { static void correctstack (lua_State *L, StkId oldstack) { CallInfo *ci; UpVal *up; + if (L->stack == oldstack) + return; /* stack address did not change */ L->top = (L->top - oldstack) + L->stack; for (up = L->openupval; up != NULL; up = up->u.open.next) up->v = s2v((uplevel(up) - oldstack) + L->stack); for (ci = L->ci; ci != NULL; ci = ci->previous) { ci->top = (ci->top - oldstack) + L->stack; ci->func = (ci->func - oldstack) + L->stack; + if (isLua(ci)) + ci->u.l.trap = 1; /* signal to update 'trap' in 'luaV_execute' */ } } @@ -277,13 +281,18 @@ void luaD_hook (lua_State *L, int event, int line) { static void callhook (lua_State *L, CallInfo *ci) { - int hook = LUA_HOOKCALL; + int hook; + ci->u.l.trap = 1; + if (!(L->hookmask & LUA_MASKCALL)) + return; /* some other hook */ ci->u.l.savedpc++; /* hooks assume 'pc' is already incremented */ if (isLua(ci->previous) && GET_OPCODE(*(ci->previous->u.l.savedpc - 1)) == OP_TAILCALL) { ci->callstatus |= CIST_TAIL; hook = LUA_HOOKTAILCALL; } + else + hook = LUA_HOOKCALL; luaD_hook(L, hook, -1); ci->u.l.savedpc--; /* correct 'pc' */ } @@ -430,7 +439,7 @@ int luaD_precall (lua_State *L, StkId func, int nresults) { lua_assert(ci->top <= L->stack_last); ci->u.l.savedpc = p->code; /* starting point */ ci->callstatus = CIST_LUA; - if (L->hookmask & LUA_MASKCALL) + if (L->hookmask) callhook(L, ci); return 0; } diff --git a/lstate.c b/lstate.c index 7f67527b..54e390b5 100644 --- a/lstate.c +++ b/lstate.c @@ -1,5 +1,5 @@ /* -** $Id: lstate.c,v 2.143 2017/10/31 17:54:35 roberto Exp $ +** $Id: lstate.c,v 2.146 2017/11/07 13:25:26 roberto Exp roberto $ ** Global State ** See Copyright Notice in lua.h */ @@ -103,6 +103,7 @@ CallInfo *luaE_extendCI (lua_State *L) { L->ci->next = ci; ci->previous = L->ci; ci->next = NULL; + ci->u.l.trap = 0; L->nci++; return ci; } diff --git a/lstate.h b/lstate.h index 4798474c..02715dfb 100644 --- a/lstate.h +++ b/lstate.h @@ -1,5 +1,5 @@ /* -** $Id: lstate.h,v 2.146 2017/11/02 11:28:56 roberto Exp $ +** $Id: lstate.h,v 2.150 2017/11/07 13:25:26 roberto Exp roberto $ ** Global State ** See Copyright Notice in lua.h */ @@ -91,6 +91,7 @@ typedef struct CallInfo { union { struct { /* only for Lua functions */ const Instruction *savedpc; + l_signalT trap; } l; struct { /* only for C functions */ lua_KFunction k; /* continuation in case of yields */ diff --git a/lvm.c b/lvm.c index c91fa638..a8285ac6 100644 --- a/lvm.c +++ b/lvm.c @@ -1,5 +1,5 @@ /* -** $Id: lvm.c,v 2.308 2017/11/08 14:50:23 roberto Exp roberto $ +** $Id: lvm.c,v 2.309 2017/11/08 19:01:02 roberto Exp roberto $ ** Lua virtual machine ** See Copyright Notice in lua.h */ @@ -756,14 +756,16 @@ void luaV_finishOp (lua_State *L) { -#define updatemask(L) (mask = L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) +#define updatetrap(ci) (trap = ci->u.l.trap) + +#define updatebase(ci) (base = ci->func + 1) /* -** Execute a jump instruction. The 'updatemask' allows signals to stop -** tight loops. (Without it, the local copy of 'mask' could never change.) +** Execute a jump instruction. The 'updatetrap' allows signals to stop +** tight loops. (Without it, the local copy of 'trap' could never change.) */ -#define dojump(ci,i,e) { pc += GETARG_sJ(i) + e; updatemask(L); } +#define dojump(ci,i,e) { pc += GETARG_sJ(i) + e; updatetrap(ci); } /* for test instructions, execute the jump instruction that follows it */ @@ -780,19 +782,24 @@ void luaV_finishOp (lua_State *L) { ** Protect code that, in general, can raise errors, reallocate the ** stack, and change the hooks. */ -#define Protect(exp) (savepc(L), (exp), base = ci->func + 1, updatemask(L)) +#define Protect(exp) (savepc(L), (exp), updatetrap(ci)) #define checkGC(L,c) \ { luaC_condGC(L, L->top = (c), /* limit of live values */ \ - Protect(L->top = ci->top)); /* restore top */ \ + (L->top = ci->top, updatetrap(ci))); /* restore top */ \ luai_threadyield(L); } /* fetch an instruction and prepare its execution */ #define vmfetch() { \ i = *(pc++); \ - if (mask) Protect(luaG_traceexec(L)); \ + if (trap) { \ + if (!(L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT))) \ + trap = ci->u.l.trap = 0; /* no need to stop again */ \ + else { savepc(L); luaG_traceexec(L); } \ + updatebase(ci); /* the trap may be just for that */ \ + } \ ra = RA(i); /* WARNING: any stack reallocation invalidates 'ra' */ \ } @@ -802,19 +809,19 @@ void luaV_finishOp (lua_State *L) { void luaV_execute (lua_State *L) { - CallInfo *ci = L->ci; + CallInfo *ci = L->ci; /* local copy of 'L->ci' */ LClosure *cl; TValue *k; StkId base; /* local copy of 'ci->func + 1' */ - int mask; /* local copy of 'L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)' */ + int trap; const Instruction *pc; /* local copy of 'ci->u.l.savedpc' */ ci->callstatus |= CIST_FRESH; /* fresh invocation of 'luaV_execute" */ newframe: /* reentry point when frame changes (call/return) */ lua_assert(ci == L->ci); cl = clLvalue(s2v(ci->func)); /* local reference to function's closure */ k = cl->p->k; /* local reference to function's constant table */ - updatemask(L); - base = ci->func + 1; + updatetrap(ci); + updatebase(ci); pc = ci->u.l.savedpc; /* main loop of interpreter */ for (;;) { @@ -1294,7 +1301,10 @@ void luaV_execute (lua_State *L) { StkId rb; L->top = base + c + 1; /* mark the end of concat operands */ Protect(luaV_concat(L, c - b + 1)); - ra = RA(i); /* 'luaV_concat' may invoke TMs and move the stack */ + if (trap) { /* 'luaV_concat' may move the stack */ + updatebase(ci); + ra = RA(i); + } rb = base + b; setobjs2s(L, ra, rb); checkGC(L, (ra >= rb ? ra + 1 : rb)); @@ -1390,7 +1400,7 @@ void luaV_execute (lua_State *L) { lua_assert(GETARG_C(i) - 1 == LUA_MULTRET); savepc(L); if (luaD_precall(L, ra, LUA_MULTRET)) /* C function? */ - Protect((void)0); /* update 'base' */ + updatetrap(ci); else { /* tail call: put called frame (n) in place of caller one (o) */ CallInfo *nci = L->ci; /* called frame (new) */ @@ -1416,7 +1426,8 @@ void luaV_execute (lua_State *L) { } vmcase(OP_RETURN) { int b = GETARG_B(i); - if (cl->p->sizep > 0) luaF_close(L, base); + if (cl->p->sizep > 0) + luaF_close(L, base); savepc(L); b = luaD_poscall(L, ci, ra, (b != 0 ? b - 1 : cast_int(L->top - ra))); if (ci->callstatus & CIST_FRESH) /* local 'ci' still from callee */ @@ -1452,7 +1463,7 @@ void luaV_execute (lua_State *L) { setfltvalue(s2v(ra + 3), idx); /* ...and external index */ } } - updatemask(L); + updatetrap(ci); vmbreak; } vmcase(OP_FORPREP) { @@ -1492,8 +1503,10 @@ void luaV_execute (lua_State *L) { L->top = cb + 3; /* func. + 2 args (state and index) */ Protect(luaD_call(L, cb, GETARG_C(i))); L->top = ci->top; + if (trap) /* keep 'base' correct for next instruction */ + updatebase(ci); i = *(pc++); /* go to next instruction */ - ra = RA(i); + ra = RA(i); /* get its 'ra' */ lua_assert(GET_OPCODE(i) == OP_TFORLOOP); goto l_tforloop; }