mirror of https://github.com/rusefi/lua.git
memory check adapted to generational mode
This commit is contained in:
parent
f74b87c3c2
commit
4679294796
136
ltests.c
136
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; i<cl->nupvalues; 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,11 +333,7 @@ static void checkstack (global_State *g, lua_State *L1) {
|
|||
}
|
||||
|
||||
|
||||
static void checkobject (global_State *g, GCObject *o, int maybedead) {
|
||||
if (isdead(g, o))
|
||||
lua_assert(maybedead);
|
||||
else {
|
||||
lua_assert(g->gcstate != GCSpause || iswhite(o));
|
||||
static void checkrefs (global_State *g, GCObject *o) {
|
||||
switch (o->tt) {
|
||||
case LUA_TUSERDATA: {
|
||||
TValue uservalue;
|
||||
|
@ -337,6 +343,10 @@ static void checkobject (global_State *g, GCObject *o, int maybedead) {
|
|||
checkvalref(g, o, &uservalue);
|
||||
break;
|
||||
}
|
||||
case LUA_TUPVAL: {
|
||||
checkvalref(g, o, gco2upv(o)->v);
|
||||
break;
|
||||
}
|
||||
case LUA_TTABLE: {
|
||||
checktable(g, gco2t(o));
|
||||
break;
|
||||
|
@ -364,6 +374,26 @@ static void checkobject (global_State *g, GCObject *o, int maybedead) {
|
|||
}
|
||||
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));
|
||||
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))));
|
||||
}
|
||||
}
|
||||
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},
|
||||
|
|
Loading…
Reference in New Issue