Chromium Code Reviews| Index: src/profile-generator.cc |
| diff --git a/src/profile-generator.cc b/src/profile-generator.cc |
| index 7612eab993ca123ab3bbe4a32022087f03050205..ea5f42929e98f9339f332cecc4073a7ebdb19f65 100644 |
| --- a/src/profile-generator.cc |
| +++ b/src/profile-generator.cc |
| @@ -29,6 +29,7 @@ |
| #include "v8.h" |
| #include "global-handles.h" |
| +#include "heap-profiler.h" |
| #include "scopeinfo.h" |
| #include "top.h" |
| #include "unicode.h" |
| @@ -94,51 +95,60 @@ StringsStorage::StringsStorage() |
| } |
| -static void DeleteIndexName(char** name_ptr) { |
| - DeleteArray(*name_ptr); |
| -} |
| - |
| - |
| StringsStorage::~StringsStorage() { |
| for (HashMap::Entry* p = names_.Start(); |
| p != NULL; |
| p = names_.Next(p)) { |
| DeleteArray(reinterpret_cast<const char*>(p->value)); |
| } |
| - index_names_.Iterate(DeleteIndexName); |
| +} |
| + |
| + |
| +const char* StringsStorage::PrintF(const char* format, ...) { |
| + va_list args; |
| + va_start(args, format); |
| + const char* result = VPrintF(format, args); |
| + va_end(args); |
| + return result; |
| +} |
| + |
| + |
| +const char* StringsStorage::AddOrDisposeString(char* str, uint32_t hash) { |
| + HashMap::Entry* cache_entry = names_.Lookup(str, hash, true); |
| + if (cache_entry->value == NULL) { |
| + // New entry added. |
| + cache_entry->value = str; |
| + } else { |
| + DeleteArray(str); |
| + } |
| + return reinterpret_cast<const char*>(cache_entry->value); |
| +} |
| + |
| + |
| +const char* StringsStorage::VPrintF(const char* format, va_list args) { |
| + Vector<char> str = Vector<char>::New(1024); |
| + int len = OS::VSNPrintF(str, format, args); |
| + if (len == -1) { |
| + DeleteArray(str.start()); |
| + return format; |
| + } |
| + uint32_t hash = HashSequentialString(str.start(), len); |
| + return AddOrDisposeString(str.start(), hash); |
| } |
| const char* StringsStorage::GetName(String* name) { |
| if (name->IsString()) { |
| - char* c_name = |
| - name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL).Detach(); |
| - HashMap::Entry* cache_entry = names_.Lookup(c_name, name->Hash(), true); |
| - if (cache_entry->value == NULL) { |
| - // New entry added. |
| - cache_entry->value = c_name; |
| - } else { |
| - DeleteArray(c_name); |
| - } |
| - return reinterpret_cast<const char*>(cache_entry->value); |
| + return AddOrDisposeString( |
| + name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL).Detach(), |
| + name->Hash()); |
| } |
| return ""; |
| } |
| const char* StringsStorage::GetName(int index) { |
| - ASSERT(index >= 0); |
| - if (index_names_.length() <= index) { |
| - index_names_.AddBlock( |
| - NULL, index - index_names_.length() + 1); |
| - } |
| - if (index_names_[index] == NULL) { |
| - const int kMaximumNameLength = 32; |
| - char* name = NewArray<char>(kMaximumNameLength); |
| - OS::SNPrintF(Vector<char>(name, kMaximumNameLength), "%d", index); |
| - index_names_[index] = name; |
| - } |
| - return index_names_[index]; |
| + return PrintF("%d", index); |
| } |
| @@ -1009,6 +1019,7 @@ const char* HeapEntry::TypeAsString() { |
| case kArray: return "/array/"; |
| case kRegExp: return "/regexp/"; |
| case kHeapNumber: return "/number/"; |
| + case kNative: return "/native/"; |
| default: return "???"; |
| } |
| } |
| @@ -1205,6 +1216,7 @@ HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection, |
| uid_(uid), |
| root_entry_(NULL), |
| gc_roots_entry_(NULL), |
| + dom_subtrees_root_entry_(NULL), |
| raw_entries_(NULL), |
| entries_sorted_(false), |
| retaining_paths_(HeapEntry::Match) { |
| @@ -1279,6 +1291,19 @@ HeapEntry* HeapSnapshot::AddGcRootsEntry(int children_count, |
| } |
| +HeapEntry* HeapSnapshot::AddNativesRootEntry(int children_count, |
| + int retainers_count) { |
| + ASSERT(dom_subtrees_root_entry_ == NULL); |
| + return (dom_subtrees_root_entry_ = AddEntry( |
| + HeapEntry::kObject, |
| + "(Native objects)", |
| + HeapObjectsMap::kNativesRootObjectId, |
| + 0, |
| + children_count, |
| + retainers_count)); |
| +} |
| + |
| + |
| HeapEntry* HeapSnapshot::AddEntry(HeapEntry::Type type, |
| const char* name, |
| uint64_t id, |
| @@ -1372,10 +1397,11 @@ void HeapSnapshot::Print(int max_depth) { |
| } |
| -const uint64_t HeapObjectsMap::kInternalRootObjectId = 0; |
| -const uint64_t HeapObjectsMap::kGcRootsObjectId = 1; |
| +const uint64_t HeapObjectsMap::kInternalRootObjectId = 1; |
|
Vitaly Repeshko
2011/03/09 14:15:49
Document why these have to be odd.
mnaganov (inactive)
2011/03/09 15:26:32
Done.
|
| +const uint64_t HeapObjectsMap::kGcRootsObjectId = 3; |
| +const uint64_t HeapObjectsMap::kNativesRootObjectId = 5; |
| // Increase kFirstAvailableObjectId if new 'special' objects appear. |
| -const uint64_t HeapObjectsMap::kFirstAvailableObjectId = 2; |
| +const uint64_t HeapObjectsMap::kFirstAvailableObjectId = 7; |
| HeapObjectsMap::HeapObjectsMap() |
| : initial_fill_mode_(true), |
| @@ -1400,7 +1426,8 @@ uint64_t HeapObjectsMap::FindObject(Address addr) { |
| uint64_t existing = FindEntry(addr); |
| if (existing != 0) return existing; |
| } |
| - uint64_t id = next_id_++; |
| + uint64_t id = next_id_; |
| + next_id_ += 2; |
| AddEntry(addr, id); |
| return id; |
| } |
| @@ -1468,6 +1495,17 @@ void HeapObjectsMap::RemoveDeadEntries() { |
| } |
| +uint64_t HeapObjectsMap::GenerateId(v8::RetainedObjectInfo* info) { |
| + uint64_t id = static_cast<uint64_t>(info->GetHash()); |
| + const char* label = info->GetLabel(); |
| + id ^= HashSequentialString(label, strlen(label)); |
| + intptr_t element_count = info->GetElementCount(); |
| + if (element_count != -1) |
| + id ^= ComputeIntegerHash(static_cast<uint32_t>(element_count)); |
| + return id << 1; |
| +} |
| + |
| + |
| HeapSnapshotsCollection::HeapSnapshotsCollection() |
| : is_tracking_objects_(false), |
| snapshots_uids_(HeapSnapshotsMatch), |
| @@ -1517,6 +1555,15 @@ HeapSnapshot* HeapSnapshotsCollection::GetSnapshot(unsigned uid) { |
| } |
| +const char* HeapSnapshotsCollection::PrintF(const char* format, ...) { |
| + va_list args; |
| + va_start(args, format); |
| + const char* result = names_.VPrintF(format, args); |
| + va_end(args); |
| + return result; |
| +} |
| + |
| + |
| HeapSnapshotsDiff* HeapSnapshotsCollection::CompareSnapshots( |
| HeapSnapshot* snapshot1, |
| HeapSnapshot* snapshot2) { |
| @@ -1551,6 +1598,8 @@ void HeapEntriesMap::AllocateEntries() { |
| p->key, |
| entry_info->children_count, |
| entry_info->retainers_count); |
| + ASSERT(entry_info->entry != NULL); |
| + ASSERT(entry_info->entry != kHeapEntryPlaceholder); |
| entry_info->children_count = 0; |
| entry_info->retainers_count = 0; |
| } |
| @@ -1632,7 +1681,7 @@ void HeapObjectsSet::Insert(Object* obj) { |
| HeapObject *const V8HeapExplorer::kInternalRootObject = |
| reinterpret_cast<HeapObject*>(1); |
| HeapObject *const V8HeapExplorer::kGcRootsObject = |
| - reinterpret_cast<HeapObject*>(2); |
| + reinterpret_cast<HeapObject*>(3); |
|
Vitaly Repeshko
2011/03/09 14:15:49
Shouldn't the constants defined above be reused he
mnaganov (inactive)
2011/03/09 15:26:32
Done.
|
| V8HeapExplorer::V8HeapExplorer( |
| @@ -1749,8 +1798,8 @@ HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object, |
| void V8HeapExplorer::AddRootEntries(SnapshotFillerInterface* filler) { |
| - filler->AddEntry(kInternalRootObject); |
| - filler->AddEntry(kGcRootsObject); |
| + filler->AddEntry(kInternalRootObject, this); |
| + filler->AddEntry(kGcRootsObject, this); |
| } |
| @@ -1940,7 +1989,7 @@ void V8HeapExplorer::ExtractInternalReferences(JSObject* js_obj, |
| HeapEntry* V8HeapExplorer::GetEntry(Object* obj) { |
| if (!obj->IsHeapObject()) return NULL; |
| - return filler_->FindOrAddEntry(obj); |
| + return filler_->FindOrAddEntry(obj, this); |
| } |
| @@ -2132,25 +2181,213 @@ void V8HeapExplorer::SetGcRootsReference(Object* child_obj) { |
| } |
| +class GlobalHandlesExtractor : public ObjectVisitor { |
| + public: |
| + explicit GlobalHandlesExtractor(NativeObjectsExplorer* explorer) |
| + : explorer_(explorer) {} |
| + virtual ~GlobalHandlesExtractor() {} |
| + virtual void VisitPointers(Object** start, Object** end) { |
| + UNREACHABLE(); |
| + } |
| + virtual void VisitEmbedderReference(Object** p, uint16_t class_id) { |
| + explorer_->VisitSubtreeWrapper(p, class_id); |
| + } |
| + private: |
| + NativeObjectsExplorer* explorer_; |
| +}; |
| + |
| +HeapThing const NativeObjectsExplorer::kNativesRootObject = |
| + reinterpret_cast<HeapThing>(5); |
|
Vitaly Repeshko
2011/03/09 14:15:49
Same as above.
mnaganov (inactive)
2011/03/09 15:26:32
Done.
|
| + |
| + |
| +NativeObjectsExplorer::NativeObjectsExplorer( |
| + HeapSnapshot* snapshot, SnapshottingProgressReportingInterface* progress) |
| + : snapshot_(snapshot), |
| + collection_(snapshot_->collection()), |
| + progress_(progress), |
| + embedder_queried_(false), |
| + objects_by_info_(RetainedInfosMatch), |
| + filler_(NULL) { |
| +} |
| + |
| + |
| +NativeObjectsExplorer::~NativeObjectsExplorer() { |
| + 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); |
| + info->Dispose(); |
| + List<HeapObject*>* objects = |
| + 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 { |
| + 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_->PrintF("%s / %" V8_PTR_PREFIX "d entries", |
| + info->GetLabel(), |
| + info->GetElementCount()) : |
| + info->GetLabel(), |
|
Vitaly Repeshko
2011/03/09 14:15:49
What if the returned label's lifetime is tied to t
mnaganov (inactive)
2011/03/09 15:26:32
Fixed.
|
| + HeapObjectsMap::GenerateId(info), |
| + size != -1 ? static_cast<int>(size) : 0, |
| + children_count, |
| + retainers_count); |
| + } |
| +} |
| + |
| + |
| +void NativeObjectsExplorer::AddRootEntries(SnapshotFillerInterface* filler) { |
| + if (EstimateObjectsCount() <= 0) return; |
| + filler->AddEntry(kNativesRootObject, this); |
| +} |
| + |
| + |
| +int NativeObjectsExplorer::EstimateObjectsCount() { |
| + FillRetainedObjects(); |
| + return objects_by_info_.occupancy(); |
| +} |
| + |
| + |
| +void NativeObjectsExplorer::FillRetainedObjects() { |
| + if (embedder_queried_) return; |
| + // Record objects that are joined into ObjectGroups. |
| + Heap::call_global_gc_prologue_callback(); |
| + List<ObjectGroup*>* groups = GlobalHandles::ObjectGroups(); |
| + for (int i = 0; i < groups->length(); ++i) { |
| + ObjectGroup* group = groups->at(i); |
| + if (group->info_ == NULL) continue; |
| + List<HeapObject*>* list = GetListMaybeDisposeInfo(group->info_); |
| + for (int j = 0; j < group->objects_.length(); ++j) { |
| + HeapObject* obj = HeapObject::cast(*group->objects_[j]); |
| + list->Add(obj); |
| + in_groups_.Insert(obj); |
| + } |
| + group->info_ = NULL; // Acquire info object ownership. |
| + } |
| + GlobalHandles::RemoveObjectGroups(); |
| + Heap::call_global_gc_epilogue_callback(); |
| + // Record objects that are not in ObjectGroups, but have class ID. |
| + GlobalHandlesExtractor extractor(this); |
| + GlobalHandles::IterateAllRootsWithClassIds(&extractor); |
| + embedder_queried_ = true; |
| +} |
| + |
| + |
| +List<HeapObject*>* NativeObjectsExplorer::GetListMaybeDisposeInfo( |
| + v8::RetainedObjectInfo* info) { |
| + HashMap::Entry* entry = |
| + objects_by_info_.Lookup(info, InfoHash(info), true); |
| + if (entry->value != NULL) { |
| + info->Dispose(); |
| + } else { |
| + entry->value = new List<HeapObject*>(4); |
| + } |
| + return reinterpret_cast<List<HeapObject*>* >(entry->value); |
| +} |
| + |
| + |
| +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); |
| + } |
| + } |
| + SetRootNativesRootReference(); |
| + filler_ = NULL; |
| + return true; |
| +} |
| + |
| + |
| +void NativeObjectsExplorer::SetNativeRootReference( |
| + v8::RetainedObjectInfo* info) { |
| + HeapEntry* child_entry = filler_->FindOrAddEntry(info, this); |
| + ASSERT(child_entry != NULL); |
| + filler_->SetIndexedAutoIndexReference( |
| + HeapGraphEdge::kElement, |
| + kNativesRootObject, snapshot_->dom_subtrees_root(), |
| + info, child_entry); |
| +} |
| + |
| + |
| +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); |
| + ASSERT(info_entry != NULL); |
| + filler_->SetNamedReference(HeapGraphEdge::kInternal, |
| + wrapper, wrapper_entry, |
| + "Native", |
| + info, info_entry); |
| + filler_->SetIndexedAutoIndexReference(HeapGraphEdge::kElement, |
| + info, info_entry, |
| + wrapper, wrapper_entry); |
| +} |
| + |
| + |
| +void NativeObjectsExplorer::SetRootNativesRootReference() { |
| + filler_->SetIndexedAutoIndexReference( |
| + HeapGraphEdge::kElement, |
| + V8HeapExplorer::kInternalRootObject, snapshot_->root(), |
| + kNativesRootObject, snapshot_->dom_subtrees_root()); |
| +} |
| + |
| + |
| +void NativeObjectsExplorer::VisitSubtreeWrapper(Object** p, uint16_t class_id) { |
| + if (in_groups_.Contains(*p)) return; |
| + v8::RetainedObjectInfo* info = |
| + HeapProfiler::ExecuteWrapperClassCallback(class_id, p); |
| + if (info == NULL) return; |
| + GetListMaybeDisposeInfo(info)->Add(HeapObject::cast(*p)); |
| +} |
| + |
| + |
| HeapSnapshotGenerator::HeapSnapshotGenerator(HeapSnapshot* snapshot, |
| v8::ActivityControl* control) |
| : snapshot_(snapshot), |
| control_(control), |
| - v8_heap_explorer_(snapshot_, this) { |
| + v8_heap_explorer_(snapshot_, this), |
| + dom_explorer_(snapshot_, this) { |
| } |
| class SnapshotCounter : public SnapshotFillerInterface { |
| public: |
| - SnapshotCounter(HeapEntriesAllocator* allocator, HeapEntriesMap* entries) |
| - : allocator_(allocator), entries_(entries) { } |
| - HeapEntry* AddEntry(HeapThing ptr) { |
| - entries_->Pair(ptr, allocator_, HeapEntriesMap::kHeapEntryPlaceholder); |
| + explicit SnapshotCounter(HeapEntriesMap* entries) : entries_(entries) { } |
| + HeapEntry* AddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) { |
| + entries_->Pair(ptr, allocator, HeapEntriesMap::kHeapEntryPlaceholder); |
| return HeapEntriesMap::kHeapEntryPlaceholder; |
| } |
| - HeapEntry* FindOrAddEntry(HeapThing ptr) { |
| - HeapEntry* entry = entries_->Map(ptr); |
| - return entry != NULL ? entry : AddEntry(ptr); |
| + HeapEntry* FindEntry(HeapThing ptr) { |
| + return entries_->Map(ptr); |
| + } |
| + HeapEntry* FindOrAddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) { |
| + HeapEntry* entry = FindEntry(ptr); |
| + return entry != NULL ? entry : AddEntry(ptr, allocator); |
| } |
| void SetIndexedReference(HeapGraphEdge::Type, |
| HeapThing parent_ptr, |
| @@ -2183,7 +2420,6 @@ class SnapshotCounter : public SnapshotFillerInterface { |
| entries_->CountReference(parent_ptr, child_ptr); |
| } |
| private: |
| - HeapEntriesAllocator* allocator_; |
| HeapEntriesMap* entries_; |
| }; |
| @@ -2194,13 +2430,16 @@ class SnapshotFiller : public SnapshotFillerInterface { |
| : snapshot_(snapshot), |
| collection_(snapshot->collection()), |
| entries_(entries) { } |
| - HeapEntry* AddEntry(HeapThing ptr) { |
| + HeapEntry* AddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) { |
| UNREACHABLE(); |
| return NULL; |
| } |
| - HeapEntry* FindOrAddEntry(HeapThing ptr) { |
| - HeapEntry* entry = entries_->Map(ptr); |
| - return entry != NULL ? entry : AddEntry(ptr); |
| + HeapEntry* FindEntry(HeapThing ptr) { |
| + return entries_->Map(ptr); |
| + } |
| + HeapEntry* FindOrAddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) { |
| + HeapEntry* entry = FindEntry(ptr); |
| + return entry != NULL ? entry : AddEntry(ptr, allocator); |
| } |
| void SetIndexedReference(HeapGraphEdge::Type type, |
| HeapThing parent_ptr, |
| @@ -2303,21 +2542,28 @@ bool HeapSnapshotGenerator::ProgressReport(bool force) { |
| void HeapSnapshotGenerator::SetProgressTotal(int iterations_count) { |
| if (control_ == NULL) return; |
| - progress_total_ = v8_heap_explorer_.EstimateObjectsCount() * iterations_count; |
| + progress_total_ = ( |
| + v8_heap_explorer_.EstimateObjectsCount() + |
| + dom_explorer_.EstimateObjectsCount()) * iterations_count; |
| progress_counter_ = 0; |
| } |
| bool HeapSnapshotGenerator::CountEntriesAndReferences() { |
| - SnapshotCounter counter(&v8_heap_explorer_, &entries_); |
| + SnapshotCounter counter(&entries_); |
| v8_heap_explorer_.AddRootEntries(&counter); |
| - return v8_heap_explorer_.IterateAndExtractReferences(&counter); |
| + dom_explorer_.AddRootEntries(&counter); |
| + return |
| + v8_heap_explorer_.IterateAndExtractReferences(&counter) && |
| + dom_explorer_.IterateAndExtractReferences(&counter); |
| } |
| bool HeapSnapshotGenerator::FillReferences() { |
| SnapshotFiller filler(snapshot_, &entries_); |
| - return v8_heap_explorer_.IterateAndExtractReferences(&filler); |
| + return |
| + v8_heap_explorer_.IterateAndExtractReferences(&filler) && |
| + dom_explorer_.IterateAndExtractReferences(&filler); |
| } |
| @@ -2735,7 +2981,8 @@ void HeapSnapshotJSONSerializer::SerializeNodes() { |
| "," JSON_S("code") |
| "," JSON_S("closure") |
| "," JSON_S("regexp") |
| - "," JSON_S("number")) |
| + "," JSON_S("number") |
| + "," JSON_S("native")) |
| "," JSON_S("string") |
| "," JSON_S("number") |
| "," JSON_S("number") |