diff options
| -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}, |
