Implement HeapIterator that skips over unreachable objects.

I'm using it when creating heap snapshots. I decided that it will
be more convenient to have it as a separate piece of code, instead
of embedding into the snapshot generator.

Review URL: http://codereview.chromium.org/6014004

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@6091 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 8df57d74
......@@ -367,7 +367,6 @@ HeapSnapshot* HeapProfiler::TakeSnapshot(String* name,
HeapSnapshot* HeapProfiler::TakeSnapshotImpl(const char* name,
int type,
v8::ActivityControl* control) {
Heap::CollectAllGarbage(true);
HeapSnapshot::Type s_type = static_cast<HeapSnapshot::Type>(type);
HeapSnapshot* result =
snapshots_->NewSnapshot(s_type, name, next_snapshot_uid_++);
......@@ -379,6 +378,7 @@ HeapSnapshot* HeapProfiler::TakeSnapshotImpl(const char* name,
break;
}
case HeapSnapshot::kAggregated: {
Heap::CollectAllGarbage(true);
AggregatedHeapSnapshot agg_snapshot;
AggregatedHeapSnapshotGenerator generator(&agg_snapshot);
generator.GenerateSnapshot();
......@@ -808,7 +808,7 @@ void AggregatedHeapSnapshotGenerator::CollectStats(HeapObject* obj) {
void AggregatedHeapSnapshotGenerator::GenerateSnapshot() {
HeapIterator iterator(HeapIterator::kPreciseFiltering);
HeapIterator iterator(HeapIterator::kFilterFreeListNodes);
for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
CollectStats(obj);
agg_snapshot_->js_cons_profile()->CollectStats(obj);
......
......@@ -4483,7 +4483,7 @@ void Heap::RecordStats(HeapStats* stats, bool take_snapshot) {
MemoryAllocator::Size() + MemoryAllocator::Available();
*stats->os_error = OS::GetLastError();
if (take_snapshot) {
HeapIterator iterator(HeapIterator::kPreciseFiltering);
HeapIterator iterator(HeapIterator::kFilterFreeListNodes);
for (HeapObject* obj = iterator.next();
obj != NULL;
obj = iterator.next()) {
......@@ -4917,13 +4917,20 @@ ObjectIterator* SpaceIterator::CreateIterator() {
}
class FreeListNodesFilter {
class HeapObjectsFilter {
public:
virtual ~HeapObjectsFilter() {}
virtual bool SkipObject(HeapObject* object) = 0;
};
class FreeListNodesFilter : public HeapObjectsFilter {
public:
FreeListNodesFilter() {
MarkFreeListNodes();
}
inline bool IsFreeListNode(HeapObject* object) {
bool SkipObject(HeapObject* object) {
if (object->IsMarked()) {
object->ClearMark();
return true;
......@@ -4955,6 +4962,65 @@ class FreeListNodesFilter {
};
class UnreachableObjectsFilter : public HeapObjectsFilter {
public:
UnreachableObjectsFilter() {
MarkUnreachableObjects();
}
bool SkipObject(HeapObject* object) {
if (object->IsMarked()) {
object->ClearMark();
return true;
} else {
return false;
}
}
private:
class UnmarkingVisitor : public ObjectVisitor {
public:
UnmarkingVisitor() : list_(10) {}
void VisitPointers(Object** start, Object** end) {
for (Object** p = start; p < end; p++) {
if (!(*p)->IsHeapObject()) continue;
HeapObject* obj = HeapObject::cast(*p);
if (obj->IsMarked()) {
obj->ClearMark();
list_.Add(obj);
}
}
}
bool can_process() { return !list_.is_empty(); }
void ProcessNext() {
HeapObject* obj = list_.RemoveLast();
obj->Iterate(this);
}
private:
List<HeapObject*> list_;
};
void MarkUnreachableObjects() {
HeapIterator iterator;
for (HeapObject* obj = iterator.next();
obj != NULL;
obj = iterator.next()) {
obj->SetMark();
}
UnmarkingVisitor visitor;
Heap::IterateRoots(&visitor, VISIT_ONLY_STRONG);
while (visitor.can_process())
visitor.ProcessNext();
}
AssertNoAllocation no_alloc;
};
HeapIterator::HeapIterator()
: filtering_(HeapIterator::kNoFiltering),
filter_(NULL) {
......@@ -4962,7 +5028,7 @@ HeapIterator::HeapIterator()
}
HeapIterator::HeapIterator(HeapIterator::FreeListNodesFiltering filtering)
HeapIterator::HeapIterator(HeapIterator::HeapObjectsFiltering filtering)
: filtering_(filtering),
filter_(NULL) {
Init();
......@@ -4976,12 +5042,17 @@ HeapIterator::~HeapIterator() {
void HeapIterator::Init() {
// Start the iteration.
if (filtering_ == kPreciseFiltering) {
filter_ = new FreeListNodesFilter;
space_iterator_ =
new SpaceIterator(MarkCompactCollector::SizeOfMarkedObject);
} else {
space_iterator_ = new SpaceIterator;
space_iterator_ = filtering_ == kNoFiltering ? new SpaceIterator :
new SpaceIterator(MarkCompactCollector::SizeOfMarkedObject);
switch (filtering_) {
case kFilterFreeListNodes:
filter_ = new FreeListNodesFilter;
break;
case kFilterUnreachable:
filter_ = new UnreachableObjectsFilter;
break;
default:
break;
}
object_iterator_ = space_iterator_->next();
}
......@@ -4989,9 +5060,9 @@ void HeapIterator::Init() {
void HeapIterator::Shutdown() {
#ifdef DEBUG
// Assert that in precise mode we have iterated through all
// Assert that in filtering mode we have iterated through all
// objects. Otherwise, heap will be left in an inconsistent state.
if (filtering_ == kPreciseFiltering) {
if (filtering_ != kNoFiltering) {
ASSERT(object_iterator_ == NULL);
}
#endif
......@@ -5008,7 +5079,7 @@ HeapObject* HeapIterator::next() {
if (filter_ == NULL) return NextObject();
HeapObject* obj = NextObject();
while (obj != NULL && filter_->IsFreeListNode(obj)) obj = NextObject();
while (obj != NULL && filter_->SkipObject(obj)) obj = NextObject();
return obj;
}
......
......@@ -1585,17 +1585,18 @@ class SpaceIterator : public Malloced {
// nodes filtering uses GC marks, it can't be used during MS/MC GC
// phases. Also, it is forbidden to interrupt iteration in this mode,
// as this will leave heap objects marked (and thus, unusable).
class FreeListNodesFilter;
class HeapObjectsFilter;
class HeapIterator BASE_EMBEDDED {
public:
enum FreeListNodesFiltering {
enum HeapObjectsFiltering {
kNoFiltering,
kPreciseFiltering
kFilterFreeListNodes,
kFilterUnreachable
};
HeapIterator();
explicit HeapIterator(FreeListNodesFiltering filtering);
explicit HeapIterator(HeapObjectsFiltering filtering);
~HeapIterator();
HeapObject* next();
......@@ -1608,8 +1609,8 @@ class HeapIterator BASE_EMBEDDED {
void Shutdown();
HeapObject* NextObject();
FreeListNodesFiltering filtering_;
FreeListNodesFilter* filter_;
HeapObjectsFiltering filtering_;
HeapObjectsFilter* filter_;
// Space iterator for iterating all the spaces.
SpaceIterator* space_iterator_;
// Object iterator for the space currently being iterated.
......
......@@ -2218,7 +2218,7 @@ void HeapSnapshotGenerator::SetGcRootsReference(Object* child_obj) {
void HeapSnapshotGenerator::SetProgressTotal(int iterations_count) {
if (control_ == NULL) return;
HeapIterator iterator(HeapIterator::kPreciseFiltering);
HeapIterator iterator(HeapIterator::kFilterUnreachable);
int objects_count = 0;
for (HeapObject* obj = iterator.next();
obj != NULL;
......@@ -2342,8 +2342,6 @@ bool HeapSnapshotGenerator::SetEntriesDominators() {
ASSERT(dominators[i] != NULL);
ordered_entries[i]->set_dominator(dominators[i]);
}
// For nodes unreachable from root, set dominator to itself.
snapshot_->SetDominatorsToSelf();
return true;
}
......@@ -2373,9 +2371,9 @@ bool HeapSnapshotGenerator::ApproximateRetainedSizes() {
bool HeapSnapshotGenerator::IterateAndExtractReferences() {
HeapIterator iterator(HeapIterator::kPreciseFiltering);
HeapIterator iterator(HeapIterator::kFilterUnreachable);
bool interrupted = false;
// Heap iteration with precise filtering must be finished in any case.
// Heap iteration with filtering must be finished in any case.
for (HeapObject* obj = iterator.next();
obj != NULL;
obj = iterator.next(), IncProgressCounter()) {
......
......@@ -1216,7 +1216,7 @@ TEST(TestInternalWeakListsTraverseWithGC) {
TEST(TestSizeOfObjectsVsHeapIteratorPrecision) {
InitializeVM();
intptr_t size_of_objects_1 = Heap::SizeOfObjects();
HeapIterator iterator(HeapIterator::kPreciseFiltering);
HeapIterator iterator(HeapIterator::kFilterFreeListNodes);
intptr_t size_of_objects_2 = 0;
for (HeapObject* obj = iterator.next();
obj != NULL;
......@@ -1240,3 +1240,65 @@ TEST(TestSizeOfObjectsVsHeapIteratorPrecision) {
CHECK_GT(size_of_objects_2 / 100, delta);
}
}
class HeapIteratorTestHelper {
public:
HeapIteratorTestHelper(Object* a, Object* b)
: a_(a), b_(b), a_found_(false), b_found_(false) {}
bool a_found() { return a_found_; }
bool b_found() { return b_found_; }
void IterateHeap(HeapIterator::HeapObjectsFiltering mode) {
HeapIterator iterator(mode);
for (HeapObject* obj = iterator.next();
obj != NULL;
obj = iterator.next()) {
if (obj == a_)
a_found_ = true;
else if (obj == b_)
b_found_ = true;
}
}
private:
Object* a_;
Object* b_;
bool a_found_;
bool b_found_;
};
TEST(HeapIteratorFilterUnreachable) {
InitializeVM();
v8::HandleScope scope;
CompileRun("a = {}; b = {};");
v8::Handle<Object> a(Top::context()->global()->GetProperty(
*Factory::LookupAsciiSymbol("a"))->ToObjectChecked());
v8::Handle<Object> b(Top::context()->global()->GetProperty(
*Factory::LookupAsciiSymbol("b"))->ToObjectChecked());
CHECK_NE(*a, *b);
{
HeapIteratorTestHelper helper(*a, *b);
helper.IterateHeap(HeapIterator::kFilterUnreachable);
CHECK(helper.a_found());
CHECK(helper.b_found());
}
CHECK(Top::context()->global()->DeleteProperty(
*Factory::LookupAsciiSymbol("a"), JSObject::FORCE_DELETION));
// We ensure that GC will not happen, so our raw pointer stays valid.
AssertNoAllocation no_alloc;
Object* a_saved = *a;
a.Clear();
// Verify that "a" object still resides in the heap...
{
HeapIteratorTestHelper helper(a_saved, *b);
helper.IterateHeap(HeapIterator::kNoFiltering);
CHECK(helper.a_found());
CHECK(helper.b_found());
}
// ...but is now unreachable.
{
HeapIteratorTestHelper helper(a_saved, *b);
helper.IterateHeap(HeapIterator::kFilterUnreachable);
CHECK(!helper.a_found());
CHECK(helper.b_found());
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment