diff options
Diffstat (limited to 'deps/v8/src/profile-generator.cc')
-rw-r--r-- | deps/v8/src/profile-generator.cc | 632 |
1 files changed, 501 insertions, 131 deletions
diff --git a/deps/v8/src/profile-generator.cc b/deps/v8/src/profile-generator.cc index e319efb043..c6e6131a71 100644 --- a/deps/v8/src/profile-generator.cc +++ b/deps/v8/src/profile-generator.cc @@ -152,9 +152,12 @@ const char* StringsStorage::GetVFormatted(const char* format, va_list args) { const char* StringsStorage::GetName(String* name) { if (name->IsString()) { - return AddOrDisposeString( - name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL).Detach(), - name->Hash()); + int length = Min(kMaxNameSize, name->length()); + SmartArrayPointer<char> data = + name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL, 0, length); + uint32_t hash = + HashSequentialString(*data, length, name->GetHeap()->HashSeed()); + return AddOrDisposeString(data.Detach(), hash); } return ""; } @@ -493,8 +496,6 @@ void CpuProfile::Print() { CodeEntry* const CodeMap::kSharedFunctionCodeEntry = NULL; const CodeMap::CodeTreeConfig::Key CodeMap::CodeTreeConfig::kNoKey = NULL; -const CodeMap::CodeTreeConfig::Value CodeMap::CodeTreeConfig::kNoValue = - CodeMap::CodeEntryInfo(NULL, 0); void CodeMap::AddCode(Address addr, CodeEntry* entry, unsigned size) { @@ -903,7 +904,7 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) { entry++; } - for (const Address *stack_pos = sample.stack, + for (const Address* stack_pos = sample.stack, *stack_end = stack_pos + sample.frames_count; stack_pos != stack_end; ++stack_pos) { @@ -943,7 +944,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; @@ -1058,8 +1059,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 { @@ -1078,29 +1082,40 @@ void HeapEntry::Print(int max_depth, int indent) { Vector<HeapGraphEdge> ch = children(); for (int i = 0; i < ch.length(); ++i) { HeapGraphEdge& edge = ch[i]; + const char* edge_prefix = ""; + ScopedVector<char> 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); } } @@ -1116,6 +1131,7 @@ const char* HeapEntry::TypeAsString() { case kRegExp: return "/regexp/"; case kHeapNumber: return "/number/"; case kNative: return "/native/"; + case kSynthetic: return "/synthetic/"; default: return "???"; } } @@ -1219,7 +1235,10 @@ HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection, SnapshotSizeConstants<kPointerSize>::kExpectedHeapGraphEdgeSize); STATIC_ASSERT( sizeof(HeapEntry) == - SnapshotSizeConstants<sizeof(void*)>::kExpectedHeapEntrySize); // NOLINT + SnapshotSizeConstants<kPointerSize>::kExpectedHeapEntrySize); + for (int i = 0; i < VisitorSynchronization::kNumberOfSyncTags; ++i) { + gc_subroot_entries_[i] = NULL; + } } HeapSnapshot::~HeapSnapshot() { @@ -1275,13 +1294,15 @@ HeapEntry* HeapSnapshot::AddGcRootsEntry(int children_count, } -HeapEntry* HeapSnapshot::AddNativesRootEntry(int children_count, - int retainers_count) { - ASSERT(natives_root_entry_ == NULL); - return (natives_root_entry_ = AddEntry( +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, - "(Native objects)", - HeapObjectsMap::kNativesRootObjectId, + VisitorSynchronization::kTagNames[tag], + HeapObjectsMap::GetNthGcSubrootId(tag), 0, children_count, retainers_count)); @@ -1360,17 +1381,20 @@ List<HeapEntry*>* 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::kGcRootsFirstSubrootId = + HeapObjectsMap::kGcRootsObjectId + HeapObjectsMap::kObjectIdStep; +const uint64_t HeapObjectsMap::kFirstAvailableObjectId = + HeapObjectsMap::kGcRootsFirstSubrootId + + VisitorSynchronization::kNumberOfSyncTags * HeapObjectsMap::kObjectIdStep; HeapObjectsMap::HeapObjectsMap() : initial_fill_mode_(true), @@ -1396,7 +1420,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; } @@ -1408,10 +1432,12 @@ void HeapObjectsMap::MoveObject(Address from, Address to) { if (entry != NULL) { void* value = entry->value; entries_map_.Remove(from, AddressHash(from)); - entry = entries_map_.Lookup(to, AddressHash(to), true); - // We can have an entry at the new location, it is OK, as GC can overwrite - // dead objects with alive objects being moved. - entry->value = value; + if (to != NULL) { + entry = entries_map_.Lookup(to, AddressHash(to), true); + // We can have an entry at the new location, it is OK, as GC can overwrite + // dead objects with alive objects being moved. + entry->value = value; + } } } @@ -1536,6 +1562,9 @@ void HeapSnapshotsCollection::RemoveSnapshot(HeapSnapshot* snapshot) { Handle<HeapObject> HeapSnapshotsCollection::FindHeapObjectById(uint64_t id) { + // First perform a full GC in order to avoid dead objects. + HEAP->CollectAllGarbage(Heap::kMakeHeapIterableMask, + "HeapSnapshotsCollection::FindHeapObjectById"); AssertNoAllocation no_allocation; HeapObject* object = NULL; HeapIterator iterator(HeapIterator::kFilterUnreachable); @@ -1553,7 +1582,7 @@ Handle<HeapObject> HeapSnapshotsCollection::FindHeapObjectById(uint64_t id) { } -HeapEntry *const HeapEntriesMap::kHeapEntryPlaceholder = +HeapEntry* const HeapEntriesMap::kHeapEntryPlaceholder = reinterpret_cast<HeapEntry*>(1); HeapEntriesMap::HeapEntriesMap() @@ -1682,12 +1711,18 @@ void HeapObjectsSet::SetTag(Object* obj, const char* tag) { } -HeapObject *const V8HeapExplorer::kInternalRootObject = +HeapObject* const V8HeapExplorer::kInternalRootObject = reinterpret_cast<HeapObject*>( static_cast<intptr_t>(HeapObjectsMap::kInternalRootObjectId)); -HeapObject *const V8HeapExplorer::kGcRootsObject = +HeapObject* const V8HeapExplorer::kGcRootsObject = reinterpret_cast<HeapObject*>( static_cast<intptr_t>(HeapObjectsMap::kGcRootsObjectId)); +HeapObject* const V8HeapExplorer::kFirstGcSubrootObject = + reinterpret_cast<HeapObject*>( + static_cast<intptr_t>(HeapObjectsMap::kGcRootsFirstSubrootId)); +HeapObject* const V8HeapExplorer::kLastGcSubrootObject = + reinterpret_cast<HeapObject*>( + static_cast<intptr_t>(HeapObjectsMap::kFirstAvailableObjectId)); V8HeapExplorer::V8HeapExplorer( @@ -1720,6 +1755,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( @@ -1783,6 +1823,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() || @@ -1822,9 +1874,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); } @@ -1843,12 +1924,13 @@ const char* V8HeapExplorer::GetSystemEntryName(HeapObject* object) { } -int V8HeapExplorer::EstimateObjectsCount() { - HeapIterator iterator(HeapIterator::kFilterUnreachable); +int V8HeapExplorer::EstimateObjectsCount(HeapIterator* iterator) { int objects_count = 0; - for (HeapObject* obj = iterator.next(); + for (HeapObject* obj = iterator->next(); obj != NULL; - obj = iterator.next(), ++objects_count) {} + obj = iterator->next()) { + objects_count++; + } return objects_count; } @@ -1921,6 +2003,7 @@ void V8HeapExplorer::ExtractReferences(HeapObject* obj) { SetPropertyReference( obj, entry, heap_->prototype_symbol(), proto_or_map, + NULL, JSFunction::kPrototypeOrInitialMapOffset); } else { SetPropertyReference( @@ -1935,10 +2018,17 @@ void V8HeapExplorer::ExtractReferences(HeapObject* obj) { SetInternalReference(js_fun, entry, "context", js_fun->unchecked_context(), JSFunction::kContextOffset); - TagObject(js_fun->literals(), "(function literals)"); + TagObject(js_fun->literals_or_bindings(), + "(function literals_or_bindings)"); SetInternalReference(js_fun, entry, - "literals", js_fun->literals(), + "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, @@ -1954,6 +2044,10 @@ void V8HeapExplorer::ExtractReferences(HeapObject* obj) { SetInternalReference(obj, entry, 1, cs->first()); SetInternalReference(obj, entry, 2, cs->second()); } + if (obj->IsSlicedString()) { + SlicedString* ss = SlicedString::cast(obj); + SetInternalReference(obj, entry, "parent", ss->parent()); + } extract_indexed_refs = false; } else if (obj->IsGlobalContext()) { Context* context = Context::cast(obj); @@ -1961,8 +2055,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, @@ -1976,6 +2076,14 @@ void V8HeapExplorer::ExtractReferences(HeapObject* obj) { "descriptors", map->instance_descriptors(), Map::kInstanceDescriptorsOrBitField3Offset); } + if (map->prototype_transitions() != heap_->empty_fixed_array()) { + TagObject(map->prototype_transitions(), "(prototype transitions)"); + SetInternalReference(obj, + entry, + "prototype_transitions", + map->prototype_transitions(), + Map::kPrototypeTransitionsOffset); + } SetInternalReference(obj, entry, "code_cache", map->code_cache(), Map::kCodeCacheOffset); @@ -1997,6 +2105,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, @@ -2052,20 +2163,27 @@ void V8HeapExplorer::ExtractReferences(HeapObject* obj) { void V8HeapExplorer::ExtractClosureReferences(JSObject* js_obj, HeapEntry* entry) { if (js_obj->IsJSFunction()) { - HandleScope hs; JSFunction* func = JSFunction::cast(js_obj); Context* context = func->context(); - ZoneScope zscope(Isolate::Current(), DELETE_ON_EXIT); - SerializedScopeInfo* serialized_scope_info = - context->closure()->shared()->scope_info(); - ScopeInfo<ZoneListAllocationPolicy> zone_scope_info(serialized_scope_info); - int locals_number = zone_scope_info.NumberOfLocals(); - for (int i = 0; i < locals_number; ++i) { - String* local_name = *zone_scope_info.LocalName(i); - int idx = serialized_scope_info->ContextSlotIndex(local_name, NULL); - if (idx >= 0 && idx < context->length()) { - SetClosureReference(js_obj, entry, local_name, context->get(idx)); - } + ScopeInfo* scope_info = context->closure()->shared()->scope_info(); + + // Add context allocated locals. + int context_locals = scope_info->ContextLocalCount(); + for (int i = 0; i < context_locals; ++i) { + String* local_name = scope_info->ContextLocalName(i); + int idx = Context::MIN_CONTEXT_SLOTS + i; + SetClosureReference(js_obj, entry, local_name, context->get(idx)); + } + + // Add function variable. + if (scope_info->HasFunctionName()) { + String* name = scope_info->FunctionName(); + int idx = Context::MIN_CONTEXT_SLOTS + context_locals; +#ifdef DEBUG + VariableMode mode; + ASSERT(idx == scope_info->FunctionContextSlotIndex(name, &mode)); +#endif + SetClosureReference(js_obj, entry, name, context->get(idx)); } } } @@ -2083,6 +2201,7 @@ void V8HeapExplorer::ExtractPropertyReferences(JSObject* js_obj, SetPropertyReference( js_obj, entry, descs->GetKey(i), js_obj->InObjectPropertyAt(index), + NULL, js_obj->GetInObjectPropertyOffset(index)); } else { SetPropertyReference( @@ -2096,7 +2215,29 @@ void V8HeapExplorer::ExtractPropertyReferences(JSObject* js_obj, js_obj, entry, descs->GetKey(i), descs->GetConstantFunction(i)); break; - default: ; + case CALLBACKS: { + Object* callback_obj = descs->GetValue(i); + if (callback_obj->IsAccessorPair()) { + AccessorPair* accessors = AccessorPair::cast(callback_obj); + if (Object* getter = accessors->getter()) { + SetPropertyReference(js_obj, entry, descs->GetKey(i), + getter, "get-%s"); + } + if (Object* setter = accessors->setter()) { + SetPropertyReference(js_obj, entry, descs->GetKey(i), + setter, "set-%s"); + } + } + break; + } + case NORMAL: // only in slow mode + case HANDLER: // only in lookup results, not in descriptors + case INTERCEPTOR: // only in lookup results, not in descriptors + case MAP_TRANSITION: // we do not care about transitions here... + case ELEMENTS_TRANSITION: + case CONSTANT_TRANSITION: + case NULL_DESCRIPTOR: // ... and not about "holes" + break; } } } else { @@ -2161,15 +2302,16 @@ void V8HeapExplorer::ExtractInternalReferences(JSObject* js_obj, String* V8HeapExplorer::GetConstructorName(JSObject* object) { - if (object->IsJSFunction()) return HEAP->closure_symbol(); + Heap* heap = object->GetHeap(); + if (object->IsJSFunction()) return heap->closure_symbol(); String* constructor_name = object->constructor_name(); - if (constructor_name == HEAP->Object_symbol()) { + if (constructor_name == heap->Object_symbol()) { // Look up an immediate "constructor" property, if it is a function, // return its name. This is for instances of binding objects, which // have prototype constructor type "Object". Object* constructor_prop = NULL; - LookupResult result; - object->LocalLookupRealNamedProperty(HEAP->constructor_symbol(), &result); + LookupResult result(heap->isolate()); + object->LocalLookupRealNamedProperty(heap->constructor_symbol(), &result); if (result.IsProperty()) { constructor_prop = result.GetLazyValue(); } @@ -2192,23 +2334,76 @@ HeapEntry* V8HeapExplorer::GetEntry(Object* obj) { class RootsReferencesExtractor : public ObjectVisitor { + private: + struct IndexTag { + IndexTag(int index, VisitorSynchronization::SyncTag tag) + : index(index), tag(tag) { } + int index; + VisitorSynchronization::SyncTag tag; + }; + public: - explicit RootsReferencesExtractor(V8HeapExplorer* explorer) - : explorer_(explorer) { + RootsReferencesExtractor() + : collecting_all_references_(false), + previous_reference_count_(0) { } + void VisitPointers(Object** start, Object** end) { - for (Object** p = start; p < end; p++) explorer_->SetGcRootsReference(*p); + 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: - V8HeapExplorer* explorer_; + bool collecting_all_references_; + List<Object*> strong_references_; + List<Object*> all_references_; + int previous_reference_count_; + List<IndexTag> reference_tags_; }; bool V8HeapExplorer::IterateAndExtractReferences( SnapshotFillerInterface* filler) { - filler_ = filler; HeapIterator iterator(HeapIterator::kFilterUnreachable); + + filler_ = filler; bool interrupted = false; + // Heap iteration with filtering must be finished in any case. for (HeapObject* obj = iterator.next(); obj != NULL; @@ -2223,8 +2418,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); } @@ -2314,19 +2512,45 @@ 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, Object* child_obj, + const char* name_format_string, int field_offset) { HeapEntry* child_entry = GetEntry(child_obj); if (child_entry != NULL) { HeapGraphEdge::Type type = reference_name->length() > 0 ? HeapGraphEdge::kProperty : HeapGraphEdge::kInternal; + const char* name = name_format_string != NULL ? + collection_->names()->GetFormatted( + name_format_string, + *reference_name->ToCString(DISALLOW_NULLS, + ROBUST_STRING_TRAVERSAL)) : + collection_->names()->GetName(reference_name); + filler_->SetNamedReference(type, parent_obj, parent_entry, - collection_->names()->GetName(reference_name), + name, child_obj, child_entry); IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset); @@ -2368,12 +2592,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); } } @@ -2417,6 +2650,7 @@ class GlobalObjectsEnumerator : public ObjectVisitor { // Modifies heap. Must not be run during heap traversal. void V8HeapExplorer::TagGlobalObjects() { + HandleScope scope; Isolate* isolate = Isolate::Current(); GlobalObjectsEnumerator enumerator; isolate->global_handles()->IterateAllRoots(&enumerator); @@ -2427,6 +2661,7 @@ void V8HeapExplorer::TagGlobalObjects() { const char** urls = NewArray<const char*>(enumerator.count()); for (int i = 0, l = enumerator.count(); i < l; ++i) { urls[i] = NULL; + HandleScope scope; Handle<JSGlobalObject> global_obj = enumerator.at(i); Object* obj_document; if (global_obj->GetProperty(*document_string)->ToObject(&obj_document) && @@ -2464,9 +2699,43 @@ class GlobalHandlesExtractor : public ObjectVisitor { NativeObjectsExplorer* explorer_; }; -HeapThing const NativeObjectsExplorer::kNativesRootObject = - reinterpret_cast<HeapThing>( - static_cast<intptr_t>(HeapObjectsMap::kNativesRootObjectId)); + +class BasicHeapEntriesAllocator : public HeapEntriesAllocator { + public: + BasicHeapEntriesAllocator( + HeapSnapshot* snapshot, + HeapEntry::Type entries_type) + : snapshot_(snapshot), + collection_(snapshot_->collection()), + entries_type_(entries_type) { + } + virtual HeapEntry* AllocateEntry( + HeapThing ptr, int children_count, int retainers_count); + private: + HeapSnapshot* snapshot_; + HeapSnapshotsCollection* collection_; + HeapEntry::Type entries_type_; +}; + + +HeapEntry* BasicHeapEntriesAllocator::AllocateEntry( + HeapThing ptr, int children_count, int retainers_count) { + v8::RetainedObjectInfo* info = reinterpret_cast<v8::RetainedObjectInfo*>(ptr); + intptr_t elements = info->GetElementCount(); + intptr_t size = info->GetSizeInBytes(); + return snapshot_->AddEntry( + entries_type_, + elements != -1 ? + collection_->names()->GetFormatted( + "%s / %" V8_PTR_PREFIX "d entries", + info->GetLabel(), + info->GetElementCount()) : + collection_->names()->GetCopy(info->GetLabel()), + HeapObjectsMap::GenerateId(info), + size != -1 ? static_cast<int>(size) : 0, + children_count, + retainers_count); +} NativeObjectsExplorer::NativeObjectsExplorer( @@ -2476,7 +2745,12 @@ NativeObjectsExplorer::NativeObjectsExplorer( progress_(progress), embedder_queried_(false), objects_by_info_(RetainedInfosMatch), + native_groups_(StringsMatch), filler_(NULL) { + synthetic_entries_allocator_ = + new BasicHeapEntriesAllocator(snapshot, HeapEntry::kSynthetic); + native_entries_allocator_ = + new BasicHeapEntriesAllocator(snapshot, HeapEntry::kNative); } @@ -2491,37 +2765,15 @@ NativeObjectsExplorer::~NativeObjectsExplorer() { reinterpret_cast<List<HeapObject*>* >(p->value); delete objects; } -} - - -HeapEntry* NativeObjectsExplorer::AllocateEntry( - HeapThing ptr, int children_count, int retainers_count) { - if (ptr == kNativesRootObject) { - return snapshot_->AddNativesRootEntry(children_count, retainers_count); - } else { + for (HashMap::Entry* p = native_groups_.Start(); + p != NULL; + p = native_groups_.Next(p)) { v8::RetainedObjectInfo* info = - reinterpret_cast<v8::RetainedObjectInfo*>(ptr); - intptr_t elements = info->GetElementCount(); - intptr_t size = info->GetSizeInBytes(); - return snapshot_->AddEntry( - HeapEntry::kNative, - elements != -1 ? - collection_->names()->GetFormatted( - "%s / %" V8_PTR_PREFIX "d entries", - info->GetLabel(), - info->GetElementCount()) : - collection_->names()->GetCopy(info->GetLabel()), - HeapObjectsMap::GenerateId(info), - size != -1 ? static_cast<int>(size) : 0, - children_count, - retainers_count); + reinterpret_cast<v8::RetainedObjectInfo*>(p->value); + info->Dispose(); } -} - - -void NativeObjectsExplorer::AddRootEntries(SnapshotFillerInterface* filler) { - if (EstimateObjectsCount() <= 0) return; - filler->AddEntry(kNativesRootObject, this); + delete synthetic_entries_allocator_; + delete native_entries_allocator_; } @@ -2556,6 +2808,29 @@ void NativeObjectsExplorer::FillRetainedObjects() { embedder_queried_ = true; } +void NativeObjectsExplorer::FillImplicitReferences() { + Isolate* isolate = Isolate::Current(); + List<ImplicitRefGroup*>* groups = + isolate->global_handles()->implicit_ref_groups(); + for (int i = 0; i < groups->length(); ++i) { + ImplicitRefGroup* group = groups->at(i); + HeapObject* parent = *group->parent_; + HeapEntry* parent_entry = + filler_->FindOrAddEntry(parent, native_entries_allocator_); + ASSERT(parent_entry != NULL); + Object*** children = group->children_; + for (size_t j = 0; j < group->length_; ++j) { + Object* child = *children[j]; + HeapEntry* child_entry = + filler_->FindOrAddEntry(child, native_entries_allocator_); + filler_->SetNamedReference( + HeapGraphEdge::kInternal, + parent, parent_entry, + "native", + child, child_entry); + } + } +} List<HeapObject*>* NativeObjectsExplorer::GetListMaybeDisposeInfo( v8::RetainedObjectInfo* info) { @@ -2572,34 +2847,82 @@ List<HeapObject*>* NativeObjectsExplorer::GetListMaybeDisposeInfo( bool NativeObjectsExplorer::IterateAndExtractReferences( SnapshotFillerInterface* filler) { - if (EstimateObjectsCount() <= 0) return true; filler_ = filler; FillRetainedObjects(); - for (HashMap::Entry* p = objects_by_info_.Start(); - p != NULL; - p = objects_by_info_.Next(p)) { - v8::RetainedObjectInfo* info = - reinterpret_cast<v8::RetainedObjectInfo*>(p->key); - SetNativeRootReference(info); - List<HeapObject*>* objects = - reinterpret_cast<List<HeapObject*>* >(p->value); - for (int i = 0; i < objects->length(); ++i) { - SetWrapperNativeReferences(objects->at(i), info); + FillImplicitReferences(); + if (EstimateObjectsCount() > 0) { + for (HashMap::Entry* p = objects_by_info_.Start(); + p != NULL; + p = objects_by_info_.Next(p)) { + v8::RetainedObjectInfo* info = + reinterpret_cast<v8::RetainedObjectInfo*>(p->key); + SetNativeRootReference(info); + List<HeapObject*>* objects = + reinterpret_cast<List<HeapObject*>* >(p->value); + for (int i = 0; i < objects->length(); ++i) { + SetWrapperNativeReferences(objects->at(i), info); + } } + SetRootNativeRootsReference(); } - SetRootNativesRootReference(); filler_ = NULL; return true; } +class NativeGroupRetainedObjectInfo : public v8::RetainedObjectInfo { + public: + explicit NativeGroupRetainedObjectInfo(const char* label) + : disposed_(false), + hash_(reinterpret_cast<intptr_t>(label)), + label_(label) { + } + + virtual ~NativeGroupRetainedObjectInfo() {} + virtual void Dispose() { + CHECK(!disposed_); + disposed_ = true; + delete this; + } + virtual bool IsEquivalent(RetainedObjectInfo* other) { + return hash_ == other->GetHash() && !strcmp(label_, other->GetLabel()); + } + virtual intptr_t GetHash() { return hash_; } + virtual const char* GetLabel() { return label_; } + + private: + bool disposed_; + intptr_t hash_; + const char* label_; +}; + + +NativeGroupRetainedObjectInfo* NativeObjectsExplorer::FindOrAddGroupInfo( + const char* label) { + const char* label_copy = collection_->names()->GetCopy(label); + uint32_t hash = HashSequentialString(label_copy, + static_cast<int>(strlen(label_copy)), + HEAP->HashSeed()); + HashMap::Entry* entry = native_groups_.Lookup(const_cast<char*>(label_copy), + hash, true); + if (entry->value == NULL) + entry->value = new NativeGroupRetainedObjectInfo(label); + return static_cast<NativeGroupRetainedObjectInfo*>(entry->value); +} + + void NativeObjectsExplorer::SetNativeRootReference( v8::RetainedObjectInfo* info) { - HeapEntry* child_entry = filler_->FindOrAddEntry(info, this); + HeapEntry* child_entry = + filler_->FindOrAddEntry(info, native_entries_allocator_); ASSERT(child_entry != NULL); - filler_->SetIndexedAutoIndexReference( - HeapGraphEdge::kElement, - kNativesRootObject, snapshot_->natives_root(), + NativeGroupRetainedObjectInfo* group_info = + FindOrAddGroupInfo(info->GetGroupLabel()); + HeapEntry* group_entry = + filler_->FindOrAddEntry(group_info, synthetic_entries_allocator_); + filler_->SetNamedAutoIndexReference( + HeapGraphEdge::kInternal, + group_info, group_entry, info, child_entry); } @@ -2608,7 +2931,8 @@ void NativeObjectsExplorer::SetWrapperNativeReferences( HeapObject* wrapper, v8::RetainedObjectInfo* info) { HeapEntry* wrapper_entry = filler_->FindEntry(wrapper); ASSERT(wrapper_entry != NULL); - HeapEntry* info_entry = filler_->FindOrAddEntry(info, this); + HeapEntry* info_entry = + filler_->FindOrAddEntry(info, native_entries_allocator_); ASSERT(info_entry != NULL); filler_->SetNamedReference(HeapGraphEdge::kInternal, wrapper, wrapper_entry, @@ -2620,11 +2944,20 @@ void NativeObjectsExplorer::SetWrapperNativeReferences( } -void NativeObjectsExplorer::SetRootNativesRootReference() { - filler_->SetIndexedAutoIndexReference( - HeapGraphEdge::kElement, - V8HeapExplorer::kInternalRootObject, snapshot_->root(), - kNativesRootObject, snapshot_->natives_root()); +void NativeObjectsExplorer::SetRootNativeRootsReference() { + for (HashMap::Entry* entry = native_groups_.Start(); + entry; + entry = native_groups_.Next(entry)) { + NativeGroupRetainedObjectInfo* group_info = + static_cast<NativeGroupRetainedObjectInfo*>(entry->value); + HeapEntry* group_entry = + filler_->FindOrAddEntry(group_info, native_entries_allocator_); + ASSERT(group_entry != NULL); + filler_->SetIndexedAutoIndexReference( + HeapGraphEdge::kElement, + V8HeapExplorer::kInternalRootObject, snapshot_->root(), + group_info, group_entry); + } } @@ -2774,13 +3107,47 @@ class SnapshotFiller : public SnapshotFillerInterface { bool HeapSnapshotGenerator::GenerateSnapshot() { v8_heap_explorer_.TagGlobalObjects(); + // TODO(1562) Profiler assumes that any object that is in the heap after + // full GC is reachable from the root when computing dominators. + // This is not true for weakly reachable objects. + // As a temporary solution we call GC twice. + Isolate::Current()->heap()->CollectAllGarbage( + Heap::kMakeHeapIterableMask, + "HeapSnapshotGenerator::GenerateSnapshot"); + Isolate::Current()->heap()->CollectAllGarbage( + Heap::kMakeHeapIterableMask, + "HeapSnapshotGenerator::GenerateSnapshot"); + +#ifdef DEBUG + Heap* debug_heap = Isolate::Current()->heap(); + ASSERT(!debug_heap->old_data_space()->was_swept_conservatively()); + ASSERT(!debug_heap->old_pointer_space()->was_swept_conservatively()); + ASSERT(!debug_heap->code_space()->was_swept_conservatively()); + ASSERT(!debug_heap->cell_space()->was_swept_conservatively()); + ASSERT(!debug_heap->map_space()->was_swept_conservatively()); +#endif + + // The following code uses heap iterators, so we want the heap to be + // stable. It should follow TagGlobalObjects as that can allocate. AssertNoAllocation no_alloc; +#ifdef DEBUG + debug_heap->Verify(); +#endif + SetProgressTotal(4); // 2 passes + dominators + sizes. +#ifdef DEBUG + debug_heap->Verify(); +#endif + // Pass 1. Iterate heap contents to count entries and references. if (!CountEntriesAndReferences()) return false; +#ifdef DEBUG + debug_heap->Verify(); +#endif + // Allocate and fill entries in the snapshot, allocate references. snapshot_->AllocateEntries(entries_.entries_count(), entries_.total_children_count(), @@ -2818,8 +3185,9 @@ bool HeapSnapshotGenerator::ProgressReport(bool force) { void HeapSnapshotGenerator::SetProgressTotal(int iterations_count) { if (control_ == NULL) return; + HeapIterator iterator(HeapIterator::kFilterUnreachable); progress_total_ = ( - v8_heap_explorer_.EstimateObjectsCount() + + v8_heap_explorer_.EstimateObjectsCount(&iterator) + dom_explorer_.EstimateObjectsCount()) * iterations_count; progress_counter_ = 0; } @@ -2828,7 +3196,6 @@ void HeapSnapshotGenerator::SetProgressTotal(int iterations_count) { bool HeapSnapshotGenerator::CountEntriesAndReferences() { SnapshotCounter counter(&entries_); v8_heap_explorer_.AddRootEntries(&counter); - dom_explorer_.AddRootEntries(&counter); return v8_heap_explorer_.IterateAndExtractReferences(&counter) && dom_explorer_.IterateAndExtractReferences(&counter); @@ -2869,7 +3236,7 @@ void HeapSnapshotGenerator::FillReversePostorderIndexes( nodes_to_visit.RemoveLast(); } } - entries->Truncate(current_entry); + ASSERT_EQ(current_entry, entries->length()); } @@ -3149,7 +3516,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())); @@ -3210,7 +3578,8 @@ void HeapSnapshotJSONSerializer::SerializeNodes() { "," JSON_S("closure") "," JSON_S("regexp") "," JSON_S("number") - "," JSON_S("native")) + "," JSON_S("native") + "," JSON_S("synthetic")) "," JSON_S("string") "," JSON_S("number") "," JSON_S("number") @@ -3229,7 +3598,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 |