mirror of
https://github.com/NixOS/nix.git
synced 2025-11-14 14:32:42 +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();
|
||||
}
|
||||
|
||||
auto afterMark = steady_time_point::clock::now();
|
||||
|
||||
// Reset all the freelists.
|
||||
for (auto & freeList : freeLists)
|
||||
freeList.front = nullptr;
|
||||
|
|
@ -253,28 +255,71 @@ void GC::gc()
|
|||
// appropriate freelists.
|
||||
size_t totalObjectsFreed = 0;
|
||||
size_t totalWordsFreed = 0;
|
||||
size_t totalObjectsKept = 0;
|
||||
size_t totalWordsKept = 0;
|
||||
|
||||
for (auto & arena : arenas) {
|
||||
auto [objectsFreed, wordsFreed] = freeUnmarked(arena);
|
||||
auto [objectsFreed, wordsFreed, objectsKept, wordsKept] = freeUnmarked(arena);
|
||||
totalObjectsFreed += objectsFreed;
|
||||
totalWordsFreed += wordsFreed;
|
||||
totalObjectsKept += objectsKept;
|
||||
totalWordsKept += wordsKept;
|
||||
}
|
||||
|
||||
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",
|
||||
totalWordsFreed * WORD_SIZE, totalObjectsFreed, marked, durationMs);
|
||||
printError("freed %d dead objects (%d bytes), keeping %d/%d objects (%d bytes), marked in %d ms, swept in %d ms",
|
||||
totalObjectsFreed, totalWordsFreed * WORD_SIZE,
|
||||
marked, totalObjectsKept, totalWordsKept * WORD_SIZE,
|
||||
markDurationMs, sweepDurationMs);
|
||||
|
||||
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 wordsFreed = 0;
|
||||
size_t objectsKept = 0;
|
||||
size_t wordsKept = 0;
|
||||
|
||||
auto end = arena.start + arena.size;
|
||||
auto pos = arena.start;
|
||||
|
|
@ -289,38 +334,8 @@ std::pair<size_t, size_t> GC::freeUnmarked(Arena & arena)
|
|||
|
||||
while (pos < end) {
|
||||
auto obj = (Object *) pos;
|
||||
auto tag = obj->type;
|
||||
|
||||
size_t objSize;
|
||||
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();
|
||||
}
|
||||
}
|
||||
auto objSize = getObjectSize(obj);
|
||||
|
||||
// Merge current object into the previous free object.
|
||||
auto mergeFree = [&]() {
|
||||
|
|
@ -329,7 +344,7 @@ std::pair<size_t, size_t> GC::freeUnmarked(Arena & arena)
|
|||
curFree->setSize(curFree->words() + objSize);
|
||||
};
|
||||
|
||||
if (tag == tFree) {
|
||||
if (obj->type == tFree) {
|
||||
//debug("KEEP FREE %x %d", obj, obj->getMisc());
|
||||
if (curFree) {
|
||||
// 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);
|
||||
linkCurFree();
|
||||
obj->unmark();
|
||||
objectsKept += 1;
|
||||
wordsKept += objSize;
|
||||
} else {
|
||||
//debug("FREE OBJECT %x %d %d", obj, obj->type, objSize);
|
||||
#if GC_DEBUG
|
||||
|
|
@ -371,7 +388,7 @@ std::pair<size_t, size_t> GC::freeUnmarked(Arena & arena)
|
|||
|
||||
assert(pos == end);
|
||||
|
||||
return {objectsFreed, wordsFreed};
|
||||
return {objectsFreed, wordsFreed, objectsKept, wordsKept};
|
||||
}
|
||||
|
||||
bool GC::isObject(void * p)
|
||||
|
|
@ -382,4 +399,139 @@ bool GC::isObject(void * p)
|
|||
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
|
||||
}
|
||||
|
||||
/* 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:
|
||||
|
||||
void addArena(size_t arenaSize);
|
||||
|
||||
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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue