diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2024-10-21 15:18:20 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2024-10-21 15:18:20 -0300 |
commit | 258355734d3aceb34eb6288ece37d9bbd7f2bc6d (patch) | |
tree | 692b2f43cffa8255f6221704af29fb94c6bbd323 | |
parent | d0815046d003f8f24efcdb03d35dd125ddd3b5f9 (diff) | |
download | lua-258355734d3aceb34eb6288ece37d9bbd7f2bc6d.tar.gz lua-258355734d3aceb34eb6288ece37d9bbd7f2bc6d.tar.bz2 lua-258355734d3aceb34eb6288ece37d9bbd7f2bc6d.zip |
Better support in 'ltests' for tracing the GC
-rw-r--r-- | lgc.c | 17 | ||||
-rw-r--r-- | ltests.c | 68 | ||||
-rw-r--r-- | ltests.h | 4 | ||||
-rw-r--r-- | testes/api.lua | 2 | ||||
-rw-r--r-- | testes/gc.lua | 6 |
5 files changed, 67 insertions, 30 deletions
@@ -1695,21 +1695,23 @@ static void incstep (lua_State *L, global_State *g) { | |||
1695 | 1695 | ||
1696 | 1696 | ||
1697 | #if !defined(luai_tracegc) | 1697 | #if !defined(luai_tracegc) |
1698 | #define luai_tracegc(L) ((void)0) | 1698 | #define luai_tracegc(L,f) ((void)0) |
1699 | #endif | 1699 | #endif |
1700 | 1700 | ||
1701 | /* | 1701 | /* |
1702 | ** Performs a basic GC step if collector is running. (If collector is | 1702 | ** Performs a basic GC step if collector is running. (If collector was |
1703 | ** not running, set a reasonable debt to avoid it being called at | 1703 | ** stopped by the user, set a reasonable debt to avoid it being called |
1704 | ** every single check.) | 1704 | ** at every single check.) |
1705 | */ | 1705 | */ |
1706 | void luaC_step (lua_State *L) { | 1706 | void luaC_step (lua_State *L) { |
1707 | global_State *g = G(L); | 1707 | global_State *g = G(L); |
1708 | lua_assert(!g->gcemergency); | 1708 | lua_assert(!g->gcemergency); |
1709 | if (!gcrunning(g)) /* not running? */ | 1709 | if (!gcrunning(g)) { /* not running? */ |
1710 | luaE_setdebt(g, 20000); | 1710 | if (g->gcstp & GCSTPUSR) /* stopped by the user? */ |
1711 | luaE_setdebt(g, 20000); | ||
1712 | } | ||
1711 | else { | 1713 | else { |
1712 | luai_tracegc(L); /* for internal debugging */ | 1714 | luai_tracegc(L, 1); /* for internal debugging */ |
1713 | switch (g->gckind) { | 1715 | switch (g->gckind) { |
1714 | case KGC_INC: case KGC_GENMAJOR: | 1716 | case KGC_INC: case KGC_GENMAJOR: |
1715 | incstep(L, g); | 1717 | incstep(L, g); |
@@ -1719,6 +1721,7 @@ void luaC_step (lua_State *L) { | |||
1719 | setminordebt(g); | 1721 | setminordebt(g); |
1720 | break; | 1722 | break; |
1721 | } | 1723 | } |
1724 | luai_tracegc(L, 0); /* for internal debugging */ | ||
1722 | } | 1725 | } |
1723 | } | 1726 | } |
1724 | 1727 | ||
@@ -944,34 +944,63 @@ static int gc_printobj (lua_State *L) { | |||
944 | } | 944 | } |
945 | 945 | ||
946 | 946 | ||
947 | static const char *statenames[] = { | ||
948 | "propagate", "enteratomic", "atomic", "sweepallgc", "sweepfinobj", | ||
949 | "sweeptobefnz", "sweepend", "callfin", "pause", ""}; | ||
950 | |||
947 | static int gc_state (lua_State *L) { | 951 | static int gc_state (lua_State *L) { |
948 | static const char *statenames[] = { | ||
949 | "propagate", "atomic", "sweepallgc", "sweepfinobj", | ||
950 | "sweeptobefnz", "sweepend", "callfin", "pause", ""}; | ||
951 | static const int states[] = { | 952 | static const int states[] = { |
952 | GCSpropagate, GCSenteratomic, GCSswpallgc, GCSswpfinobj, | 953 | GCSpropagate, GCSenteratomic, GCSatomic, GCSswpallgc, GCSswpfinobj, |
953 | GCSswptobefnz, GCSswpend, GCScallfin, GCSpause, -1}; | 954 | GCSswptobefnz, GCSswpend, GCScallfin, GCSpause, -1}; |
954 | int option = states[luaL_checkoption(L, 1, "", statenames)]; | 955 | int option = states[luaL_checkoption(L, 1, "", statenames)]; |
956 | global_State *g = G(L); | ||
955 | if (option == -1) { | 957 | if (option == -1) { |
956 | lua_pushstring(L, statenames[G(L)->gcstate]); | 958 | lua_pushstring(L, statenames[g->gcstate]); |
957 | return 1; | 959 | return 1; |
958 | } | 960 | } |
959 | else { | 961 | else { |
960 | global_State *g = G(L); | 962 | if (g->gckind != KGC_INC) |
961 | if (G(L)->gckind != KGC_INC) | ||
962 | luaL_error(L, "cannot change states in generational mode"); | 963 | luaL_error(L, "cannot change states in generational mode"); |
963 | lua_lock(L); | 964 | lua_lock(L); |
964 | if (option < g->gcstate) { /* must cross 'pause'? */ | 965 | if (option < g->gcstate) { /* must cross 'pause'? */ |
965 | luaC_runtilstate(L, GCSpause, 1); /* run until pause */ | 966 | luaC_runtilstate(L, GCSpause, 1); /* run until pause */ |
966 | } | 967 | } |
967 | luaC_runtilstate(L, option, 0); /* do not skip propagation state */ | 968 | luaC_runtilstate(L, option, 0); /* do not skip propagation state */ |
968 | lua_assert(G(L)->gcstate == option); | 969 | lua_assert(g->gcstate == option); |
969 | lua_unlock(L); | 970 | lua_unlock(L); |
970 | return 0; | 971 | return 0; |
971 | } | 972 | } |
972 | } | 973 | } |
973 | 974 | ||
974 | 975 | ||
976 | static int tracinggc = 0; | ||
977 | void luai_tracegctest (lua_State *L, int first) { | ||
978 | if (!tracinggc) return; | ||
979 | else { | ||
980 | global_State *g = G(L); | ||
981 | lua_unlock(L); | ||
982 | g->gcstp = GCSTPGC; | ||
983 | lua_checkstack(L, 10); | ||
984 | lua_getfield(L, LUA_REGISTRYINDEX, "tracegc"); | ||
985 | lua_pushboolean(L, first); | ||
986 | lua_call(L, 1, 0); | ||
987 | g->gcstp = 0; | ||
988 | lua_lock(L); | ||
989 | } | ||
990 | } | ||
991 | |||
992 | |||
993 | static int tracegc (lua_State *L) { | ||
994 | if (lua_isnil(L, 1)) | ||
995 | tracinggc = 0; | ||
996 | else { | ||
997 | tracinggc = 1; | ||
998 | lua_setfield(L, LUA_REGISTRYINDEX, "tracegc"); | ||
999 | } | ||
1000 | return 0; | ||
1001 | } | ||
1002 | |||
1003 | |||
975 | static int hash_query (lua_State *L) { | 1004 | static int hash_query (lua_State *L) { |
976 | if (lua_isnone(L, 2)) { | 1005 | if (lua_isnone(L, 2)) { |
977 | luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "string expected"); | 1006 | luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "string expected"); |
@@ -1038,17 +1067,17 @@ static int table_query (lua_State *L) { | |||
1038 | } | 1067 | } |
1039 | 1068 | ||
1040 | 1069 | ||
1041 | static int query_GCparams (lua_State *L) { | 1070 | static int gc_query (lua_State *L) { |
1042 | global_State *g = G(L); | 1071 | global_State *g = G(L); |
1043 | lua_pushinteger(L, cast(lua_Integer, gettotalbytes(g))); | 1072 | lua_pushstring(L, g->gckind == KGC_INC ? "inc" |
1044 | lua_pushinteger(L, cast(lua_Integer, g->GCdebt)); | 1073 | : g->gckind == KGC_GENMAJOR ? "genmajor" |
1045 | lua_pushinteger(L, cast(lua_Integer, applygcparam(g, MINORMUL, 100))); | 1074 | : "genminor"); |
1046 | lua_pushinteger(L, cast(lua_Integer, applygcparam(g, MAJORMINOR, 100))); | 1075 | lua_pushstring(L, statenames[g->gcstate]); |
1047 | lua_pushinteger(L, cast(lua_Integer, applygcparam(g, MINORMAJOR, 100))); | 1076 | lua_pushinteger(L, cast_st2S(gettotalbytes(g))); |
1048 | lua_pushinteger(L, cast(lua_Integer, applygcparam(g, PAUSE, 100))); | 1077 | lua_pushinteger(L, cast_st2S(g->GCdebt)); |
1049 | lua_pushinteger(L, cast(lua_Integer, applygcparam(g, STEPMUL, 100))); | 1078 | lua_pushinteger(L, cast_st2S(g->GCmarked)); |
1050 | lua_pushinteger(L, cast(lua_Integer, applygcparam(g, STEPSIZE, 100))); | 1079 | lua_pushinteger(L, cast_st2S(g->GCmajorminor)); |
1051 | return 8; | 1080 | return 6; |
1052 | } | 1081 | } |
1053 | 1082 | ||
1054 | 1083 | ||
@@ -2009,6 +2038,7 @@ static const struct luaL_Reg tests_funcs[] = { | |||
2009 | {"gccolor", gc_color}, | 2038 | {"gccolor", gc_color}, |
2010 | {"gcage", gc_age}, | 2039 | {"gcage", gc_age}, |
2011 | {"gcstate", gc_state}, | 2040 | {"gcstate", gc_state}, |
2041 | {"tracegc", tracegc}, | ||
2012 | {"pobj", gc_printobj}, | 2042 | {"pobj", gc_printobj}, |
2013 | {"getref", getref}, | 2043 | {"getref", getref}, |
2014 | {"hash", hash_query}, | 2044 | {"hash", hash_query}, |
@@ -2026,9 +2056,9 @@ static const struct luaL_Reg tests_funcs[] = { | |||
2026 | {"num2int", num2int}, | 2056 | {"num2int", num2int}, |
2027 | {"makeseed", makeseed}, | 2057 | {"makeseed", makeseed}, |
2028 | {"pushuserdata", pushuserdata}, | 2058 | {"pushuserdata", pushuserdata}, |
2059 | {"gcquery", gc_query}, | ||
2029 | {"querystr", string_query}, | 2060 | {"querystr", string_query}, |
2030 | {"querytab", table_query}, | 2061 | {"querytab", table_query}, |
2031 | {"queryGCparams", query_GCparams}, | ||
2032 | {"codeparam", test_codeparam}, | 2062 | {"codeparam", test_codeparam}, |
2033 | {"applyparam", test_applyparam}, | 2063 | {"applyparam", test_applyparam}, |
2034 | {"ref", tref}, | 2064 | {"ref", tref}, |
@@ -58,6 +58,10 @@ typedef struct Memcontrol { | |||
58 | LUA_API Memcontrol l_memcontrol; | 58 | LUA_API Memcontrol l_memcontrol; |
59 | 59 | ||
60 | 60 | ||
61 | #define luai_tracegc(L,f) luai_tracegctest(L, f) | ||
62 | LUAI_FUNC void luai_tracegctest (lua_State *L, int first); | ||
63 | |||
64 | |||
61 | /* | 65 | /* |
62 | ** generic variable for debug tricks | 66 | ** generic variable for debug tricks |
63 | */ | 67 | */ |
diff --git a/testes/api.lua b/testes/api.lua index ae2f82dd..b7e34f7f 100644 --- a/testes/api.lua +++ b/testes/api.lua | |||
@@ -799,7 +799,7 @@ assert(debug.getuservalue(b) == 134) | |||
799 | -- test barrier for uservalues | 799 | -- test barrier for uservalues |
800 | do | 800 | do |
801 | local oldmode = collectgarbage("incremental") | 801 | local oldmode = collectgarbage("incremental") |
802 | T.gcstate("atomic") | 802 | T.gcstate("enteratomic") |
803 | assert(T.gccolor(b) == "black") | 803 | assert(T.gccolor(b) == "black") |
804 | debug.setuservalue(b, {x = 100}) | 804 | debug.setuservalue(b, {x = 100}) |
805 | T.gcstate("pause") -- complete collection | 805 | T.gcstate("pause") -- complete collection |
diff --git a/testes/gc.lua b/testes/gc.lua index 3f8143b1..09bfe09a 100644 --- a/testes/gc.lua +++ b/testes/gc.lua | |||
@@ -560,8 +560,8 @@ if T then -- tests for weird cases collecting upvalues | |||
560 | -- create coroutine in a weak table, so it will never be marked | 560 | -- create coroutine in a weak table, so it will never be marked |
561 | t.co = coroutine.wrap(foo) | 561 | t.co = coroutine.wrap(foo) |
562 | local f = t.co() -- create function to access local 'a' | 562 | local f = t.co() -- create function to access local 'a' |
563 | T.gcstate("atomic") -- ensure all objects are traversed | 563 | T.gcstate("enteratomic") -- ensure all objects are traversed |
564 | assert(T.gcstate() == "atomic") | 564 | assert(T.gcstate() == "enteratomic") |
565 | assert(t.co() == 100) -- resume coroutine, creating new table for 'a' | 565 | assert(t.co() == 100) -- resume coroutine, creating new table for 'a' |
566 | assert(T.gccolor(t.co) == "white") -- thread was not traversed | 566 | assert(T.gccolor(t.co) == "white") -- thread was not traversed |
567 | T.gcstate("pause") -- collect thread, but should mark 'a' before that | 567 | T.gcstate("pause") -- collect thread, but should mark 'a' before that |
@@ -574,7 +574,7 @@ if T then -- tests for weird cases collecting upvalues | |||
574 | collectgarbage() | 574 | collectgarbage() |
575 | collectgarbage"stop" | 575 | collectgarbage"stop" |
576 | local a = {} -- avoid 'u' as first element in 'allgc' | 576 | local a = {} -- avoid 'u' as first element in 'allgc' |
577 | T.gcstate"atomic" | 577 | T.gcstate"enteratomic" |
578 | T.gcstate"sweepallgc" | 578 | T.gcstate"sweepallgc" |
579 | local x = {} | 579 | local x = {} |
580 | assert(T.gccolor(u) == "black") -- userdata is "old" (black) | 580 | assert(T.gccolor(u) == "black") -- userdata is "old" (black) |