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, ...@@ -367,7 +367,6 @@ HeapSnapshot* HeapProfiler::TakeSnapshot(String* name,
HeapSnapshot* HeapProfiler::TakeSnapshotImpl(const char* name, HeapSnapshot* HeapProfiler::TakeSnapshotImpl(const char* name,
int type, int type,
v8::ActivityControl* control) { v8::ActivityControl* control) {
Heap::CollectAllGarbage(true);
HeapSnapshot::Type s_type = static_cast<HeapSnapshot::Type>(type); HeapSnapshot::Type s_type = static_cast<HeapSnapshot::Type>(type);
HeapSnapshot* result = HeapSnapshot* result =
snapshots_->NewSnapshot(s_type, name, next_snapshot_uid_++); snapshots_->NewSnapshot(s_type, name, next_snapshot_uid_++);
...@@ -379,6 +378,7 @@ HeapSnapshot* HeapProfiler::TakeSnapshotImpl(const char* name, ...@@ -379,6 +378,7 @@ HeapSnapshot* HeapProfiler::TakeSnapshotImpl(const char* name,
break; break;
} }
case HeapSnapshot::kAggregated: { case HeapSnapshot::kAggregated: {
Heap::CollectAllGarbage(true);
AggregatedHeapSnapshot agg_snapshot; AggregatedHeapSnapshot agg_snapshot;
AggregatedHeapSnapshotGenerator generator(&agg_snapshot); AggregatedHeapSnapshotGenerator generator(&agg_snapshot);
generator.GenerateSnapshot(); generator.GenerateSnapshot();
...@@ -808,7 +808,7 @@ void AggregatedHeapSnapshotGenerator::CollectStats(HeapObject* obj) { ...@@ -808,7 +808,7 @@ void AggregatedHeapSnapshotGenerator::CollectStats(HeapObject* obj) {
void AggregatedHeapSnapshotGenerator::GenerateSnapshot() { void AggregatedHeapSnapshotGenerator::GenerateSnapshot() {
HeapIterator iterator(HeapIterator::kPreciseFiltering); HeapIterator iterator(HeapIterator::kFilterFreeListNodes);
for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) { for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
CollectStats(obj); CollectStats(obj);
agg_snapshot_->js_cons_profile()->CollectStats(obj); agg_snapshot_->js_cons_profile()->CollectStats(obj);
......
...@@ -4483,7 +4483,7 @@ void Heap::RecordStats(HeapStats* stats, bool take_snapshot) { ...@@ -4483,7 +4483,7 @@ void Heap::RecordStats(HeapStats* stats, bool take_snapshot) {
MemoryAllocator::Size() + MemoryAllocator::Available(); MemoryAllocator::Size() + MemoryAllocator::Available();
*stats->os_error = OS::GetLastError(); *stats->os_error = OS::GetLastError();
if (take_snapshot) { if (take_snapshot) {
HeapIterator iterator(HeapIterator::kPreciseFiltering); HeapIterator iterator(HeapIterator::kFilterFreeListNodes);
for (HeapObject* obj = iterator.next(); for (HeapObject* obj = iterator.next();
obj != NULL; obj != NULL;
obj = iterator.next()) { obj = iterator.next()) {
...@@ -4917,13 +4917,20 @@ ObjectIterator* SpaceIterator::CreateIterator() { ...@@ -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: public:
FreeListNodesFilter() { FreeListNodesFilter() {
MarkFreeListNodes(); MarkFreeListNodes();
} }
inline bool IsFreeListNode(HeapObject* object) { bool SkipObject(HeapObject* object) {
if (object->IsMarked()) { if (object->IsMarked()) {
object->ClearMark(); object->ClearMark();
return true; return true;
...@@ -4955,6 +4962,65 @@ class FreeListNodesFilter { ...@@ -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() HeapIterator::HeapIterator()
: filtering_(HeapIterator::kNoFiltering), : filtering_(HeapIterator::kNoFiltering),
filter_(NULL) { filter_(NULL) {
...@@ -4962,7 +5028,7 @@ HeapIterator::HeapIterator() ...@@ -4962,7 +5028,7 @@ HeapIterator::HeapIterator()
} }
HeapIterator::HeapIterator(HeapIterator::FreeListNodesFiltering filtering) HeapIterator::HeapIterator(HeapIterator::HeapObjectsFiltering filtering)
: filtering_(filtering), : filtering_(filtering),
filter_(NULL) { filter_(NULL) {
Init(); Init();
...@@ -4976,12 +5042,17 @@ HeapIterator::~HeapIterator() { ...@@ -4976,12 +5042,17 @@ HeapIterator::~HeapIterator() {
void HeapIterator::Init() { void HeapIterator::Init() {
// Start the iteration. // Start the iteration.
if (filtering_ == kPreciseFiltering) { space_iterator_ = filtering_ == kNoFiltering ? new SpaceIterator :
filter_ = new FreeListNodesFilter; new SpaceIterator(MarkCompactCollector::SizeOfMarkedObject);
space_iterator_ = switch (filtering_) {
new SpaceIterator(MarkCompactCollector::SizeOfMarkedObject); case kFilterFreeListNodes:
} else { filter_ = new FreeListNodesFilter;
space_iterator_ = new SpaceIterator; break;
case kFilterUnreachable:
filter_ = new UnreachableObjectsFilter;
break;
default:
break;
} }
object_iterator_ = space_iterator_->next(); object_iterator_ = space_iterator_->next();
} }
...@@ -4989,9 +5060,9 @@ void HeapIterator::Init() { ...@@ -4989,9 +5060,9 @@ void HeapIterator::Init() {
void HeapIterator::Shutdown() { void HeapIterator::Shutdown() {
#ifdef DEBUG #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. // objects. Otherwise, heap will be left in an inconsistent state.
if (filtering_ == kPreciseFiltering) { if (filtering_ != kNoFiltering) {
ASSERT(object_iterator_ == NULL); ASSERT(object_iterator_ == NULL);
} }
#endif #endif
...@@ -5008,7 +5079,7 @@ HeapObject* HeapIterator::next() { ...@@ -5008,7 +5079,7 @@ HeapObject* HeapIterator::next() {
if (filter_ == NULL) return NextObject(); if (filter_ == NULL) return NextObject();
HeapObject* obj = NextObject(); HeapObject* obj = NextObject();
while (obj != NULL && filter_->IsFreeListNode(obj)) obj = NextObject(); while (obj != NULL && filter_->SkipObject(obj)) obj = NextObject();
return obj; return obj;
} }
......
...@@ -1585,17 +1585,18 @@ class SpaceIterator : public Malloced { ...@@ -1585,17 +1585,18 @@ class SpaceIterator : public Malloced {
// nodes filtering uses GC marks, it can't be used during MS/MC GC // 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, // phases. Also, it is forbidden to interrupt iteration in this mode,
// as this will leave heap objects marked (and thus, unusable). // as this will leave heap objects marked (and thus, unusable).
class FreeListNodesFilter; class HeapObjectsFilter;
class HeapIterator BASE_EMBEDDED { class HeapIterator BASE_EMBEDDED {
public: public:
enum FreeListNodesFiltering { enum HeapObjectsFiltering {
kNoFiltering, kNoFiltering,
kPreciseFiltering kFilterFreeListNodes,
kFilterUnreachable
}; };
HeapIterator(); HeapIterator();
explicit HeapIterator(FreeListNodesFiltering filtering); explicit HeapIterator(HeapObjectsFiltering filtering);
~HeapIterator(); ~HeapIterator();
HeapObject* next(); HeapObject* next();
...@@ -1608,8 +1609,8 @@ class HeapIterator BASE_EMBEDDED { ...@@ -1608,8 +1609,8 @@ class HeapIterator BASE_EMBEDDED {
void Shutdown(); void Shutdown();
HeapObject* NextObject(); HeapObject* NextObject();
FreeListNodesFiltering filtering_; HeapObjectsFiltering filtering_;
FreeListNodesFilter* filter_; HeapObjectsFilter* filter_;
// Space iterator for iterating all the spaces. // Space iterator for iterating all the spaces.
SpaceIterator* space_iterator_; SpaceIterator* space_iterator_;
// Object iterator for the space currently being iterated. // Object iterator for the space currently being iterated.
......
...@@ -2218,7 +2218,7 @@ void HeapSnapshotGenerator::SetGcRootsReference(Object* child_obj) { ...@@ -2218,7 +2218,7 @@ void HeapSnapshotGenerator::SetGcRootsReference(Object* child_obj) {
void HeapSnapshotGenerator::SetProgressTotal(int iterations_count) { void HeapSnapshotGenerator::SetProgressTotal(int iterations_count) {
if (control_ == NULL) return; if (control_ == NULL) return;
HeapIterator iterator(HeapIterator::kPreciseFiltering); HeapIterator iterator(HeapIterator::kFilterUnreachable);
int objects_count = 0; int objects_count = 0;
for (HeapObject* obj = iterator.next(); for (HeapObject* obj = iterator.next();
obj != NULL; obj != NULL;
...@@ -2342,8 +2342,6 @@ bool HeapSnapshotGenerator::SetEntriesDominators() { ...@@ -2342,8 +2342,6 @@ bool HeapSnapshotGenerator::SetEntriesDominators() {
ASSERT(dominators[i] != NULL); ASSERT(dominators[i] != NULL);
ordered_entries[i]->set_dominator(dominators[i]); ordered_entries[i]->set_dominator(dominators[i]);
} }
// For nodes unreachable from root, set dominator to itself.
snapshot_->SetDominatorsToSelf();
return true; return true;
} }
...@@ -2373,9 +2371,9 @@ bool HeapSnapshotGenerator::ApproximateRetainedSizes() { ...@@ -2373,9 +2371,9 @@ bool HeapSnapshotGenerator::ApproximateRetainedSizes() {
bool HeapSnapshotGenerator::IterateAndExtractReferences() { bool HeapSnapshotGenerator::IterateAndExtractReferences() {
HeapIterator iterator(HeapIterator::kPreciseFiltering); HeapIterator iterator(HeapIterator::kFilterUnreachable);
bool interrupted = false; 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(); for (HeapObject* obj = iterator.next();
obj != NULL; obj != NULL;
obj = iterator.next(), IncProgressCounter()) { obj = iterator.next(), IncProgressCounter()) {
......
...@@ -1216,7 +1216,7 @@ TEST(TestInternalWeakListsTraverseWithGC) { ...@@ -1216,7 +1216,7 @@ TEST(TestInternalWeakListsTraverseWithGC) {
TEST(TestSizeOfObjectsVsHeapIteratorPrecision) { TEST(TestSizeOfObjectsVsHeapIteratorPrecision) {
InitializeVM(); InitializeVM();
intptr_t size_of_objects_1 = Heap::SizeOfObjects(); intptr_t size_of_objects_1 = Heap::SizeOfObjects();
HeapIterator iterator(HeapIterator::kPreciseFiltering); HeapIterator iterator(HeapIterator::kFilterFreeListNodes);
intptr_t size_of_objects_2 = 0; intptr_t size_of_objects_2 = 0;
for (HeapObject* obj = iterator.next(); for (HeapObject* obj = iterator.next();
obj != NULL; obj != NULL;
...@@ -1240,3 +1240,65 @@ TEST(TestSizeOfObjectsVsHeapIteratorPrecision) { ...@@ -1240,3 +1240,65 @@ TEST(TestSizeOfObjectsVsHeapIteratorPrecision) {
CHECK_GT(size_of_objects_2 / 100, delta); 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