diff --git a/src/lgc.c b/src/lgc.c index 116a058..64254de 100644 --- a/src/lgc.c +++ b/src/lgc.c @@ -106,6 +106,7 @@ static uint32_t trace_heaps = 0; static int local_collection(lua_State *L, int type); static int global_trace(lua_State *L); static void unblock_mutators(lua_State *L); +static void validate_heap_objects(lua_State *L, const char *where); static INLINE int is_black(lua_State *L, GCheader *obj) { @@ -1656,6 +1657,8 @@ void luaC_inherit_thread(lua_State *L, lua_State *th) ck_sequence_write_end(&th->memlock); luaE_flush_stringtable(th); + validate_heap_objects(L, "inherit_thread:before_transfer"); + TAILQ_FOREACH_SAFE(steal, &th->heap->objects, allocd, tmp) { /* Update owner before removing from source heap. This ensures that * if any concurrent reader sees this object, it will either: @@ -1670,6 +1673,9 @@ void luaC_inherit_thread(lua_State *L, lua_State *th) make_grey(L, steal); } + + validate_heap_objects(L, "inherit_thread:after_transfer"); + TAILQ_REMOVE(&G(L)->all_heaps, th->heap, heaps); unlock_all_threads(); @@ -1954,6 +1960,44 @@ static void sanity_check_mark_status(lua_State *L) } } +static int is_bad_gc_ptr(uintptr_t p) +{ + return p == 0x5a5a5a5a5a5a5a5aULL || p == 0 || (p & 0x7) != 0; +} + +static void validate_heap_objects(lua_State *L, const char *where) +{ + GCheader *o; + int count = 0; + + TAILQ_FOREACH(o, &L->heap->objects, allocd) { + if (is_bad_gc_ptr((uintptr_t)o) || + o->tt < LUA_TSTRING || o->tt > LUA_TGLOBAL || + (o->marked & FREEDBIT)) { + if (!is_bad_gc_ptr((uintptr_t)o)) + thrlua_log(L, DCRITICAL, + "thrlua HEAP CORRUPT [%s] L=%p heap=%p count=%d bad_node=%p" + " tt=%d marked=0x%x\n", + where, (void*)L, (void*)L->heap, count, (void*)o, + o->tt, o->marked); + else + thrlua_log(L, DCRITICAL, + "thrlua HEAP CORRUPT [%s] L=%p heap=%p count=%d bad_node=%p\n", + where, (void*)L, (void*)L->heap, count, (void*)o); + abort(); + } + if (o->owner != L->heap) { + thrlua_log(L, DCRITICAL, + "thrlua HEAP CORRUPT [%s] L=%p heap=%p count=%d node=%p" + " owner=%p (expected %p) tt=%d\n", + where, (void*)L, (void*)L->heap, count, (void*)o, + (void*)o->owner, (void*)L->heap, o->tt); + abort(); + } + count++; + } +} + static int local_collection(lua_State *L, int type) { int reclaimed; @@ -1974,6 +2018,8 @@ static int local_collection(lua_State *L, int type) * while we are in this function and manipulating our string tables or heap */ block_collector(L, pt); + validate_heap_objects(L, "local_collection:entry"); + /* prune out excess string table entries. * We don't want to be too aggressive, as we'd like to see some benefit * from string interning. We remove the head of each chain and repeat @@ -2020,6 +2066,8 @@ static int local_collection(lua_State *L, int type) check_references(L); } + validate_heap_objects(L, "local_collection:after_mark"); + /* run any finalizers; may turn some objects grey again */ run_finalize(L); @@ -2035,26 +2083,34 @@ static int local_collection(lua_State *L, int type) /* remove collected weak values from weak tables */ fixup_weak_refs(L); + validate_heap_objects(L, "local_collection:before_reclaim"); + /* and now we can free whatever is left in White. Note that we're still * blocked here so we are pulling white out of the heap and placing them * in another list that will free them when we unblock the collector. */ reclaimed = reclaim_white(L, 0); + validate_heap_objects(L, "local_collection:after_reclaim"); + /* White is the new Black */ L->black = !L->black; sanity_check_mark_status(L); + /* Finalize deferred objects */ + finalize_deferred(L); + + validate_heap_objects(L, "local_collection:after_finalize"); + /* Now we can un-block the global collector, as we are done with our string * tables and our heap. */ unblock_collector(L, pt); - /* Finalize deferred objects */ - finalize_deferred(L); - /* Free any objects that were white */ free_deferred_white(L); + validate_heap_objects(L, "local_collection:after_free"); + /* Free any deferred stringtable nodes */ while (tofree) { n = tofree; @@ -2119,6 +2175,7 @@ static void trace_heap(GCheap *h) GCheader *o; ck_pr_store_32(&h->owner->xref_count, 0); + validate_heap_objects(h->owner, "trace_heap"); TAILQ_FOREACH(o, &h->objects, allocd) { global_trace_obj(h->owner, &h->owner->gch, o); }