Better support in 'ltests' for tracing the GC

This commit is contained in:
Roberto Ierusalimschy
2024-10-21 15:18:20 -03:00
parent d0815046d0
commit 258355734d
5 changed files with 67 additions and 30 deletions

17
lgc.c
View File

@@ -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 */
} }
} }

View File

@@ -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},

View File

@@ -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
*/ */

View File

@@ -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

View File

@@ -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)