aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ltests.c196
1 files changed, 133 insertions, 63 deletions
diff --git a/ltests.c b/ltests.c
index ac177de8..4022809c 100644
--- a/ltests.c
+++ b/ltests.c
@@ -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*/
189static int testobjref1 (global_State *g, GCObject *f, GCObject *t) { 197static 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
326static void checkobject (global_State *g, GCObject *o, int maybedead) { 336static 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
380static 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) {
371static void checkgraylist (global_State *g, GCObject *o) { 401static 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
403static void checkgray (global_State *g, GCObject *o) { 433static 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
444static 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
414int lua_checkmemory (lua_State *L) { 466int 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
688static 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
636static int gc_state (lua_State *L) { 703static 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},