Better support in 'ltests' for tracing the GC
This commit is contained in:
17
lgc.c
17
lgc.c
@@ -1695,21 +1695,23 @@ static void incstep (lua_State *L, global_State *g) {
|
|||||||
|
|
||||||
|
|
||||||
#if !defined(luai_tracegc)
|
#if !defined(luai_tracegc)
|
||||||
#define luai_tracegc(L) ((void)0)
|
#define luai_tracegc(L,f) ((void)0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Performs a basic GC step if collector is running. (If collector is
|
** Performs a basic GC step if collector is running. (If collector was
|
||||||
** not running, set a reasonable debt to avoid it being called at
|
** stopped by the user, set a reasonable debt to avoid it being called
|
||||||
** every single check.)
|
** at every single check.)
|
||||||
*/
|
*/
|
||||||
void luaC_step (lua_State *L) {
|
void luaC_step (lua_State *L) {
|
||||||
global_State *g = G(L);
|
global_State *g = G(L);
|
||||||
lua_assert(!g->gcemergency);
|
lua_assert(!g->gcemergency);
|
||||||
if (!gcrunning(g)) /* not running? */
|
if (!gcrunning(g)) { /* not running? */
|
||||||
luaE_setdebt(g, 20000);
|
if (g->gcstp & GCSTPUSR) /* stopped by the user? */
|
||||||
|
luaE_setdebt(g, 20000);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
luai_tracegc(L); /* for internal debugging */
|
luai_tracegc(L, 1); /* for internal debugging */
|
||||||
switch (g->gckind) {
|
switch (g->gckind) {
|
||||||
case KGC_INC: case KGC_GENMAJOR:
|
case KGC_INC: case KGC_GENMAJOR:
|
||||||
incstep(L, g);
|
incstep(L, g);
|
||||||
@@ -1719,6 +1721,7 @@ void luaC_step (lua_State *L) {
|
|||||||
setminordebt(g);
|
setminordebt(g);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
luai_tracegc(L, 0); /* for internal debugging */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
68
ltests.c
68
ltests.c
@@ -944,34 +944,63 @@ static int gc_printobj (lua_State *L) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const char *statenames[] = {
|
||||||
|
"propagate", "enteratomic", "atomic", "sweepallgc", "sweepfinobj",
|
||||||
|
"sweeptobefnz", "sweepend", "callfin", "pause", ""};
|
||||||
|
|
||||||
static int gc_state (lua_State *L) {
|
static int gc_state (lua_State *L) {
|
||||||
static const char *statenames[] = {
|
|
||||||
"propagate", "atomic", "sweepallgc", "sweepfinobj",
|
|
||||||
"sweeptobefnz", "sweepend", "callfin", "pause", ""};
|
|
||||||
static const int states[] = {
|
static const int states[] = {
|
||||||
GCSpropagate, GCSenteratomic, GCSswpallgc, GCSswpfinobj,
|
GCSpropagate, GCSenteratomic, GCSatomic, GCSswpallgc, GCSswpfinobj,
|
||||||
GCSswptobefnz, GCSswpend, GCScallfin, GCSpause, -1};
|
GCSswptobefnz, GCSswpend, GCScallfin, GCSpause, -1};
|
||||||
int option = states[luaL_checkoption(L, 1, "", statenames)];
|
int option = states[luaL_checkoption(L, 1, "", statenames)];
|
||||||
|
global_State *g = G(L);
|
||||||
if (option == -1) {
|
if (option == -1) {
|
||||||
lua_pushstring(L, statenames[G(L)->gcstate]);
|
lua_pushstring(L, statenames[g->gcstate]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
global_State *g = G(L);
|
if (g->gckind != KGC_INC)
|
||||||
if (G(L)->gckind != KGC_INC)
|
|
||||||
luaL_error(L, "cannot change states in generational mode");
|
luaL_error(L, "cannot change states in generational mode");
|
||||||
lua_lock(L);
|
lua_lock(L);
|
||||||
if (option < g->gcstate) { /* must cross 'pause'? */
|
if (option < g->gcstate) { /* must cross 'pause'? */
|
||||||
luaC_runtilstate(L, GCSpause, 1); /* run until pause */
|
luaC_runtilstate(L, GCSpause, 1); /* run until pause */
|
||||||
}
|
}
|
||||||
luaC_runtilstate(L, option, 0); /* do not skip propagation state */
|
luaC_runtilstate(L, option, 0); /* do not skip propagation state */
|
||||||
lua_assert(G(L)->gcstate == option);
|
lua_assert(g->gcstate == option);
|
||||||
lua_unlock(L);
|
lua_unlock(L);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int tracinggc = 0;
|
||||||
|
void luai_tracegctest (lua_State *L, int first) {
|
||||||
|
if (!tracinggc) return;
|
||||||
|
else {
|
||||||
|
global_State *g = G(L);
|
||||||
|
lua_unlock(L);
|
||||||
|
g->gcstp = GCSTPGC;
|
||||||
|
lua_checkstack(L, 10);
|
||||||
|
lua_getfield(L, LUA_REGISTRYINDEX, "tracegc");
|
||||||
|
lua_pushboolean(L, first);
|
||||||
|
lua_call(L, 1, 0);
|
||||||
|
g->gcstp = 0;
|
||||||
|
lua_lock(L);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int tracegc (lua_State *L) {
|
||||||
|
if (lua_isnil(L, 1))
|
||||||
|
tracinggc = 0;
|
||||||
|
else {
|
||||||
|
tracinggc = 1;
|
||||||
|
lua_setfield(L, LUA_REGISTRYINDEX, "tracegc");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int hash_query (lua_State *L) {
|
static int hash_query (lua_State *L) {
|
||||||
if (lua_isnone(L, 2)) {
|
if (lua_isnone(L, 2)) {
|
||||||
luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "string expected");
|
luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "string expected");
|
||||||
@@ -1038,17 +1067,17 @@ static int table_query (lua_State *L) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int query_GCparams (lua_State *L) {
|
static int gc_query (lua_State *L) {
|
||||||
global_State *g = G(L);
|
global_State *g = G(L);
|
||||||
lua_pushinteger(L, cast(lua_Integer, gettotalbytes(g)));
|
lua_pushstring(L, g->gckind == KGC_INC ? "inc"
|
||||||
lua_pushinteger(L, cast(lua_Integer, g->GCdebt));
|
: g->gckind == KGC_GENMAJOR ? "genmajor"
|
||||||
lua_pushinteger(L, cast(lua_Integer, applygcparam(g, MINORMUL, 100)));
|
: "genminor");
|
||||||
lua_pushinteger(L, cast(lua_Integer, applygcparam(g, MAJORMINOR, 100)));
|
lua_pushstring(L, statenames[g->gcstate]);
|
||||||
lua_pushinteger(L, cast(lua_Integer, applygcparam(g, MINORMAJOR, 100)));
|
lua_pushinteger(L, cast_st2S(gettotalbytes(g)));
|
||||||
lua_pushinteger(L, cast(lua_Integer, applygcparam(g, PAUSE, 100)));
|
lua_pushinteger(L, cast_st2S(g->GCdebt));
|
||||||
lua_pushinteger(L, cast(lua_Integer, applygcparam(g, STEPMUL, 100)));
|
lua_pushinteger(L, cast_st2S(g->GCmarked));
|
||||||
lua_pushinteger(L, cast(lua_Integer, applygcparam(g, STEPSIZE, 100)));
|
lua_pushinteger(L, cast_st2S(g->GCmajorminor));
|
||||||
return 8;
|
return 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -2009,6 +2038,7 @@ static const struct luaL_Reg tests_funcs[] = {
|
|||||||
{"gccolor", gc_color},
|
{"gccolor", gc_color},
|
||||||
{"gcage", gc_age},
|
{"gcage", gc_age},
|
||||||
{"gcstate", gc_state},
|
{"gcstate", gc_state},
|
||||||
|
{"tracegc", tracegc},
|
||||||
{"pobj", gc_printobj},
|
{"pobj", gc_printobj},
|
||||||
{"getref", getref},
|
{"getref", getref},
|
||||||
{"hash", hash_query},
|
{"hash", hash_query},
|
||||||
@@ -2026,9 +2056,9 @@ static const struct luaL_Reg tests_funcs[] = {
|
|||||||
{"num2int", num2int},
|
{"num2int", num2int},
|
||||||
{"makeseed", makeseed},
|
{"makeseed", makeseed},
|
||||||
{"pushuserdata", pushuserdata},
|
{"pushuserdata", pushuserdata},
|
||||||
|
{"gcquery", gc_query},
|
||||||
{"querystr", string_query},
|
{"querystr", string_query},
|
||||||
{"querytab", table_query},
|
{"querytab", table_query},
|
||||||
{"queryGCparams", query_GCparams},
|
|
||||||
{"codeparam", test_codeparam},
|
{"codeparam", test_codeparam},
|
||||||
{"applyparam", test_applyparam},
|
{"applyparam", test_applyparam},
|
||||||
{"ref", tref},
|
{"ref", tref},
|
||||||
|
|||||||
4
ltests.h
4
ltests.h
@@ -58,6 +58,10 @@ typedef struct Memcontrol {
|
|||||||
LUA_API Memcontrol l_memcontrol;
|
LUA_API Memcontrol l_memcontrol;
|
||||||
|
|
||||||
|
|
||||||
|
#define luai_tracegc(L,f) luai_tracegctest(L, f)
|
||||||
|
LUAI_FUNC void luai_tracegctest (lua_State *L, int first);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** generic variable for debug tricks
|
** generic variable for debug tricks
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -799,7 +799,7 @@ assert(debug.getuservalue(b) == 134)
|
|||||||
-- test barrier for uservalues
|
-- test barrier for uservalues
|
||||||
do
|
do
|
||||||
local oldmode = collectgarbage("incremental")
|
local oldmode = collectgarbage("incremental")
|
||||||
T.gcstate("atomic")
|
T.gcstate("enteratomic")
|
||||||
assert(T.gccolor(b) == "black")
|
assert(T.gccolor(b) == "black")
|
||||||
debug.setuservalue(b, {x = 100})
|
debug.setuservalue(b, {x = 100})
|
||||||
T.gcstate("pause") -- complete collection
|
T.gcstate("pause") -- complete collection
|
||||||
|
|||||||
@@ -560,8 +560,8 @@ if T then -- tests for weird cases collecting upvalues
|
|||||||
-- create coroutine in a weak table, so it will never be marked
|
-- create coroutine in a weak table, so it will never be marked
|
||||||
t.co = coroutine.wrap(foo)
|
t.co = coroutine.wrap(foo)
|
||||||
local f = t.co() -- create function to access local 'a'
|
local f = t.co() -- create function to access local 'a'
|
||||||
T.gcstate("atomic") -- ensure all objects are traversed
|
T.gcstate("enteratomic") -- ensure all objects are traversed
|
||||||
assert(T.gcstate() == "atomic")
|
assert(T.gcstate() == "enteratomic")
|
||||||
assert(t.co() == 100) -- resume coroutine, creating new table for 'a'
|
assert(t.co() == 100) -- resume coroutine, creating new table for 'a'
|
||||||
assert(T.gccolor(t.co) == "white") -- thread was not traversed
|
assert(T.gccolor(t.co) == "white") -- thread was not traversed
|
||||||
T.gcstate("pause") -- collect thread, but should mark 'a' before that
|
T.gcstate("pause") -- collect thread, but should mark 'a' before that
|
||||||
@@ -574,7 +574,7 @@ if T then -- tests for weird cases collecting upvalues
|
|||||||
collectgarbage()
|
collectgarbage()
|
||||||
collectgarbage"stop"
|
collectgarbage"stop"
|
||||||
local a = {} -- avoid 'u' as first element in 'allgc'
|
local a = {} -- avoid 'u' as first element in 'allgc'
|
||||||
T.gcstate"atomic"
|
T.gcstate"enteratomic"
|
||||||
T.gcstate"sweepallgc"
|
T.gcstate"sweepallgc"
|
||||||
local x = {}
|
local x = {}
|
||||||
assert(T.gccolor(u) == "black") -- userdata is "old" (black)
|
assert(T.gccolor(u) == "black") -- userdata is "old" (black)
|
||||||
|
|||||||
Reference in New Issue
Block a user