From 46792947965aa2b28232c212add07b08749e7adf Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Tue, 18 Apr 2017 16:42:12 -0300 Subject: [PATCH] memory check adapted to generational mode --- ltests.c | 196 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 133 insertions(+), 63 deletions(-) diff --git a/ltests.c b/ltests.c index ac177de8..4022809c 100644 --- a/ltests.c +++ b/ltests.c @@ -1,5 +1,5 @@ /* -** $Id: ltests.c,v 2.211 2016/12/04 20:17:24 roberto Exp roberto $ +** $Id: ltests.c,v 2.212 2017/02/23 21:07:34 roberto Exp roberto $ ** Internal Module for Debugging of the Lua Implementation ** See Copyright Notice in lua.h */ @@ -186,11 +186,22 @@ void *debug_realloc (void *ud, void *b, size_t oldsize, size_t size) { */ +/* +** Check GC invariants. For incremental mode, a black object cannot +** point to a white one. For generational mode, really old objects +** cannot point to young objects. (Threads and open upvalues, despite +** being marked "really old", continue to be visited in all collections, +** and therefore can point to new objects. They, and only they, are +** old but gray.) +*/ static int testobjref1 (global_State *g, GCObject *f, GCObject *t) { if (isdead(g,t)) return 0; - if (!issweepphase(g)) - return !(isblack(f) && iswhite(t)); - else return 1; + if (issweepphase(g)) + return 1; /* no invariants */ + else if (g->gckind == KGC_NORMAL) + return !(isblack(f) && iswhite(t)); /* basic incremental invariant */ + else + return !((getage(f) == G_OLD && isblack(f)) && !isold(t)); } @@ -198,8 +209,7 @@ static void printobj (global_State *g, GCObject *o) { printf("||%s(%p)-%c%c(%02X)||", ttypename(novariant(o->tt)), (void *)o, isdead(g,o) ? 'd' : isblack(o) ? 'b' : iswhite(o) ? 'w' : 'g', - testbit(o->marked, OLDBIT) ? 'o' : 'n', - o->marked); + "ns01oTt"[getage(o)], o->marked); if (o->tt == LUA_TSHRSTR || o->tt == LUA_TLNGSTR) printf(" '%s'", getstr(gco2ts(o))); } @@ -282,9 +292,9 @@ static void checkLclosure (global_State *g, LClosure *cl) { for (i=0; inupvalues; i++) { UpVal *uv = cl->upvals[i]; if (uv) { - if (!upisopen(uv)) /* only closed upvalues matter to invariant */ - checkvalref(g, clgc, uv->v); - lua_assert(uv->refcount > 0); + checkobjref(g, clgc, uv); + if (!upisopen(uv)) + checkvalref(g, obj2gco(uv), uv->v); } } } @@ -323,47 +333,67 @@ static void checkstack (global_State *g, lua_State *L1) { } -static void checkobject (global_State *g, GCObject *o, int maybedead) { +static void checkrefs (global_State *g, GCObject *o) { + switch (o->tt) { + case LUA_TUSERDATA: { + TValue uservalue; + Table *mt = gco2u(o)->metatable; + checkobjref(g, o, mt); + getuservalue(g->mainthread, gco2u(o), &uservalue); + checkvalref(g, o, &uservalue); + break; + } + case LUA_TUPVAL: { + checkvalref(g, o, gco2upv(o)->v); + break; + } + case LUA_TTABLE: { + checktable(g, gco2t(o)); + break; + } + case LUA_TTHREAD: { + checkstack(g, gco2th(o)); + break; + } + case LUA_TLCL: { + checkLclosure(g, gco2lcl(o)); + break; + } + case LUA_TCCL: { + checkCclosure(g, gco2ccl(o)); + break; + } + case LUA_TPROTO: { + checkproto(g, gco2p(o)); + break; + } + case LUA_TSHRSTR: + case LUA_TLNGSTR: { + lua_assert(!isgray(o)); /* strings are never gray */ + break; + } + default: lua_assert(0); + } +} + + +static void checkobject (global_State *g, GCObject *o, int maybedead, + int listage) { if (isdead(g, o)) lua_assert(maybedead); else { lua_assert(g->gcstate != GCSpause || iswhite(o)); - switch (o->tt) { - case LUA_TUSERDATA: { - TValue uservalue; - Table *mt = gco2u(o)->metatable; - checkobjref(g, o, mt); - getuservalue(g->mainthread, gco2u(o), &uservalue); - checkvalref(g, o, &uservalue); - break; + if (g->gckind == KGC_GEN) { /* generational mode? */ + lua_assert(getage(o) >= listage); + lua_assert(!iswhite(o) || !isold(o)); + if (isold(o)) { + lua_assert(isblack(o) || + getage(o) == G_TOUCHED1 || + o->tt == LUA_TTHREAD || + (o->tt == LUA_TUPVAL && upisopen(gco2upv(o)))); } - case LUA_TTABLE: { - checktable(g, gco2t(o)); - break; - } - case LUA_TTHREAD: { - checkstack(g, gco2th(o)); - break; - } - case LUA_TLCL: { - checkLclosure(g, gco2lcl(o)); - break; - } - case LUA_TCCL: { - checkCclosure(g, gco2ccl(o)); - break; - } - case LUA_TPROTO: { - checkproto(g, gco2p(o)); - break; - } - case LUA_TSHRSTR: - case LUA_TLNGSTR: { - lua_assert(!isgray(o)); /* strings are never gray */ - break; - } - default: lua_assert(0); } + checkrefs(g, o); } } @@ -371,7 +401,7 @@ static void checkobject (global_State *g, GCObject *o, int maybedead) { static void checkgraylist (global_State *g, GCObject *o) { ((void)g); /* better to keep it available if we need to print an object */ while (o) { - lua_assert(isgray(o)); + lua_assert(isgray(o) || getage(o) == G_TOUCHED2); lua_assert(!testbit(o->marked, TESTGRAYBIT)); l_setbit(o->marked, TESTGRAYBIT); switch (o->tt) { @@ -380,7 +410,7 @@ static void checkgraylist (global_State *g, GCObject *o) { case LUA_TCCL: o = gco2ccl(o)->gclist; break; case LUA_TTHREAD: o = gco2th(o)->gclist; break; case LUA_TPROTO: o = gco2p(o)->gclist; break; - default: lua_assert(0); /* other objects cannot be gray */ + default: lua_assert(0); /* other objects cannot be in a gray list */ } } } @@ -402,7 +432,7 @@ static void markgrays (global_State *g) { static void checkgray (global_State *g, GCObject *o) { for (; o != NULL; o = o->next) { - if (isgray(o)) { + if ((isgray(o) && o->tt != LUA_TUPVAL) || getage(o) == G_TOUCHED2) { lua_assert(!keepinvariant(g) || testbit(o->marked, TESTGRAYBIT)); resetbit(o->marked, TESTGRAYBIT); } @@ -411,6 +441,28 @@ static void checkgray (global_State *g, GCObject *o) { } +static void checklist (global_State *g, int maybedead, int tof, + GCObject *new, GCObject *survival, GCObject *old, GCObject *reallyold) { + GCObject *o; + for (o = new; o != survival; o = o->next) { + checkobject(g, o, maybedead, G_NEW); + lua_assert(!tof == !tofinalize(o)); + } + for (o = survival; o != old; o = o->next) { + checkobject(g, o, 0, G_SURVIVAL); + lua_assert(!tof == !tofinalize(o)); + } + for (o = old; o != reallyold; o = o->next) { + checkobject(g, o, 0, G_OLD1); + lua_assert(!tof == !tofinalize(o)); + } + for (o = reallyold; o != NULL; o = o->next) { + checkobject(g, o, 0, G_OLD); + lua_assert(!tof == !tofinalize(o)); + } +} + + int lua_checkmemory (lua_State *L) { global_State *g = G(L); GCObject *o; @@ -420,32 +472,27 @@ int lua_checkmemory (lua_State *L) { lua_assert(!iswhite(gcvalue(&g->l_registry))); } lua_assert(!isdead(g, gcvalue(&g->l_registry))); - checkstack(g, g->mainthread); - resetbit(g->mainthread->marked, TESTGRAYBIT); lua_assert(g->sweepgc == NULL || issweepphase(g)); markgrays(g); + /* check 'fixedgc' list */ for (o = g->fixedgc; o != NULL; o = o->next) { - lua_assert(o->tt == LUA_TSHRSTR && isgray(o)); + lua_assert(o->tt == LUA_TSHRSTR && isgray(o) && getage(o) == G_OLD); } + /* check 'allgc' list */ checkgray(g, g->allgc); maybedead = (GCSatomic < g->gcstate && g->gcstate <= GCSswpallgc); - for (o = g->allgc; o != NULL; o = o->next) { - checkobject(g, o, maybedead); - lua_assert(!tofinalize(o)); - } + checklist(g, maybedead, 0, g->allgc, g->survival, g->old, g->reallyold); + /* check 'finobj' list */ checkgray(g, g->finobj); - for (o = g->finobj; o != NULL; o = o->next) { - checkobject(g, o, 0); - lua_assert(tofinalize(o)); - lua_assert(o->tt == LUA_TUSERDATA || o->tt == LUA_TTABLE); - } + checklist(g, 0, 1, g->finobj, g->finobjsur, g->finobjold, g->finobjrold); + /* check 'tobefnz' list */ checkgray(g, g->tobefnz); for (o = g->tobefnz; o != NULL; o = o->next) { - checkobject(g, o, 0); + checkobject(g, o, 0, G_NEW); lua_assert(tofinalize(o)); lua_assert(o->tt == LUA_TUSERDATA || o->tt == LUA_TTABLE); } @@ -621,22 +668,42 @@ static int gc_color (lua_State *L) { TValue *o; luaL_checkany(L, 1); o = obj_at(L, 1); - if (!iscollectable(o)) + if (!iscollectable(o)) { lua_pushstring(L, "no collectable"); + return 1; + } else { + static const char *gennames[] = {"new", "survival", "old0", "old1", + "old", "touched1", "touched2"}; GCObject *obj = gcvalue(o); lua_pushstring(L, isdead(G(L), obj) ? "dead" : iswhite(obj) ? "white" : isblack(obj) ? "black" : "grey"); + lua_pushstring(L, gennames[getage(obj)]); + return 2; } - return 1; +} + + +static int gc_printobj (lua_State *L) { + TValue *o; + luaL_checkany(L, 1); + o = obj_at(L, 1); + if (!iscollectable(o)) + printf("no collectable\n"); + else { + GCObject *obj = gcvalue(o); + printobj(G(L), obj); + printf("\n"); + } + return 0; } static int gc_state (lua_State *L) { static const char *statenames[] = {"propagate", "atomic", "sweepallgc", "sweepfinobj", "sweeptobefnz", "sweepend", "pause", ""}; - static const int states[] = {GCSpropagate, GCSatomic, GCSswpallgc, + static const int states[] = {GCSpropagate, GCSenteratomic, GCSswpallgc, GCSswpfinobj, GCSswptobefnz, GCSswpend, GCSpause, -1}; int option = states[luaL_checkoption(L, 1, "", statenames)]; if (option == -1) { @@ -645,6 +712,8 @@ static int gc_state (lua_State *L) { } else { global_State *g = G(L); + if (G(L)->gckind == KGC_GEN) + luaL_error(L, "cannot change states in generational mode"); lua_lock(L); if (option < g->gcstate) { /* must cross 'pause'? */ luaC_runtilstate(L, bitmask(GCSpause)); /* run until pause */ @@ -1519,6 +1588,7 @@ static const struct luaL_Reg tests_funcs[] = { {"doremote", doremote}, {"gccolor", gc_color}, {"gcstate", gc_state}, + {"pobj", gc_printobj}, {"getref", getref}, {"hash", hash_query}, {"int2fb", int2fb_aux},