diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2017-04-18 16:42:12 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2017-04-18 16:42:12 -0300 |
commit | 46792947965aa2b28232c212add07b08749e7adf (patch) | |
tree | b2ee7eff8ae0fd313520842ea0a4b2cc9f9d40f1 | |
parent | f74b87c3c29fb052adc681317a54dfbe4a5a281b (diff) | |
download | lua-46792947965aa2b28232c212add07b08749e7adf.tar.gz lua-46792947965aa2b28232c212add07b08749e7adf.tar.bz2 lua-46792947965aa2b28232c212add07b08749e7adf.zip |
memory check adapted to generational mode
-rw-r--r-- | ltests.c | 196 |
1 files changed, 133 insertions, 63 deletions
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | ** $Id: ltests.c,v 2.211 2016/12/04 20:17:24 roberto Exp roberto $ | 2 | ** $Id: ltests.c,v 2.212 2017/02/23 21:07:34 roberto Exp roberto $ |
3 | ** Internal Module for Debugging of the Lua Implementation | 3 | ** Internal Module for Debugging of the Lua Implementation |
4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
5 | */ | 5 | */ |
@@ -186,11 +186,22 @@ void *debug_realloc (void *ud, void *b, size_t oldsize, size_t size) { | |||
186 | */ | 186 | */ |
187 | 187 | ||
188 | 188 | ||
189 | /* | ||
190 | ** Check GC invariants. For incremental mode, a black object cannot | ||
191 | ** point to a white one. For generational mode, really old objects | ||
192 | ** cannot point to young objects. (Threads and open upvalues, despite | ||
193 | ** being marked "really old", continue to be visited in all collections, | ||
194 | ** and therefore can point to new objects. They, and only they, are | ||
195 | ** old but gray.) | ||
196 | */ | ||
189 | static int testobjref1 (global_State *g, GCObject *f, GCObject *t) { | 197 | static int testobjref1 (global_State *g, GCObject *f, GCObject *t) { |
190 | if (isdead(g,t)) return 0; | 198 | if (isdead(g,t)) return 0; |
191 | if (!issweepphase(g)) | 199 | if (issweepphase(g)) |
192 | return !(isblack(f) && iswhite(t)); | 200 | return 1; /* no invariants */ |
193 | else return 1; | 201 | else if (g->gckind == KGC_NORMAL) |
202 | return !(isblack(f) && iswhite(t)); /* basic incremental invariant */ | ||
203 | else | ||
204 | return !((getage(f) == G_OLD && isblack(f)) && !isold(t)); | ||
194 | } | 205 | } |
195 | 206 | ||
196 | 207 | ||
@@ -198,8 +209,7 @@ static void printobj (global_State *g, GCObject *o) { | |||
198 | printf("||%s(%p)-%c%c(%02X)||", | 209 | printf("||%s(%p)-%c%c(%02X)||", |
199 | ttypename(novariant(o->tt)), (void *)o, | 210 | ttypename(novariant(o->tt)), (void *)o, |
200 | isdead(g,o) ? 'd' : isblack(o) ? 'b' : iswhite(o) ? 'w' : 'g', | 211 | isdead(g,o) ? 'd' : isblack(o) ? 'b' : iswhite(o) ? 'w' : 'g', |
201 | testbit(o->marked, OLDBIT) ? 'o' : 'n', | 212 | "ns01oTt"[getage(o)], o->marked); |
202 | o->marked); | ||
203 | if (o->tt == LUA_TSHRSTR || o->tt == LUA_TLNGSTR) | 213 | if (o->tt == LUA_TSHRSTR || o->tt == LUA_TLNGSTR) |
204 | printf(" '%s'", getstr(gco2ts(o))); | 214 | printf(" '%s'", getstr(gco2ts(o))); |
205 | } | 215 | } |
@@ -282,9 +292,9 @@ static void checkLclosure (global_State *g, LClosure *cl) { | |||
282 | for (i=0; i<cl->nupvalues; i++) { | 292 | for (i=0; i<cl->nupvalues; i++) { |
283 | UpVal *uv = cl->upvals[i]; | 293 | UpVal *uv = cl->upvals[i]; |
284 | if (uv) { | 294 | if (uv) { |
285 | if (!upisopen(uv)) /* only closed upvalues matter to invariant */ | 295 | checkobjref(g, clgc, uv); |
286 | checkvalref(g, clgc, uv->v); | 296 | if (!upisopen(uv)) |
287 | lua_assert(uv->refcount > 0); | 297 | checkvalref(g, obj2gco(uv), uv->v); |
288 | } | 298 | } |
289 | } | 299 | } |
290 | } | 300 | } |
@@ -323,47 +333,67 @@ static void checkstack (global_State *g, lua_State *L1) { | |||
323 | } | 333 | } |
324 | 334 | ||
325 | 335 | ||
326 | static void checkobject (global_State *g, GCObject *o, int maybedead) { | 336 | static void checkrefs (global_State *g, GCObject *o) { |
337 | switch (o->tt) { | ||
338 | case LUA_TUSERDATA: { | ||
339 | TValue uservalue; | ||
340 | Table *mt = gco2u(o)->metatable; | ||
341 | checkobjref(g, o, mt); | ||
342 | getuservalue(g->mainthread, gco2u(o), &uservalue); | ||
343 | checkvalref(g, o, &uservalue); | ||
344 | break; | ||
345 | } | ||
346 | case LUA_TUPVAL: { | ||
347 | checkvalref(g, o, gco2upv(o)->v); | ||
348 | break; | ||
349 | } | ||
350 | case LUA_TTABLE: { | ||
351 | checktable(g, gco2t(o)); | ||
352 | break; | ||
353 | } | ||
354 | case LUA_TTHREAD: { | ||
355 | checkstack(g, gco2th(o)); | ||
356 | break; | ||
357 | } | ||
358 | case LUA_TLCL: { | ||
359 | checkLclosure(g, gco2lcl(o)); | ||
360 | break; | ||
361 | } | ||
362 | case LUA_TCCL: { | ||
363 | checkCclosure(g, gco2ccl(o)); | ||
364 | break; | ||
365 | } | ||
366 | case LUA_TPROTO: { | ||
367 | checkproto(g, gco2p(o)); | ||
368 | break; | ||
369 | } | ||
370 | case LUA_TSHRSTR: | ||
371 | case LUA_TLNGSTR: { | ||
372 | lua_assert(!isgray(o)); /* strings are never gray */ | ||
373 | break; | ||
374 | } | ||
375 | default: lua_assert(0); | ||
376 | } | ||
377 | } | ||
378 | |||
379 | |||
380 | static void checkobject (global_State *g, GCObject *o, int maybedead, | ||
381 | int listage) { | ||
327 | if (isdead(g, o)) | 382 | if (isdead(g, o)) |
328 | lua_assert(maybedead); | 383 | lua_assert(maybedead); |
329 | else { | 384 | else { |
330 | lua_assert(g->gcstate != GCSpause || iswhite(o)); | 385 | lua_assert(g->gcstate != GCSpause || iswhite(o)); |
331 | switch (o->tt) { | 386 | if (g->gckind == KGC_GEN) { /* generational mode? */ |
332 | case LUA_TUSERDATA: { | 387 | lua_assert(getage(o) >= listage); |
333 | TValue uservalue; | 388 | lua_assert(!iswhite(o) || !isold(o)); |
334 | Table *mt = gco2u(o)->metatable; | 389 | if (isold(o)) { |
335 | checkobjref(g, o, mt); | 390 | lua_assert(isblack(o) || |
336 | getuservalue(g->mainthread, gco2u(o), &uservalue); | 391 | getage(o) == G_TOUCHED1 || |
337 | checkvalref(g, o, &uservalue); | 392 | o->tt == LUA_TTHREAD || |
338 | break; | 393 | (o->tt == LUA_TUPVAL && upisopen(gco2upv(o)))); |
339 | } | 394 | } |
340 | case LUA_TTABLE: { | ||
341 | checktable(g, gco2t(o)); | ||
342 | break; | ||
343 | } | ||
344 | case LUA_TTHREAD: { | ||
345 | checkstack(g, gco2th(o)); | ||
346 | break; | ||
347 | } | ||
348 | case LUA_TLCL: { | ||
349 | checkLclosure(g, gco2lcl(o)); | ||
350 | break; | ||
351 | } | ||
352 | case LUA_TCCL: { | ||
353 | checkCclosure(g, gco2ccl(o)); | ||
354 | break; | ||
355 | } | ||
356 | case LUA_TPROTO: { | ||
357 | checkproto(g, gco2p(o)); | ||
358 | break; | ||
359 | } | ||
360 | case LUA_TSHRSTR: | ||
361 | case LUA_TLNGSTR: { | ||
362 | lua_assert(!isgray(o)); /* strings are never gray */ | ||
363 | break; | ||
364 | } | ||
365 | default: lua_assert(0); | ||
366 | } | 395 | } |
396 | checkrefs(g, o); | ||
367 | } | 397 | } |
368 | } | 398 | } |
369 | 399 | ||
@@ -371,7 +401,7 @@ static void checkobject (global_State *g, GCObject *o, int maybedead) { | |||
371 | static void checkgraylist (global_State *g, GCObject *o) { | 401 | static void checkgraylist (global_State *g, GCObject *o) { |
372 | ((void)g); /* better to keep it available if we need to print an object */ | 402 | ((void)g); /* better to keep it available if we need to print an object */ |
373 | while (o) { | 403 | while (o) { |
374 | lua_assert(isgray(o)); | 404 | lua_assert(isgray(o) || getage(o) == G_TOUCHED2); |
375 | lua_assert(!testbit(o->marked, TESTGRAYBIT)); | 405 | lua_assert(!testbit(o->marked, TESTGRAYBIT)); |
376 | l_setbit(o->marked, TESTGRAYBIT); | 406 | l_setbit(o->marked, TESTGRAYBIT); |
377 | switch (o->tt) { | 407 | switch (o->tt) { |
@@ -380,7 +410,7 @@ static void checkgraylist (global_State *g, GCObject *o) { | |||
380 | case LUA_TCCL: o = gco2ccl(o)->gclist; break; | 410 | case LUA_TCCL: o = gco2ccl(o)->gclist; break; |
381 | case LUA_TTHREAD: o = gco2th(o)->gclist; break; | 411 | case LUA_TTHREAD: o = gco2th(o)->gclist; break; |
382 | case LUA_TPROTO: o = gco2p(o)->gclist; break; | 412 | case LUA_TPROTO: o = gco2p(o)->gclist; break; |
383 | default: lua_assert(0); /* other objects cannot be gray */ | 413 | default: lua_assert(0); /* other objects cannot be in a gray list */ |
384 | } | 414 | } |
385 | } | 415 | } |
386 | } | 416 | } |
@@ -402,7 +432,7 @@ static void markgrays (global_State *g) { | |||
402 | 432 | ||
403 | static void checkgray (global_State *g, GCObject *o) { | 433 | static void checkgray (global_State *g, GCObject *o) { |
404 | for (; o != NULL; o = o->next) { | 434 | for (; o != NULL; o = o->next) { |
405 | if (isgray(o)) { | 435 | if ((isgray(o) && o->tt != LUA_TUPVAL) || getage(o) == G_TOUCHED2) { |
406 | lua_assert(!keepinvariant(g) || testbit(o->marked, TESTGRAYBIT)); | 436 | lua_assert(!keepinvariant(g) || testbit(o->marked, TESTGRAYBIT)); |
407 | resetbit(o->marked, TESTGRAYBIT); | 437 | resetbit(o->marked, TESTGRAYBIT); |
408 | } | 438 | } |
@@ -411,6 +441,28 @@ static void checkgray (global_State *g, GCObject *o) { | |||
411 | } | 441 | } |
412 | 442 | ||
413 | 443 | ||
444 | static void checklist (global_State *g, int maybedead, int tof, | ||
445 | GCObject *new, GCObject *survival, GCObject *old, GCObject *reallyold) { | ||
446 | GCObject *o; | ||
447 | for (o = new; o != survival; o = o->next) { | ||
448 | checkobject(g, o, maybedead, G_NEW); | ||
449 | lua_assert(!tof == !tofinalize(o)); | ||
450 | } | ||
451 | for (o = survival; o != old; o = o->next) { | ||
452 | checkobject(g, o, 0, G_SURVIVAL); | ||
453 | lua_assert(!tof == !tofinalize(o)); | ||
454 | } | ||
455 | for (o = old; o != reallyold; o = o->next) { | ||
456 | checkobject(g, o, 0, G_OLD1); | ||
457 | lua_assert(!tof == !tofinalize(o)); | ||
458 | } | ||
459 | for (o = reallyold; o != NULL; o = o->next) { | ||
460 | checkobject(g, o, 0, G_OLD); | ||
461 | lua_assert(!tof == !tofinalize(o)); | ||
462 | } | ||
463 | } | ||
464 | |||
465 | |||
414 | int lua_checkmemory (lua_State *L) { | 466 | int lua_checkmemory (lua_State *L) { |
415 | global_State *g = G(L); | 467 | global_State *g = G(L); |
416 | GCObject *o; | 468 | GCObject *o; |
@@ -420,32 +472,27 @@ int lua_checkmemory (lua_State *L) { | |||
420 | lua_assert(!iswhite(gcvalue(&g->l_registry))); | 472 | lua_assert(!iswhite(gcvalue(&g->l_registry))); |
421 | } | 473 | } |
422 | lua_assert(!isdead(g, gcvalue(&g->l_registry))); | 474 | lua_assert(!isdead(g, gcvalue(&g->l_registry))); |
423 | checkstack(g, g->mainthread); | ||
424 | resetbit(g->mainthread->marked, TESTGRAYBIT); | ||
425 | lua_assert(g->sweepgc == NULL || issweepphase(g)); | 475 | lua_assert(g->sweepgc == NULL || issweepphase(g)); |
426 | markgrays(g); | 476 | markgrays(g); |
477 | |||
427 | /* check 'fixedgc' list */ | 478 | /* check 'fixedgc' list */ |
428 | for (o = g->fixedgc; o != NULL; o = o->next) { | 479 | for (o = g->fixedgc; o != NULL; o = o->next) { |
429 | lua_assert(o->tt == LUA_TSHRSTR && isgray(o)); | 480 | lua_assert(o->tt == LUA_TSHRSTR && isgray(o) && getage(o) == G_OLD); |
430 | } | 481 | } |
482 | |||
431 | /* check 'allgc' list */ | 483 | /* check 'allgc' list */ |
432 | checkgray(g, g->allgc); | 484 | checkgray(g, g->allgc); |
433 | maybedead = (GCSatomic < g->gcstate && g->gcstate <= GCSswpallgc); | 485 | maybedead = (GCSatomic < g->gcstate && g->gcstate <= GCSswpallgc); |
434 | for (o = g->allgc; o != NULL; o = o->next) { | 486 | checklist(g, maybedead, 0, g->allgc, g->survival, g->old, g->reallyold); |
435 | checkobject(g, o, maybedead); | 487 | |
436 | lua_assert(!tofinalize(o)); | ||
437 | } | ||
438 | /* check 'finobj' list */ | 488 | /* check 'finobj' list */ |
439 | checkgray(g, g->finobj); | 489 | checkgray(g, g->finobj); |
440 | for (o = g->finobj; o != NULL; o = o->next) { | 490 | checklist(g, 0, 1, g->finobj, g->finobjsur, g->finobjold, g->finobjrold); |
441 | checkobject(g, o, 0); | 491 | |
442 | lua_assert(tofinalize(o)); | ||
443 | lua_assert(o->tt == LUA_TUSERDATA || o->tt == LUA_TTABLE); | ||
444 | } | ||
445 | /* check 'tobefnz' list */ | 492 | /* check 'tobefnz' list */ |
446 | checkgray(g, g->tobefnz); | 493 | checkgray(g, g->tobefnz); |
447 | for (o = g->tobefnz; o != NULL; o = o->next) { | 494 | for (o = g->tobefnz; o != NULL; o = o->next) { |
448 | checkobject(g, o, 0); | 495 | checkobject(g, o, 0, G_NEW); |
449 | lua_assert(tofinalize(o)); | 496 | lua_assert(tofinalize(o)); |
450 | lua_assert(o->tt == LUA_TUSERDATA || o->tt == LUA_TTABLE); | 497 | lua_assert(o->tt == LUA_TUSERDATA || o->tt == LUA_TTABLE); |
451 | } | 498 | } |
@@ -621,22 +668,42 @@ static int gc_color (lua_State *L) { | |||
621 | TValue *o; | 668 | TValue *o; |
622 | luaL_checkany(L, 1); | 669 | luaL_checkany(L, 1); |
623 | o = obj_at(L, 1); | 670 | o = obj_at(L, 1); |
624 | if (!iscollectable(o)) | 671 | if (!iscollectable(o)) { |
625 | lua_pushstring(L, "no collectable"); | 672 | lua_pushstring(L, "no collectable"); |
673 | return 1; | ||
674 | } | ||
626 | else { | 675 | else { |
676 | static const char *gennames[] = {"new", "survival", "old0", "old1", | ||
677 | "old", "touched1", "touched2"}; | ||
627 | GCObject *obj = gcvalue(o); | 678 | GCObject *obj = gcvalue(o); |
628 | lua_pushstring(L, isdead(G(L), obj) ? "dead" : | 679 | lua_pushstring(L, isdead(G(L), obj) ? "dead" : |
629 | iswhite(obj) ? "white" : | 680 | iswhite(obj) ? "white" : |
630 | isblack(obj) ? "black" : "grey"); | 681 | isblack(obj) ? "black" : "grey"); |
682 | lua_pushstring(L, gennames[getage(obj)]); | ||
683 | return 2; | ||
631 | } | 684 | } |
632 | return 1; | 685 | } |
686 | |||
687 | |||
688 | static int gc_printobj (lua_State *L) { | ||
689 | TValue *o; | ||
690 | luaL_checkany(L, 1); | ||
691 | o = obj_at(L, 1); | ||
692 | if (!iscollectable(o)) | ||
693 | printf("no collectable\n"); | ||
694 | else { | ||
695 | GCObject *obj = gcvalue(o); | ||
696 | printobj(G(L), obj); | ||
697 | printf("\n"); | ||
698 | } | ||
699 | return 0; | ||
633 | } | 700 | } |
634 | 701 | ||
635 | 702 | ||
636 | static int gc_state (lua_State *L) { | 703 | static int gc_state (lua_State *L) { |
637 | static const char *statenames[] = {"propagate", "atomic", "sweepallgc", | 704 | static const char *statenames[] = {"propagate", "atomic", "sweepallgc", |
638 | "sweepfinobj", "sweeptobefnz", "sweepend", "pause", ""}; | 705 | "sweepfinobj", "sweeptobefnz", "sweepend", "pause", ""}; |
639 | static const int states[] = {GCSpropagate, GCSatomic, GCSswpallgc, | 706 | static const int states[] = {GCSpropagate, GCSenteratomic, GCSswpallgc, |
640 | GCSswpfinobj, GCSswptobefnz, GCSswpend, GCSpause, -1}; | 707 | GCSswpfinobj, GCSswptobefnz, GCSswpend, GCSpause, -1}; |
641 | int option = states[luaL_checkoption(L, 1, "", statenames)]; | 708 | int option = states[luaL_checkoption(L, 1, "", statenames)]; |
642 | if (option == -1) { | 709 | if (option == -1) { |
@@ -645,6 +712,8 @@ static int gc_state (lua_State *L) { | |||
645 | } | 712 | } |
646 | else { | 713 | else { |
647 | global_State *g = G(L); | 714 | global_State *g = G(L); |
715 | if (G(L)->gckind == KGC_GEN) | ||
716 | luaL_error(L, "cannot change states in generational mode"); | ||
648 | lua_lock(L); | 717 | lua_lock(L); |
649 | if (option < g->gcstate) { /* must cross 'pause'? */ | 718 | if (option < g->gcstate) { /* must cross 'pause'? */ |
650 | luaC_runtilstate(L, bitmask(GCSpause)); /* run until pause */ | 719 | luaC_runtilstate(L, bitmask(GCSpause)); /* run until pause */ |
@@ -1519,6 +1588,7 @@ static const struct luaL_Reg tests_funcs[] = { | |||
1519 | {"doremote", doremote}, | 1588 | {"doremote", doremote}, |
1520 | {"gccolor", gc_color}, | 1589 | {"gccolor", gc_color}, |
1521 | {"gcstate", gc_state}, | 1590 | {"gcstate", gc_state}, |
1591 | {"pobj", gc_printobj}, | ||
1522 | {"getref", getref}, | 1592 | {"getref", getref}, |
1523 | {"hash", hash_query}, | 1593 | {"hash", hash_query}, |
1524 | {"int2fb", int2fb_aux}, | 1594 | {"int2fb", int2fb_aux}, |