diff --git a/include/v8-profiler.h b/include/v8-profiler.h index f67646f54e..27b3c6def3 100644 --- a/include/v8-profiler.h +++ b/include/v8-profiler.h @@ -219,8 +219,9 @@ class V8EXPORT HeapGraphEdge { // (e.g. parts of a ConsString). kHidden = 4, // A link that is needed for proper sizes // calculation, but may be hidden from user. - kShortcut = 5 // A link that must not be followed during + kShortcut = 5, // A link that must not be followed during // sizes calculation. + kWeak = 6 // A weak reference (ignored by the GC). }; /** Returns edge type (see HeapGraphEdge::Type). */ diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index 29c16ee936..6d388a5680 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -299,7 +299,7 @@ class Genesis BASE_EMBEDDED { void Bootstrapper::Iterate(ObjectVisitor* v) { extensions_cache_.Iterate(v); - v->Synchronize("Extensions"); + v->Synchronize(VisitorSynchronization::kExtensions); } diff --git a/src/heap.cc b/src/heap.cc index 4feb70eb41..6172d22a33 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -5151,29 +5151,29 @@ void Heap::IterateRoots(ObjectVisitor* v, VisitMode mode) { void Heap::IterateWeakRoots(ObjectVisitor* v, VisitMode mode) { v->VisitPointer(reinterpret_cast(&roots_[kSymbolTableRootIndex])); - v->Synchronize("symbol_table"); + v->Synchronize(VisitorSynchronization::kSymbolTable); if (mode != VISIT_ALL_IN_SCAVENGE && mode != VISIT_ALL_IN_SWEEP_NEWSPACE) { // Scavenge collections have special processing for this. external_string_table_.Iterate(v); } - v->Synchronize("external_string_table"); + v->Synchronize(VisitorSynchronization::kExternalStringsTable); } void Heap::IterateStrongRoots(ObjectVisitor* v, VisitMode mode) { v->VisitPointers(&roots_[0], &roots_[kStrongRootListLength]); - v->Synchronize("strong_root_list"); + v->Synchronize(VisitorSynchronization::kStrongRootList); v->VisitPointer(BitCast(&hidden_symbol_)); - v->Synchronize("symbol"); + v->Synchronize(VisitorSynchronization::kSymbol); isolate_->bootstrapper()->Iterate(v); - v->Synchronize("bootstrapper"); + v->Synchronize(VisitorSynchronization::kBootstrapper); isolate_->Iterate(v); - v->Synchronize("top"); + v->Synchronize(VisitorSynchronization::kTop); Relocatable::Iterate(v); - v->Synchronize("relocatable"); + v->Synchronize(VisitorSynchronization::kRelocatable); #ifdef ENABLE_DEBUGGER_SUPPORT isolate_->debug()->Iterate(v); @@ -5181,13 +5181,13 @@ void Heap::IterateStrongRoots(ObjectVisitor* v, VisitMode mode) { isolate_->deoptimizer_data()->Iterate(v); } #endif - v->Synchronize("debug"); + v->Synchronize(VisitorSynchronization::kDebug); isolate_->compilation_cache()->Iterate(v); - v->Synchronize("compilationcache"); + v->Synchronize(VisitorSynchronization::kCompilationCache); // Iterate over local handles in handle scopes. isolate_->handle_scope_implementer()->Iterate(v); - v->Synchronize("handlescope"); + v->Synchronize(VisitorSynchronization::kHandleScope); // Iterate over the builtin code objects and code stubs in the // heap. Note that it is not necessary to iterate over code objects @@ -5195,7 +5195,7 @@ void Heap::IterateStrongRoots(ObjectVisitor* v, VisitMode mode) { if (mode != VISIT_ALL_IN_SCAVENGE) { isolate_->builtins()->IterateBuiltins(v); } - v->Synchronize("builtins"); + v->Synchronize(VisitorSynchronization::kBuiltins); // Iterate over global handles. switch (mode) { @@ -5210,11 +5210,11 @@ void Heap::IterateStrongRoots(ObjectVisitor* v, VisitMode mode) { isolate_->global_handles()->IterateAllRoots(v); break; } - v->Synchronize("globalhandles"); + v->Synchronize(VisitorSynchronization::kGlobalHandles); // Iterate over pointers being held by inactive threads. isolate_->thread_manager()->Iterate(v); - v->Synchronize("threadmanager"); + v->Synchronize(VisitorSynchronization::kThreadManager); // Iterate over the pointers the Serialization/Deserialization code is // holding. diff --git a/src/objects.cc b/src/objects.cc index 1565504c28..474a9ade66 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -7639,6 +7639,22 @@ void SharedFunctionInfo::CompleteInobjectSlackTracking() { } +#define DECLARE_TAG(ignore1, name, ignore2) name, +const char* const VisitorSynchronization::kTags[ + VisitorSynchronization::kNumberOfSyncTags] = { + VISITOR_SYNCHRONIZATION_TAGS_LIST(DECLARE_TAG) +}; +#undef DECLARE_TAG + + +#define DECLARE_TAG(ignore1, ignore2, name) name, +const char* const VisitorSynchronization::kTagNames[ + VisitorSynchronization::kNumberOfSyncTags] = { + VISITOR_SYNCHRONIZATION_TAGS_LIST(DECLARE_TAG) +}; +#undef DECLARE_TAG + + void ObjectVisitor::VisitCodeTarget(RelocInfo* rinfo) { ASSERT(RelocInfo::IsCodeTarget(rinfo->rmode())); Object* target = Code::GetCodeFromTargetAddress(rinfo->target_address()); diff --git a/src/objects.h b/src/objects.h index 6c88cc01ae..d651bf9849 100644 --- a/src/objects.h +++ b/src/objects.h @@ -7862,6 +7862,34 @@ class BreakPointInfo: public Struct { #undef DECL_BOOLEAN_ACCESSORS #undef DECL_ACCESSORS +#define VISITOR_SYNCHRONIZATION_TAGS_LIST(V) \ + V(kSymbolTable, "symbol_table", "(Symbols)") \ + V(kExternalStringsTable, "external_strings_table", "(External strings)") \ + V(kStrongRootList, "strong_root_list", "(Strong roots)") \ + V(kSymbol, "symbol", "(Symbol)") \ + V(kBootstrapper, "bootstrapper", "(Bootstrapper)") \ + V(kTop, "top", "(Isolate)") \ + V(kRelocatable, "relocatable", "(Relocatable)") \ + V(kDebug, "debug", "(Debugger)") \ + V(kCompilationCache, "compilationcache", "(Compilation cache)") \ + V(kHandleScope, "handlescope", "(Handle scope)") \ + V(kBuiltins, "builtins", "(Builtins)") \ + V(kGlobalHandles, "globalhandles", "(Global handles)") \ + V(kThreadManager, "threadmanager", "(Thread manager)") \ + V(kExtensions, "Extensions", "(Extensions)") + +class VisitorSynchronization : public AllStatic { + public: +#define DECLARE_ENUM(enum_item, ignore1, ignore2) enum_item, + enum SyncTag { + VISITOR_SYNCHRONIZATION_TAGS_LIST(DECLARE_ENUM) + kNumberOfSyncTags + }; +#undef DECLARE_ENUM + + static const char* const kTags[kNumberOfSyncTags]; + static const char* const kTagNames[kNumberOfSyncTags]; +}; // Abstract base class for visiting, and optionally modifying, the // pointers contained in Objects. Used in GC and serialization/deserialization. @@ -7917,13 +7945,10 @@ class ObjectVisitor BASE_EMBEDDED { // Visits a handle that has an embedder-assigned class ID. virtual void VisitEmbedderReference(Object** p, uint16_t class_id) {} -#ifdef DEBUG // Intended for serialization/deserialization checking: insert, or // check for the presence of, a tag at this position in the stream. - virtual void Synchronize(const char* tag) {} -#else - inline void Synchronize(const char* tag) {} -#endif + // Also used for marking up GC roots in heap snapshots. + virtual void Synchronize(VisitorSynchronization::SyncTag tag) {} }; diff --git a/src/profile-generator-inl.h b/src/profile-generator-inl.h index 88d6e87941..a8c6c44096 100644 --- a/src/profile-generator-inl.h +++ b/src/profile-generator-inl.h @@ -95,6 +95,25 @@ CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) { } +uint64_t HeapObjectsMap::GetNthGcSubrootId(int delta) { + return kGcRootsObjectId + delta * kObjectIdStep; +} + + +HeapObject* V8HeapExplorer::GetNthGcSubrootObject(int delta) { + return reinterpret_cast( + reinterpret_cast(kFirstGcSubrootObject) + + delta * HeapObjectsMap::kObjectIdStep); +} + + +int V8HeapExplorer::GetGcSubrootOrder(HeapObject* subroot) { + return (reinterpret_cast(subroot) - + reinterpret_cast(kFirstGcSubrootObject)) / + HeapObjectsMap::kObjectIdStep; +} + + uint64_t HeapEntry::id() { union { Id stored_id; diff --git a/src/profile-generator.cc b/src/profile-generator.cc index 5626acaba4..a46122be67 100644 --- a/src/profile-generator.cc +++ b/src/profile-generator.cc @@ -938,7 +938,7 @@ void HeapGraphEdge::Init( void HeapGraphEdge::Init(int child_index, Type type, int index, HeapEntry* to) { - ASSERT(type == kElement || type == kHidden); + ASSERT(type == kElement || type == kHidden || type == kWeak); child_index_ = child_index; type_ = type; index_ = index; @@ -1053,8 +1053,11 @@ void HeapEntry::PaintAllReachable() { } -void HeapEntry::Print(int max_depth, int indent) { - OS::Print("%6d %6d [%llu] ", self_size(), RetainedSize(false), id()); +void HeapEntry::Print( + const char* prefix, const char* edge_name, int max_depth, int indent) { + OS::Print("%6d %7d @%6llu %*c %s%s: ", + self_size(), RetainedSize(false), id(), + indent, ' ', prefix, edge_name); if (type() != kString) { OS::Print("%s %.40s\n", TypeAsString(), name_); } else { @@ -1073,29 +1076,40 @@ void HeapEntry::Print(int max_depth, int indent) { Vector ch = children(); for (int i = 0; i < ch.length(); ++i) { HeapGraphEdge& edge = ch[i]; + const char* edge_prefix = ""; + ScopedVector index(64); + const char* edge_name = index.start(); switch (edge.type()) { case HeapGraphEdge::kContextVariable: - OS::Print(" %*c #%s: ", indent, ' ', edge.name()); + edge_prefix = "#"; + edge_name = edge.name(); break; case HeapGraphEdge::kElement: - OS::Print(" %*c %d: ", indent, ' ', edge.index()); + OS::SNPrintF(index, "%d", edge.index()); break; case HeapGraphEdge::kInternal: - OS::Print(" %*c $%s: ", indent, ' ', edge.name()); + edge_prefix = "$"; + edge_name = edge.name(); break; case HeapGraphEdge::kProperty: - OS::Print(" %*c %s: ", indent, ' ', edge.name()); + edge_name = edge.name(); break; case HeapGraphEdge::kHidden: - OS::Print(" %*c $%d: ", indent, ' ', edge.index()); + edge_prefix = "$"; + OS::SNPrintF(index, "%d", edge.index()); break; case HeapGraphEdge::kShortcut: - OS::Print(" %*c ^%s: ", indent, ' ', edge.name()); + edge_prefix = "^"; + edge_name = edge.name(); + break; + case HeapGraphEdge::kWeak: + edge_prefix = "w"; + OS::SNPrintF(index, "%d", edge.index()); break; default: - OS::Print("!!! unknown edge type: %d ", edge.type()); + OS::SNPrintF(index, "!!! unknown edge type: %d ", edge.type()); } - edge.to()->Print(max_depth, indent + 2); + edge.to()->Print(edge_prefix, edge_name, max_depth, indent + 2); } } @@ -1215,6 +1229,9 @@ HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection, STATIC_ASSERT( sizeof(HeapEntry) == SnapshotSizeConstants::kExpectedHeapEntrySize); // NOLINT + for (int i = 0; i < VisitorSynchronization::kNumberOfSyncTags; ++i) { + gc_subroot_entries_[i] = NULL; + } } HeapSnapshot::~HeapSnapshot() { @@ -1270,6 +1287,21 @@ HeapEntry* HeapSnapshot::AddGcRootsEntry(int children_count, } +HeapEntry* HeapSnapshot::AddGcSubrootEntry(int tag, + int children_count, + int retainers_count) { + ASSERT(gc_subroot_entries_[tag] == NULL); + ASSERT(0 <= tag && tag < VisitorSynchronization::kNumberOfSyncTags); + return (gc_subroot_entries_[tag] = AddEntry( + HeapEntry::kObject, + VisitorSynchronization::kTagNames[tag], + HeapObjectsMap::GetNthGcSubrootId(tag), + 0, + children_count, + retainers_count)); +} + + HeapEntry* HeapSnapshot::AddNativesRootEntry(int children_count, int retainers_count) { ASSERT(natives_root_entry_ == NULL); @@ -1355,17 +1387,22 @@ List* HeapSnapshot::GetSortedEntriesList() { void HeapSnapshot::Print(int max_depth) { - root()->Print(max_depth, 0); + root()->Print("", "", max_depth, 0); } // We split IDs on evens for embedder objects (see // HeapObjectsMap::GenerateId) and odds for native objects. const uint64_t HeapObjectsMap::kInternalRootObjectId = 1; -const uint64_t HeapObjectsMap::kGcRootsObjectId = 3; -const uint64_t HeapObjectsMap::kNativesRootObjectId = 5; -// Increase kFirstAvailableObjectId if new 'special' objects appear. -const uint64_t HeapObjectsMap::kFirstAvailableObjectId = 7; +const uint64_t HeapObjectsMap::kGcRootsObjectId = + HeapObjectsMap::kInternalRootObjectId + HeapObjectsMap::kObjectIdStep; +const uint64_t HeapObjectsMap::kNativesRootObjectId = + HeapObjectsMap::kGcRootsObjectId + HeapObjectsMap::kObjectIdStep; +const uint64_t HeapObjectsMap::kGcRootsFirstSubrootId = + HeapObjectsMap::kNativesRootObjectId + HeapObjectsMap::kObjectIdStep; +const uint64_t HeapObjectsMap::kFirstAvailableObjectId = + HeapObjectsMap::kGcRootsFirstSubrootId + + VisitorSynchronization::kNumberOfSyncTags * HeapObjectsMap::kObjectIdStep; HeapObjectsMap::HeapObjectsMap() : initial_fill_mode_(true), @@ -1391,7 +1428,7 @@ uint64_t HeapObjectsMap::FindObject(Address addr) { if (existing != 0) return existing; } uint64_t id = next_id_; - next_id_ += 2; + next_id_ += kObjectIdStep; AddEntry(addr, id); return id; } @@ -1684,6 +1721,12 @@ HeapObject *const V8HeapExplorer::kInternalRootObject = HeapObject *const V8HeapExplorer::kGcRootsObject = reinterpret_cast( static_cast(HeapObjectsMap::kGcRootsObjectId)); +HeapObject *const V8HeapExplorer::kFirstGcSubrootObject = + reinterpret_cast( + static_cast(HeapObjectsMap::kGcRootsFirstSubrootId)); +HeapObject *const V8HeapExplorer::kLastGcSubrootObject = + reinterpret_cast( + static_cast(HeapObjectsMap::kFirstAvailableObjectId)); V8HeapExplorer::V8HeapExplorer( @@ -1716,6 +1759,11 @@ HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object, return snapshot_->AddRootEntry(children_count); } else if (object == kGcRootsObject) { return snapshot_->AddGcRootsEntry(children_count, retainers_count); + } else if (object >= kFirstGcSubrootObject && object < kLastGcSubrootObject) { + return snapshot_->AddGcSubrootEntry( + GetGcSubrootOrder(object), + children_count, + retainers_count); } else if (object->IsJSGlobalObject()) { const char* tag = objects_tags_.GetTag(object); const char* name = collection_->names()->GetName( @@ -1779,6 +1827,18 @@ HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object, : "", children_count, retainers_count); + } else if (object->IsGlobalContext()) { + return AddEntry(object, + HeapEntry::kHidden, + "system / GlobalContext", + children_count, + retainers_count); + } else if (object->IsContext()) { + return AddEntry(object, + HeapEntry::kHidden, + "system / Context", + children_count, + retainers_count); } else if (object->IsFixedArray() || object->IsFixedDoubleArray() || object->IsByteArray() || @@ -1818,9 +1878,38 @@ HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object, } +class GcSubrootsEnumerator : public ObjectVisitor { + public: + GcSubrootsEnumerator( + SnapshotFillerInterface* filler, V8HeapExplorer* explorer) + : filler_(filler), + explorer_(explorer), + previous_object_count_(0), + object_count_(0) { + } + void VisitPointers(Object** start, Object** end) { + object_count_ += end - start; + } + void Synchronize(VisitorSynchronization::SyncTag tag) { + // Skip empty subroots. + if (previous_object_count_ != object_count_) { + previous_object_count_ = object_count_; + filler_->AddEntry(V8HeapExplorer::GetNthGcSubrootObject(tag), explorer_); + } + } + private: + SnapshotFillerInterface* filler_; + V8HeapExplorer* explorer_; + intptr_t previous_object_count_; + intptr_t object_count_; +}; + + void V8HeapExplorer::AddRootEntries(SnapshotFillerInterface* filler) { filler->AddEntry(kInternalRootObject, this); filler->AddEntry(kGcRootsObject, this); + GcSubrootsEnumerator enumerator(filler, this); + heap_->IterateRoots(&enumerator, VISIT_ALL); } @@ -1939,6 +2028,11 @@ void V8HeapExplorer::ExtractReferences(HeapObject* obj) { "literals_or_bindings", js_fun->literals_or_bindings(), JSFunction::kLiteralsOffset); + for (int i = JSFunction::kNonWeakFieldsEndOffset; + i < JSFunction::kSize; + i += kPointerSize) { + SetWeakReference(js_fun, entry, i, *HeapObject::RawField(js_fun, i), i); + } } TagObject(js_obj->properties(), "(object properties)"); SetInternalReference(obj, entry, @@ -1965,8 +2059,14 @@ void V8HeapExplorer::ExtractReferences(HeapObject* obj) { "(context func. result caches)"); TagObject(context->normalized_map_cache(), "(context norm. map cache)"); TagObject(context->runtime_context(), "(runtime context)"); - TagObject(context->map_cache(), "(context map cache)"); TagObject(context->data(), "(context data)"); + for (int i = Context::FIRST_WEAK_SLOT; + i < Context::GLOBAL_CONTEXT_SLOTS; + ++i) { + SetWeakReference(obj, entry, + i, context->get(i), + FixedArray::OffsetOfElementAt(i)); + } } else if (obj->IsMap()) { Map* map = Map::cast(obj); SetInternalReference(obj, entry, @@ -2009,6 +2109,9 @@ void V8HeapExplorer::ExtractReferences(HeapObject* obj) { SetInternalReference(obj, entry, "script", shared->script(), SharedFunctionInfo::kScriptOffset); + SetWeakReference(obj, entry, + 1, shared->initial_map(), + SharedFunctionInfo::kInitialMapOffset); } else if (obj->IsScript()) { Script* script = Script::cast(obj); SetInternalReference(obj, entry, @@ -2235,15 +2338,66 @@ HeapEntry* V8HeapExplorer::GetEntry(Object* obj) { class RootsReferencesExtractor : public ObjectVisitor { - public: - explicit RootsReferencesExtractor(V8HeapExplorer* explorer) - : explorer_(explorer) { - } - void VisitPointers(Object** start, Object** end) { - for (Object** p = start; p < end; p++) explorer_->SetGcRootsReference(*p); - } private: - V8HeapExplorer* explorer_; + struct IndexTag { + IndexTag(int index, VisitorSynchronization::SyncTag tag) + : index(index), tag(tag) { } + int index; + VisitorSynchronization::SyncTag tag; + }; + + public: + RootsReferencesExtractor() + : collecting_all_references_(false), + previous_reference_count_(0) { + } + + void VisitPointers(Object** start, Object** end) { + if (collecting_all_references_) { + for (Object** p = start; p < end; p++) all_references_.Add(*p); + } else { + for (Object** p = start; p < end; p++) strong_references_.Add(*p); + } + } + + void SetCollectingAllReferences() { collecting_all_references_ = true; } + + void FillReferences(V8HeapExplorer* explorer) { + ASSERT(strong_references_.length() <= all_references_.length()); + for (int i = 0; i < reference_tags_.length(); ++i) { + explorer->SetGcRootsReference(reference_tags_[i].tag); + } + int strong_index = 0, all_index = 0, tags_index = 0; + while (all_index < all_references_.length()) { + if (strong_index < strong_references_.length() && + strong_references_[strong_index] == all_references_[all_index]) { + explorer->SetGcSubrootReference(reference_tags_[tags_index].tag, + false, + all_references_[all_index++]); + ++strong_index; + } else { + explorer->SetGcSubrootReference(reference_tags_[tags_index].tag, + true, + all_references_[all_index++]); + } + if (reference_tags_[tags_index].index == all_index) ++tags_index; + } + } + + void Synchronize(VisitorSynchronization::SyncTag tag) { + if (collecting_all_references_ && + previous_reference_count_ != all_references_.length()) { + previous_reference_count_ = all_references_.length(); + reference_tags_.Add(IndexTag(previous_reference_count_, tag)); + } + } + + private: + bool collecting_all_references_; + List strong_references_; + List all_references_; + int previous_reference_count_; + List reference_tags_; }; @@ -2268,8 +2422,11 @@ bool V8HeapExplorer::IterateAndExtractReferences( return false; } SetRootGcRootsReference(); - RootsReferencesExtractor extractor(this); + RootsReferencesExtractor extractor; + heap_->IterateRoots(&extractor, VISIT_ONLY_STRONG); + extractor.SetCollectingAllReferences(); heap_->IterateRoots(&extractor, VISIT_ALL); + extractor.FillReferences(this); filler_ = NULL; return progress_->ProgressReport(false); } @@ -2359,6 +2516,24 @@ void V8HeapExplorer::SetHiddenReference(HeapObject* parent_obj, } +void V8HeapExplorer::SetWeakReference(HeapObject* parent_obj, + HeapEntry* parent_entry, + int index, + Object* child_obj, + int field_offset) { + HeapEntry* child_entry = GetEntry(child_obj); + if (child_entry != NULL) { + filler_->SetIndexedReference(HeapGraphEdge::kWeak, + parent_obj, + parent_entry, + index, + child_obj, + child_entry); + IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset); + } +} + + void V8HeapExplorer::SetPropertyReference(HeapObject* parent_obj, HeapEntry* parent_entry, String* reference_name, @@ -2421,12 +2596,21 @@ void V8HeapExplorer::SetRootShortcutReference(Object* child_obj) { } -void V8HeapExplorer::SetGcRootsReference(Object* child_obj) { +void V8HeapExplorer::SetGcRootsReference(VisitorSynchronization::SyncTag tag) { + filler_->SetIndexedAutoIndexReference( + HeapGraphEdge::kElement, + kGcRootsObject, snapshot_->gc_roots(), + GetNthGcSubrootObject(tag), snapshot_->gc_subroot(tag)); +} + + +void V8HeapExplorer::SetGcSubrootReference( + VisitorSynchronization::SyncTag tag, bool is_weak, Object* child_obj) { HeapEntry* child_entry = GetEntry(child_obj); if (child_entry != NULL) { filler_->SetIndexedAutoIndexReference( - HeapGraphEdge::kElement, - kGcRootsObject, snapshot_->gc_roots(), + is_weak ? HeapGraphEdge::kWeak : HeapGraphEdge::kElement, + GetNthGcSubrootObject(tag), snapshot_->gc_subroot(tag), child_obj, child_entry); } } @@ -3235,7 +3419,8 @@ void HeapSnapshotJSONSerializer::SerializeEdge(HeapGraphEdge* edge) { writer_->AddNumber(edge->type()); writer_->AddCharacter(','); if (edge->type() == HeapGraphEdge::kElement - || edge->type() == HeapGraphEdge::kHidden) { + || edge->type() == HeapGraphEdge::kHidden + || edge->type() == HeapGraphEdge::kWeak) { writer_->AddNumber(edge->index()); } else { writer_->AddNumber(GetStringId(edge->name())); @@ -3315,7 +3500,8 @@ void HeapSnapshotJSONSerializer::SerializeNodes() { "," JSON_S("property") "," JSON_S("internal") "," JSON_S("hidden") - "," JSON_S("shortcut")) + "," JSON_S("shortcut") + "," JSON_S("weak")) "," JSON_S("string_or_number") "," JSON_S("node")))))); #undef JSON_S diff --git a/src/profile-generator.h b/src/profile-generator.h index 44be3db78c..b47ce8255a 100644 --- a/src/profile-generator.h +++ b/src/profile-generator.h @@ -455,7 +455,8 @@ class HeapGraphEdge BASE_EMBEDDED { kProperty = v8::HeapGraphEdge::kProperty, kInternal = v8::HeapGraphEdge::kInternal, kHidden = v8::HeapGraphEdge::kHidden, - kShortcut = v8::HeapGraphEdge::kShortcut + kShortcut = v8::HeapGraphEdge::kShortcut, + kWeak = v8::HeapGraphEdge::kWeak }; HeapGraphEdge() { } @@ -465,7 +466,7 @@ class HeapGraphEdge BASE_EMBEDDED { Type type() { return static_cast(type_); } int index() { - ASSERT(type_ == kElement || type_ == kHidden); + ASSERT(type_ == kElement || type_ == kHidden || type_ == kWeak); return index_; } const char* name() { @@ -588,7 +589,8 @@ class HeapEntry BASE_EMBEDDED { int EntrySize() { return EntriesSize(1, children_count_, retainers_count_); } int RetainedSize(bool exact); - void Print(int max_depth, int indent); + void Print( + const char* prefix, const char* edge_name, int max_depth, int indent); Handle GetHeapObject(); @@ -661,6 +663,7 @@ class HeapSnapshot { HeapEntry* root() { return root_entry_; } HeapEntry* gc_roots() { return gc_roots_entry_; } HeapEntry* natives_root() { return natives_root_entry_; } + HeapEntry* gc_subroot(int index) { return gc_subroot_entries_[index]; } List* entries() { return &entries_; } int raw_entries_size() { return raw_entries_size_; } @@ -674,6 +677,9 @@ class HeapSnapshot { int retainers_count); HeapEntry* AddRootEntry(int children_count); HeapEntry* AddGcRootsEntry(int children_count, int retainers_count); + HeapEntry* AddGcSubrootEntry(int tag, + int children_count, + int retainers_count); HeapEntry* AddNativesRootEntry(int children_count, int retainers_count); void ClearPaint(); HeapEntry* GetEntryById(uint64_t id); @@ -695,6 +701,7 @@ class HeapSnapshot { HeapEntry* root_entry_; HeapEntry* gc_roots_entry_; HeapEntry* natives_root_entry_; + HeapEntry* gc_subroot_entries_[VisitorSynchronization::kNumberOfSyncTags]; char* raw_entries_; List entries_; bool entries_sorted_; @@ -716,10 +723,13 @@ class HeapObjectsMap { void MoveObject(Address from, Address to); static uint64_t GenerateId(v8::RetainedObjectInfo* info); + static inline uint64_t GetNthGcSubrootId(int delta); + static const int kObjectIdStep = 2; static const uint64_t kInternalRootObjectId; static const uint64_t kGcRootsObjectId; static const uint64_t kNativesRootObjectId; + static const uint64_t kGcRootsFirstSubrootId; static const uint64_t kFirstAvailableObjectId; private: @@ -969,6 +979,11 @@ class V8HeapExplorer : public HeapEntriesAllocator { HeapEntry* parent, int index, Object* child); + void SetWeakReference(HeapObject* parent_obj, + HeapEntry* parent_entry, + int index, + Object* child_obj, + int field_offset); void SetPropertyReference(HeapObject* parent_obj, HeapEntry* parent, String* reference_name, @@ -981,11 +996,16 @@ class V8HeapExplorer : public HeapEntriesAllocator { Object* child); void SetRootShortcutReference(Object* child); void SetRootGcRootsReference(); - void SetGcRootsReference(Object* child); + void SetGcRootsReference(VisitorSynchronization::SyncTag tag); + void SetGcSubrootReference( + VisitorSynchronization::SyncTag tag, bool is_weak, Object* child); void TagObject(Object* obj, const char* tag); HeapEntry* GetEntry(Object* obj); + static inline HeapObject* GetNthGcSubrootObject(int delta); + static inline int GetGcSubrootOrder(HeapObject* subroot); + Heap* heap_; HeapSnapshot* snapshot_; HeapSnapshotsCollection* collection_; @@ -994,8 +1014,11 @@ class V8HeapExplorer : public HeapEntriesAllocator { HeapObjectsSet objects_tags_; static HeapObject* const kGcRootsObject; + static HeapObject* const kFirstGcSubrootObject; + static HeapObject* const kLastGcSubrootObject; friend class IndexedReferencesExtractor; + friend class GcSubrootsEnumerator; friend class RootsReferencesExtractor; DISALLOW_COPY_AND_ASSIGN(V8HeapExplorer); diff --git a/test/cctest/test-heap-profiler.cc b/test/cctest/test-heap-profiler.cc index 81b68a713f..a9f2ceaecb 100644 --- a/test/cctest/test-heap-profiler.cc +++ b/test/cctest/test-heap-profiler.cc @@ -1065,3 +1065,82 @@ TEST(FastCaseGetter) { GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set-propWithSetter"); CHECK_NE(NULL, setterFunction); } + + +bool HasWeakEdge(const v8::HeapGraphNode* node) { + for (int i = 0; i < node->GetChildrenCount(); ++i) { + const v8::HeapGraphEdge* handle_edge = node->GetChild(i); + if (handle_edge->GetType() == v8::HeapGraphEdge::kWeak) return true; + } + return false; +} + + +bool HasWeakGlobalHandle() { + const v8::HeapSnapshot* snapshot = + v8::HeapProfiler::TakeSnapshot(v8_str("weaks")); + const v8::HeapGraphNode* gc_roots = GetNode( + snapshot->GetRoot(), v8::HeapGraphNode::kObject, "(GC roots)"); + CHECK_NE(NULL, gc_roots); + const v8::HeapGraphNode* global_handles = GetNode( + gc_roots, v8::HeapGraphNode::kObject, "(Global handles)"); + CHECK_NE(NULL, global_handles); + return HasWeakEdge(global_handles); +} + + +static void PersistentHandleCallback(v8::Persistent handle, void*) { + handle.Dispose(); +} + + +TEST(WeakGlobalHandle) { + v8::HandleScope scope; + LocalContext env; + + CHECK(!HasWeakGlobalHandle()); + + v8::Persistent handle = + v8::Persistent::New(v8::Object::New()); + handle.MakeWeak(NULL, PersistentHandleCallback); + + CHECK(HasWeakGlobalHandle()); +} + + +TEST(WeakGlobalContextRefs) { + v8::HandleScope scope; + LocalContext env; + + const v8::HeapSnapshot* snapshot = + v8::HeapProfiler::TakeSnapshot(v8_str("weaks")); + const v8::HeapGraphNode* gc_roots = GetNode( + snapshot->GetRoot(), v8::HeapGraphNode::kObject, "(GC roots)"); + CHECK_NE(NULL, gc_roots); + const v8::HeapGraphNode* global_handles = GetNode( + gc_roots, v8::HeapGraphNode::kObject, "(Global handles)"); + CHECK_NE(NULL, global_handles); + const v8::HeapGraphNode* global_context = GetNode( + global_handles, v8::HeapGraphNode::kHidden, "system / GlobalContext"); + CHECK_NE(NULL, global_context); + CHECK(HasWeakEdge(global_context)); +} + + +TEST(SfiAndJsFunctionWeakRefs) { + v8::HandleScope scope; + LocalContext env; + + CompileRun( + "fun = (function (x) { return function () { return x + 1; } })(1);"); + const v8::HeapSnapshot* snapshot = + v8::HeapProfiler::TakeSnapshot(v8_str("fun")); + const v8::HeapGraphNode* global = GetGlobalObject(snapshot); + CHECK_NE(NULL, global); + const v8::HeapGraphNode* fun = + GetProperty(global, v8::HeapGraphEdge::kShortcut, "fun"); + CHECK(HasWeakEdge(fun)); + const v8::HeapGraphNode* shared = + GetProperty(fun, v8::HeapGraphEdge::kInternal, "shared"); + CHECK(HasWeakEdge(shared)); +}