mirror of
https://github.com/NixOS/nix.git
synced 2025-11-14 22:42:41 +01:00
Add some GC diagnostics functions
This commit is contained in:
parent
4d74e67aa8
commit
3ce1ecd63b
2 changed files with 199 additions and 40 deletions
|
|
@ -245,6 +245,8 @@ void GC::gc()
|
||||||
processStack();
|
processStack();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto afterMark = steady_time_point::clock::now();
|
||||||
|
|
||||||
// Reset all the freelists.
|
// Reset all the freelists.
|
||||||
for (auto & freeList : freeLists)
|
for (auto & freeList : freeLists)
|
||||||
freeList.front = nullptr;
|
freeList.front = nullptr;
|
||||||
|
|
@ -253,28 +255,71 @@ void GC::gc()
|
||||||
// appropriate freelists.
|
// appropriate freelists.
|
||||||
size_t totalObjectsFreed = 0;
|
size_t totalObjectsFreed = 0;
|
||||||
size_t totalWordsFreed = 0;
|
size_t totalWordsFreed = 0;
|
||||||
|
size_t totalObjectsKept = 0;
|
||||||
|
size_t totalWordsKept = 0;
|
||||||
|
|
||||||
for (auto & arena : arenas) {
|
for (auto & arena : arenas) {
|
||||||
auto [objectsFreed, wordsFreed] = freeUnmarked(arena);
|
auto [objectsFreed, wordsFreed, objectsKept, wordsKept] = freeUnmarked(arena);
|
||||||
totalObjectsFreed += objectsFreed;
|
totalObjectsFreed += objectsFreed;
|
||||||
totalWordsFreed += wordsFreed;
|
totalWordsFreed += wordsFreed;
|
||||||
|
totalObjectsKept += objectsKept;
|
||||||
|
totalWordsKept += wordsKept;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto after = steady_time_point::clock::now();
|
auto after = steady_time_point::clock::now();
|
||||||
|
|
||||||
auto durationMs = std::chrono::duration_cast<std::chrono::milliseconds>(after - before).count();
|
auto markDurationMs = std::chrono::duration_cast<std::chrono::milliseconds>(afterMark - before).count();
|
||||||
|
auto sweepDurationMs = std::chrono::duration_cast<std::chrono::milliseconds>(after - afterMark).count();
|
||||||
|
|
||||||
debug("freed %d bytes in %d dead objects, keeping %d objects, in %d ms",
|
printError("freed %d dead objects (%d bytes), keeping %d/%d objects (%d bytes), marked in %d ms, swept in %d ms",
|
||||||
totalWordsFreed * WORD_SIZE, totalObjectsFreed, marked, durationMs);
|
totalObjectsFreed, totalWordsFreed * WORD_SIZE,
|
||||||
|
marked, totalObjectsKept, totalWordsKept * WORD_SIZE,
|
||||||
|
markDurationMs, sweepDurationMs);
|
||||||
|
|
||||||
allTimeWordsFreed += totalWordsFreed;
|
allTimeWordsFreed += totalWordsFreed;
|
||||||
totalDurationMs += durationMs;
|
totalDurationMs += markDurationMs + sweepDurationMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<size_t, size_t> GC::freeUnmarked(Arena & arena)
|
size_t GC::getObjectSize(Object * obj)
|
||||||
|
{
|
||||||
|
auto tag = obj->type;
|
||||||
|
if (tag >= tInt && tag <= tFloat) {
|
||||||
|
return ((Value *) obj)->words();
|
||||||
|
} else {
|
||||||
|
switch (tag) {
|
||||||
|
case tFree:
|
||||||
|
return ((Free *) obj)->words();
|
||||||
|
break;
|
||||||
|
case tString:
|
||||||
|
return ((String *) obj)->words();
|
||||||
|
break;
|
||||||
|
case tBindings:
|
||||||
|
return ((Bindings *) obj)->words();
|
||||||
|
break;
|
||||||
|
case tValueList:
|
||||||
|
return ((PtrList<Value> *) obj)->words();
|
||||||
|
break;
|
||||||
|
case tEnv:
|
||||||
|
case tWithExprEnv:
|
||||||
|
case tWithAttrsEnv:
|
||||||
|
return ((Env *) obj)->words();
|
||||||
|
break;
|
||||||
|
case tContext:
|
||||||
|
return ((Context *) obj)->getSize() + 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printError("GC encountered invalid object with tag %d", tag);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::tuple<size_t, size_t, size_t, size_t> GC::freeUnmarked(Arena & arena)
|
||||||
{
|
{
|
||||||
size_t objectsFreed = 0;
|
size_t objectsFreed = 0;
|
||||||
size_t wordsFreed = 0;
|
size_t wordsFreed = 0;
|
||||||
|
size_t objectsKept = 0;
|
||||||
|
size_t wordsKept = 0;
|
||||||
|
|
||||||
auto end = arena.start + arena.size;
|
auto end = arena.start + arena.size;
|
||||||
auto pos = arena.start;
|
auto pos = arena.start;
|
||||||
|
|
@ -289,38 +334,8 @@ std::pair<size_t, size_t> GC::freeUnmarked(Arena & arena)
|
||||||
|
|
||||||
while (pos < end) {
|
while (pos < end) {
|
||||||
auto obj = (Object *) pos;
|
auto obj = (Object *) pos;
|
||||||
auto tag = obj->type;
|
|
||||||
|
|
||||||
size_t objSize;
|
auto objSize = getObjectSize(obj);
|
||||||
if (tag >= tInt && tag <= tFloat) {
|
|
||||||
objSize = ((Value *) obj)->words();
|
|
||||||
} else {
|
|
||||||
switch (tag) {
|
|
||||||
case tFree:
|
|
||||||
objSize = ((Free *) obj)->words();
|
|
||||||
break;
|
|
||||||
case tString:
|
|
||||||
objSize = ((String *) obj)->words();
|
|
||||||
break;
|
|
||||||
case tBindings:
|
|
||||||
objSize = ((Bindings *) obj)->words();
|
|
||||||
break;
|
|
||||||
case tValueList:
|
|
||||||
objSize = ((PtrList<Value> *) obj)->words();
|
|
||||||
break;
|
|
||||||
case tEnv:
|
|
||||||
case tWithExprEnv:
|
|
||||||
case tWithAttrsEnv:
|
|
||||||
objSize = ((Env *) obj)->words();
|
|
||||||
break;
|
|
||||||
case tContext:
|
|
||||||
objSize = ((Context *) obj)->getSize() + 1;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
printError("GC encountered invalid object with tag %d", tag);
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Merge current object into the previous free object.
|
// Merge current object into the previous free object.
|
||||||
auto mergeFree = [&]() {
|
auto mergeFree = [&]() {
|
||||||
|
|
@ -329,7 +344,7 @@ std::pair<size_t, size_t> GC::freeUnmarked(Arena & arena)
|
||||||
curFree->setSize(curFree->words() + objSize);
|
curFree->setSize(curFree->words() + objSize);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (tag == tFree) {
|
if (obj->type == tFree) {
|
||||||
//debug("KEEP FREE %x %d", obj, obj->getMisc());
|
//debug("KEEP FREE %x %d", obj, obj->getMisc());
|
||||||
if (curFree) {
|
if (curFree) {
|
||||||
// Merge this object into the previous free
|
// Merge this object into the previous free
|
||||||
|
|
@ -345,6 +360,8 @@ std::pair<size_t, size_t> GC::freeUnmarked(Arena & arena)
|
||||||
//debug("KEEP OBJECT %x %d %d", obj, obj->type, objSize);
|
//debug("KEEP OBJECT %x %d %d", obj, obj->type, objSize);
|
||||||
linkCurFree();
|
linkCurFree();
|
||||||
obj->unmark();
|
obj->unmark();
|
||||||
|
objectsKept += 1;
|
||||||
|
wordsKept += objSize;
|
||||||
} else {
|
} else {
|
||||||
//debug("FREE OBJECT %x %d %d", obj, obj->type, objSize);
|
//debug("FREE OBJECT %x %d %d", obj, obj->type, objSize);
|
||||||
#if GC_DEBUG
|
#if GC_DEBUG
|
||||||
|
|
@ -371,7 +388,7 @@ std::pair<size_t, size_t> GC::freeUnmarked(Arena & arena)
|
||||||
|
|
||||||
assert(pos == end);
|
assert(pos == end);
|
||||||
|
|
||||||
return {objectsFreed, wordsFreed};
|
return {objectsFreed, wordsFreed, objectsKept, wordsKept};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GC::isObject(void * p)
|
bool GC::isObject(void * p)
|
||||||
|
|
@ -382,4 +399,139 @@ bool GC::isObject(void * p)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::tuple<size_t, size_t> GC::getObjectClosureSize(Object * p)
|
||||||
|
{
|
||||||
|
std::unordered_set<Object *> seen;
|
||||||
|
|
||||||
|
// FIXME: cut&paste.
|
||||||
|
|
||||||
|
std::stack<Object *> stack;
|
||||||
|
|
||||||
|
// FIXME: ensure this gets inlined.
|
||||||
|
auto push = [&](Object * p) { if (p) { assertObject(p); stack.push(p); } };
|
||||||
|
|
||||||
|
auto pushPointers = [&](Object * obj) {
|
||||||
|
switch (obj->type) {
|
||||||
|
|
||||||
|
case tFree:
|
||||||
|
printError("reached a freed object at %x", obj);
|
||||||
|
abort();
|
||||||
|
|
||||||
|
case tBindings: {
|
||||||
|
auto obj2 = (Bindings *) obj;
|
||||||
|
for (auto i = obj2->attrs; i < obj2->attrs + obj2->size_; ++i)
|
||||||
|
push(i->value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case tValueList: {
|
||||||
|
auto obj2 = (PtrList<Object> *) obj;
|
||||||
|
for (auto i = obj2->elems; i < obj2->elems + obj2->size(); ++i)
|
||||||
|
push(*i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case tEnv: {
|
||||||
|
auto obj2 = (Env *) obj;
|
||||||
|
push(obj2->up);
|
||||||
|
for (auto i = obj2->values; i < obj2->values + obj2->getSize(); ++i)
|
||||||
|
push(*i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case tWithExprEnv: {
|
||||||
|
auto obj2 = (Env *) obj;
|
||||||
|
push(obj2->up);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case tWithAttrsEnv: {
|
||||||
|
auto obj2 = (Env *) obj;
|
||||||
|
push(obj2->up);
|
||||||
|
push(obj2->values[0]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case tString:
|
||||||
|
case tContext:
|
||||||
|
case tInt:
|
||||||
|
case tBool:
|
||||||
|
case tNull:
|
||||||
|
case tList0:
|
||||||
|
case tFloat:
|
||||||
|
case tShortString:
|
||||||
|
case tStaticString:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case tLongString: {
|
||||||
|
auto obj2 = (Value *) obj;
|
||||||
|
push(obj2->string.s);
|
||||||
|
// See setContext().
|
||||||
|
if (!(((ptrdiff_t) obj2->string.context) & 1))
|
||||||
|
push(obj2->string.context);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case tPath:
|
||||||
|
push(((Value *) obj)->path);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case tAttrs:
|
||||||
|
push(((Value *) obj)->attrs);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case tList1:
|
||||||
|
push(((Value *) obj)->smallList[0]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case tList2:
|
||||||
|
push(((Value *) obj)->smallList[0]);
|
||||||
|
push(((Value *) obj)->smallList[1]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case tListN:
|
||||||
|
push(((Value *) obj)->bigList);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case tThunk:
|
||||||
|
case tBlackhole:
|
||||||
|
push(((Value *) obj)->thunk.env);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case tApp:
|
||||||
|
case tPrimOpApp:
|
||||||
|
push(((Value *) obj)->app.left);
|
||||||
|
push(((Value *) obj)->app.right);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case tLambda:
|
||||||
|
push(((Value *) obj)->lambda.env);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case tPrimOp:
|
||||||
|
// FIXME: GC primops?
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
printError("don't know how to traverse object at %x (tag %d)", obj, obj->type);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
stack.push(p);
|
||||||
|
|
||||||
|
size_t totalSize = 0;
|
||||||
|
|
||||||
|
while (!stack.empty()) {
|
||||||
|
auto obj = stack.top();
|
||||||
|
stack.pop();
|
||||||
|
if (seen.insert(obj).second) {
|
||||||
|
pushPointers(obj);
|
||||||
|
totalSize += getObjectSize(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {seen.size(), totalSize};
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -286,13 +286,20 @@ public:
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return the size in words of object 'obj'. */
|
||||||
|
static size_t getObjectSize(Object * obj);
|
||||||
|
|
||||||
|
/* Return the number and size in words of the objects reachable
|
||||||
|
from object 'obj'. */
|
||||||
|
std::tuple<size_t, size_t> getObjectClosureSize(Object * obj);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void addArena(size_t arenaSize);
|
void addArena(size_t arenaSize);
|
||||||
|
|
||||||
void addToFreeList(Free * obj);
|
void addToFreeList(Free * obj);
|
||||||
|
|
||||||
std::pair<size_t, size_t> freeUnmarked(Arena & arena);
|
std::tuple<size_t, size_t, size_t, size_t> freeUnmarked(Arena & arena);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern GC gc;
|
extern GC gc;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue