| Index: src/profile-generator.cc
|
| ===================================================================
|
| --- src/profile-generator.cc (revision 7180)
|
| +++ src/profile-generator.cc (working copy)
|
| @@ -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,70 @@
|
| }
|
|
|
|
|
| -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::GetCopy(const char* src) {
|
| + int len = static_cast<int>(strlen(src));
|
| + Vector<char> dst = Vector<char>::New(len + 1);
|
| + OS::StrNCpy(dst, src, len);
|
| + dst[len] = '\0';
|
| + uint32_t hash = HashSequentialString(dst.start(), len);
|
| + return AddOrDisposeString(dst.start(), hash);
|
| +}
|
| +
|
| +
|
| +const char* StringsStorage::GetFormatted(const char* format, ...) {
|
| + va_list args;
|
| + va_start(args, format);
|
| + const char* result = GetVFormatted(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::GetVFormatted(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 GetFormatted("%d", index);
|
| }
|
|
|
|
|
| @@ -156,13 +176,18 @@
|
|
|
| uint32_t CodeEntry::GetCallUid() const {
|
| uint32_t hash = ComputeIntegerHash(tag_);
|
| - hash ^= ComputeIntegerHash(
|
| - static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name_prefix_)));
|
| - hash ^= ComputeIntegerHash(
|
| - static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name_)));
|
| - hash ^= ComputeIntegerHash(
|
| - static_cast<uint32_t>(reinterpret_cast<uintptr_t>(resource_name_)));
|
| - hash ^= ComputeIntegerHash(line_number_);
|
| + if (shared_id_ != 0) {
|
| + hash ^= ComputeIntegerHash(
|
| + static_cast<uint32_t>(shared_id_));
|
| + } else {
|
| + hash ^= ComputeIntegerHash(
|
| + static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name_prefix_)));
|
| + hash ^= ComputeIntegerHash(
|
| + static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name_)));
|
| + hash ^= ComputeIntegerHash(
|
| + static_cast<uint32_t>(reinterpret_cast<uintptr_t>(resource_name_)));
|
| + hash ^= ComputeIntegerHash(line_number_);
|
| + }
|
| return hash;
|
| }
|
|
|
| @@ -170,10 +195,12 @@
|
| bool CodeEntry::IsSameAs(CodeEntry* entry) const {
|
| return this == entry
|
| || (tag_ == entry->tag_
|
| - && name_prefix_ == entry->name_prefix_
|
| - && name_ == entry->name_
|
| - && resource_name_ == entry->resource_name_
|
| - && line_number_ == entry->line_number_);
|
| + && shared_id_ == entry->shared_id_
|
| + && (shared_id_ != 0
|
| + || (name_prefix_ == entry->name_prefix_
|
| + && name_ == entry->name_
|
| + && resource_name_ == entry->resource_name_
|
| + && line_number_ == entry->line_number_)));
|
| }
|
|
|
|
|
| @@ -458,23 +485,12 @@
|
| }
|
|
|
|
|
| +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::AddAlias(Address start, CodeEntry* entry, Address code_start) {
|
| - CodeTree::Locator locator;
|
| - if (tree_.Find(code_start, &locator)) {
|
| - const CodeEntryInfo& code_info = locator.value();
|
| - if (tree_.Insert(start, &locator)) {
|
| - entry->CopyData(*code_info.entry);
|
| - locator.set_value(CodeEntryInfo(entry, code_info.size));
|
| - }
|
| - }
|
| -}
|
| -
|
| -
|
| CodeEntry* CodeMap::FindEntry(Address addr) {
|
| CodeTree::Locator locator;
|
| if (tree_.FindGreatestLessThan(addr, &locator)) {
|
| @@ -487,6 +503,22 @@
|
| }
|
|
|
|
|
| +int CodeMap::GetSharedId(Address addr) {
|
| + CodeTree::Locator locator;
|
| + // For shared function entries, 'size' field is used to store their IDs.
|
| + if (tree_.Find(addr, &locator)) {
|
| + const CodeEntryInfo& entry = locator.value();
|
| + ASSERT(entry.entry == kSharedFunctionCodeEntry);
|
| + return entry.size;
|
| + } else {
|
| + tree_.Insert(addr, &locator);
|
| + int id = next_shared_id_++;
|
| + locator.set_value(CodeEntryInfo(kSharedFunctionCodeEntry, id));
|
| + return id;
|
| + }
|
| +}
|
| +
|
| +
|
| void CodeMap::CodeTreePrinter::Call(
|
| const Address& key, const CodeMap::CodeEntryInfo& value) {
|
| OS::Print("%p %5d %s\n", key, value.size, value.entry->name());
|
| @@ -715,13 +747,6 @@
|
| }
|
|
|
|
|
| -CodeEntry* CpuProfilesCollection::NewCodeEntry(int security_token_id) {
|
| - CodeEntry* entry = new CodeEntry(security_token_id);
|
| - code_entries_.Add(entry);
|
| - return entry;
|
| -}
|
| -
|
| -
|
| void CpuProfilesCollection::AddPathToCurrentProfiles(
|
| const Vector<CodeEntry*>& path) {
|
| // As starting / stopping profiles is rare relatively to this
|
| @@ -784,19 +809,10 @@
|
| if (sample.pc != NULL) {
|
| *entry++ = code_map_.FindEntry(sample.pc);
|
|
|
| - if (sample.function != NULL) {
|
| - *entry = code_map_.FindEntry(sample.function);
|
| + if (sample.tos != NULL) {
|
| + *entry = code_map_.FindEntry(sample.tos);
|
| if (*entry != NULL && !(*entry)->is_js_function()) {
|
| *entry = NULL;
|
| - } else {
|
| - CodeEntry* pc_entry = *entries.start();
|
| - if (pc_entry == NULL) {
|
| - *entry = NULL;
|
| - } else if (pc_entry->is_js_function()) {
|
| - // Use function entry in favor of pc entry, as function
|
| - // entry has security token.
|
| - *entries.start() = NULL;
|
| - }
|
| }
|
| entry++;
|
| }
|
| @@ -1013,6 +1029,7 @@
|
| case kArray: return "/array/";
|
| case kRegExp: return "/regexp/";
|
| case kHeapNumber: return "/number/";
|
| + case kNative: return "/native/";
|
| default: return "???";
|
| }
|
| }
|
| @@ -1181,12 +1198,6 @@
|
| }
|
|
|
|
|
| -HeapObject *const HeapSnapshot::kInternalRootObject =
|
| - reinterpret_cast<HeapObject*>(1);
|
| -HeapObject *const HeapSnapshot::kGcRootsObject =
|
| - reinterpret_cast<HeapObject*>(2);
|
| -
|
| -
|
| // It is very important to keep objects that form a heap snapshot
|
| // as small as possible.
|
| namespace { // Avoid littering the global namespace.
|
| @@ -1215,6 +1226,7 @@
|
| uid_(uid),
|
| root_entry_(NULL),
|
| gc_roots_entry_(NULL),
|
| + natives_root_entry_(NULL),
|
| raw_entries_(NULL),
|
| entries_sorted_(false),
|
| retaining_paths_(HeapEntry::Match) {
|
| @@ -1257,96 +1269,6 @@
|
| }
|
|
|
|
|
| -HeapEntry* HeapSnapshot::AddEntry(HeapObject* object,
|
| - int children_count,
|
| - int retainers_count) {
|
| - if (object == kInternalRootObject) {
|
| - ASSERT(root_entry_ == NULL);
|
| - ASSERT(retainers_count == 0);
|
| - return (root_entry_ = AddEntry(HeapEntry::kObject,
|
| - "",
|
| - HeapObjectsMap::kInternalRootObjectId,
|
| - 0,
|
| - children_count,
|
| - retainers_count));
|
| - } else if (object == kGcRootsObject) {
|
| - ASSERT(gc_roots_entry_ == NULL);
|
| - return (gc_roots_entry_ = AddEntry(HeapEntry::kObject,
|
| - "(GC roots)",
|
| - HeapObjectsMap::kGcRootsObjectId,
|
| - 0,
|
| - children_count,
|
| - retainers_count));
|
| - } else if (object->IsJSFunction()) {
|
| - JSFunction* func = JSFunction::cast(object);
|
| - SharedFunctionInfo* shared = func->shared();
|
| - return AddEntry(object,
|
| - HeapEntry::kClosure,
|
| - collection_->GetName(String::cast(shared->name())),
|
| - children_count,
|
| - retainers_count);
|
| - } else if (object->IsJSRegExp()) {
|
| - JSRegExp* re = JSRegExp::cast(object);
|
| - return AddEntry(object,
|
| - HeapEntry::kRegExp,
|
| - collection_->GetName(re->Pattern()),
|
| - children_count,
|
| - retainers_count);
|
| - } else if (object->IsJSObject()) {
|
| - return AddEntry(object,
|
| - HeapEntry::kObject,
|
| - collection_->GetName(GetConstructorNameForHeapProfile(
|
| - JSObject::cast(object))),
|
| - children_count,
|
| - retainers_count);
|
| - } else if (object->IsString()) {
|
| - return AddEntry(object,
|
| - HeapEntry::kString,
|
| - collection_->GetName(String::cast(object)),
|
| - children_count,
|
| - retainers_count);
|
| - } else if (object->IsCode()) {
|
| - return AddEntry(object,
|
| - HeapEntry::kCode,
|
| - "",
|
| - children_count,
|
| - retainers_count);
|
| - } else if (object->IsSharedFunctionInfo()) {
|
| - SharedFunctionInfo* shared = SharedFunctionInfo::cast(object);
|
| - return AddEntry(object,
|
| - HeapEntry::kCode,
|
| - collection_->GetName(String::cast(shared->name())),
|
| - children_count,
|
| - retainers_count);
|
| - } else if (object->IsScript()) {
|
| - Script* script = Script::cast(object);
|
| - return AddEntry(object,
|
| - HeapEntry::kCode,
|
| - script->name()->IsString() ?
|
| - collection_->GetName(String::cast(script->name())) : "",
|
| - children_count,
|
| - retainers_count);
|
| - } else if (object->IsFixedArray()) {
|
| - return AddEntry(object,
|
| - HeapEntry::kArray,
|
| - "",
|
| - children_count,
|
| - retainers_count);
|
| - } else if (object->IsHeapNumber()) {
|
| - return AddEntry(object,
|
| - HeapEntry::kHeapNumber,
|
| - "number",
|
| - children_count,
|
| - retainers_count);
|
| - }
|
| - return AddEntry(object,
|
| - HeapEntry::kHidden,
|
| - "system",
|
| - children_count,
|
| - retainers_count);
|
| -}
|
| -
|
| -
|
| static void HeapEntryClearPaint(HeapEntry** entry_ptr) {
|
| (*entry_ptr)->clear_paint();
|
| }
|
| @@ -1356,20 +1278,42 @@
|
| }
|
|
|
|
|
| -HeapEntry* HeapSnapshot::AddEntry(HeapObject* object,
|
| - HeapEntry::Type type,
|
| - const char* name,
|
| - int children_count,
|
| - int retainers_count) {
|
| - return AddEntry(type,
|
| - name,
|
| - collection_->GetObjectId(object->address()),
|
| - object->Size(),
|
| - children_count,
|
| - retainers_count);
|
| +HeapEntry* HeapSnapshot::AddRootEntry(int children_count) {
|
| + ASSERT(root_entry_ == NULL);
|
| + return (root_entry_ = AddEntry(HeapEntry::kObject,
|
| + "",
|
| + HeapObjectsMap::kInternalRootObjectId,
|
| + 0,
|
| + children_count,
|
| + 0));
|
| }
|
|
|
|
|
| +HeapEntry* HeapSnapshot::AddGcRootsEntry(int children_count,
|
| + int retainers_count) {
|
| + ASSERT(gc_roots_entry_ == NULL);
|
| + return (gc_roots_entry_ = AddEntry(HeapEntry::kObject,
|
| + "(GC roots)",
|
| + HeapObjectsMap::kGcRootsObjectId,
|
| + 0,
|
| + children_count,
|
| + retainers_count));
|
| +}
|
| +
|
| +
|
| +HeapEntry* HeapSnapshot::AddNativesRootEntry(int children_count,
|
| + int retainers_count) {
|
| + ASSERT(natives_root_entry_ == NULL);
|
| + return (natives_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,
|
| @@ -1463,10 +1407,13 @@
|
| }
|
|
|
|
|
| -const uint64_t HeapObjectsMap::kInternalRootObjectId = 0;
|
| -const uint64_t HeapObjectsMap::kGcRootsObjectId = 1;
|
| +// 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 = 2;
|
| +const uint64_t HeapObjectsMap::kFirstAvailableObjectId = 7;
|
|
|
| HeapObjectsMap::HeapObjectsMap()
|
| : initial_fill_mode_(true),
|
| @@ -1491,7 +1438,8 @@
|
| 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;
|
| }
|
| @@ -1559,6 +1507,17 @@
|
| }
|
|
|
|
|
| +uint64_t HeapObjectsMap::GenerateId(v8::RetainedObjectInfo* info) {
|
| + uint64_t id = static_cast<uint64_t>(info->GetHash());
|
| + const char* label = info->GetLabel();
|
| + id ^= HashSequentialString(label, static_cast<int>(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),
|
| @@ -1619,7 +1578,7 @@
|
| reinterpret_cast<HeapEntry*>(1);
|
|
|
| HeapEntriesMap::HeapEntriesMap()
|
| - : entries_(HeapObjectsMatch),
|
| + : entries_(HeapThingsMatch),
|
| entries_count_(0),
|
| total_children_count_(0),
|
| total_retainers_count_(0) {
|
| @@ -1633,8 +1592,25 @@
|
| }
|
|
|
|
|
| -HeapEntry* HeapEntriesMap::Map(HeapObject* object) {
|
| - HashMap::Entry* cache_entry = entries_.Lookup(object, Hash(object), false);
|
| +void HeapEntriesMap::AllocateEntries() {
|
| + for (HashMap::Entry* p = entries_.Start();
|
| + p != NULL;
|
| + p = entries_.Next(p)) {
|
| + EntryInfo* entry_info = reinterpret_cast<EntryInfo*>(p->value);
|
| + entry_info->entry = entry_info->allocator->AllocateEntry(
|
| + 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;
|
| + }
|
| +}
|
| +
|
| +
|
| +HeapEntry* HeapEntriesMap::Map(HeapThing thing) {
|
| + HashMap::Entry* cache_entry = entries_.Lookup(thing, Hash(thing), false);
|
| if (cache_entry != NULL) {
|
| EntryInfo* entry_info = reinterpret_cast<EntryInfo*>(cache_entry->value);
|
| return entry_info->entry;
|
| @@ -1644,15 +1620,16 @@
|
| }
|
|
|
|
|
| -void HeapEntriesMap::Pair(HeapObject* object, HeapEntry* entry) {
|
| - HashMap::Entry* cache_entry = entries_.Lookup(object, Hash(object), true);
|
| +void HeapEntriesMap::Pair(
|
| + HeapThing thing, HeapEntriesAllocator* allocator, HeapEntry* entry) {
|
| + HashMap::Entry* cache_entry = entries_.Lookup(thing, Hash(thing), true);
|
| ASSERT(cache_entry->value == NULL);
|
| - cache_entry->value = new EntryInfo(entry);
|
| + cache_entry->value = new EntryInfo(entry, allocator);
|
| ++entries_count_;
|
| }
|
|
|
|
|
| -void HeapEntriesMap::CountReference(HeapObject* from, HeapObject* to,
|
| +void HeapEntriesMap::CountReference(HeapThing from, HeapThing to,
|
| int* prev_children_count,
|
| int* prev_retainers_count) {
|
| HashMap::Entry* from_cache_entry = entries_.Lookup(from, Hash(from), false);
|
| @@ -1675,7 +1652,7 @@
|
|
|
|
|
| HeapObjectsSet::HeapObjectsSet()
|
| - : entries_(HeapEntriesMap::HeapObjectsMatch) {
|
| + : entries_(HeapEntriesMap::HeapThingsMatch) {
|
| }
|
|
|
|
|
| @@ -1704,206 +1681,149 @@
|
| }
|
|
|
|
|
| -HeapSnapshotGenerator::HeapSnapshotGenerator(HeapSnapshot* snapshot,
|
| - v8::ActivityControl* control)
|
| +HeapObject *const V8HeapExplorer::kInternalRootObject =
|
| + reinterpret_cast<HeapObject*>(
|
| + static_cast<intptr_t>(HeapObjectsMap::kInternalRootObjectId));
|
| +HeapObject *const V8HeapExplorer::kGcRootsObject =
|
| + reinterpret_cast<HeapObject*>(
|
| + static_cast<intptr_t>(HeapObjectsMap::kGcRootsObjectId));
|
| +
|
| +
|
| +V8HeapExplorer::V8HeapExplorer(
|
| + HeapSnapshot* snapshot,
|
| + SnapshottingProgressReportingInterface* progress)
|
| : snapshot_(snapshot),
|
| - control_(control),
|
| - collection_(snapshot->collection()),
|
| + collection_(snapshot_->collection()),
|
| + progress_(progress),
|
| filler_(NULL) {
|
| }
|
|
|
| -class SnapshotCounter : public HeapSnapshotGenerator::SnapshotFillerInterface {
|
| - public:
|
| - explicit SnapshotCounter(HeapEntriesMap* entries)
|
| - : entries_(entries) { }
|
| - HeapEntry* AddEntry(HeapObject* obj) {
|
| - entries_->Pair(obj, HeapEntriesMap::kHeapEntryPlaceholder);
|
| - return HeapEntriesMap::kHeapEntryPlaceholder;
|
| - }
|
| - void SetIndexedReference(HeapGraphEdge::Type,
|
| - HeapObject* parent_obj,
|
| - HeapEntry*,
|
| - int,
|
| - Object* child_obj,
|
| - HeapEntry*) {
|
| - entries_->CountReference(parent_obj, HeapObject::cast(child_obj));
|
| - }
|
| - void SetNamedReference(HeapGraphEdge::Type,
|
| - HeapObject* parent_obj,
|
| - HeapEntry*,
|
| - const char*,
|
| - Object* child_obj,
|
| - HeapEntry*) {
|
| - entries_->CountReference(parent_obj, HeapObject::cast(child_obj));
|
| - }
|
| - void SetRootShortcutReference(Object* child_obj, HeapEntry*) {
|
| - entries_->CountReference(
|
| - HeapSnapshot::kInternalRootObject, HeapObject::cast(child_obj));
|
| - }
|
| - void SetRootGcRootsReference() {
|
| - entries_->CountReference(
|
| - HeapSnapshot::kInternalRootObject, HeapSnapshot::kGcRootsObject);
|
| - }
|
| - void SetStrongRootReference(Object* child_obj, HeapEntry*) {
|
| - entries_->CountReference(
|
| - HeapSnapshot::kGcRootsObject, HeapObject::cast(child_obj));
|
| - }
|
| - private:
|
| - HeapEntriesMap* entries_;
|
| -};
|
|
|
| +V8HeapExplorer::~V8HeapExplorer() {
|
| +}
|
|
|
| -class SnapshotFiller : public HeapSnapshotGenerator::SnapshotFillerInterface {
|
| - public:
|
| - explicit SnapshotFiller(HeapSnapshot* snapshot, HeapEntriesMap* entries)
|
| - : snapshot_(snapshot),
|
| - collection_(snapshot->collection()),
|
| - entries_(entries) { }
|
| - HeapEntry* AddEntry(HeapObject* obj) {
|
| - UNREACHABLE();
|
| - return NULL;
|
| - }
|
| - void SetIndexedReference(HeapGraphEdge::Type type,
|
| - HeapObject* parent_obj,
|
| - HeapEntry* parent_entry,
|
| - int index,
|
| - Object* child_obj,
|
| - HeapEntry* child_entry) {
|
| - int child_index, retainer_index;
|
| - entries_->CountReference(parent_obj,
|
| - HeapObject::cast(child_obj),
|
| - &child_index,
|
| - &retainer_index);
|
| - parent_entry->SetIndexedReference(
|
| - type, child_index, index, child_entry, retainer_index);
|
| - }
|
| - void SetNamedReference(HeapGraphEdge::Type type,
|
| - HeapObject* parent_obj,
|
| - HeapEntry* parent_entry,
|
| - const char* reference_name,
|
| - Object* child_obj,
|
| - HeapEntry* child_entry) {
|
| - int child_index, retainer_index;
|
| - entries_->CountReference(parent_obj, HeapObject::cast(child_obj),
|
| - &child_index, &retainer_index);
|
| - parent_entry->SetNamedReference(type,
|
| - child_index,
|
| - reference_name,
|
| - child_entry,
|
| - retainer_index);
|
| - }
|
| - void SetRootGcRootsReference() {
|
| - int child_index, retainer_index;
|
| - entries_->CountReference(HeapSnapshot::kInternalRootObject,
|
| - HeapSnapshot::kGcRootsObject,
|
| - &child_index,
|
| - &retainer_index);
|
| - snapshot_->root()->SetIndexedReference(HeapGraphEdge::kElement,
|
| - child_index,
|
| - child_index + 1,
|
| - snapshot_->gc_roots(),
|
| - retainer_index);
|
| - }
|
| - void SetRootShortcutReference(Object* child_obj,
|
| - HeapEntry* child_entry) {
|
| - int child_index, retainer_index;
|
| - entries_->CountReference(HeapSnapshot::kInternalRootObject,
|
| - HeapObject::cast(child_obj),
|
| - &child_index,
|
| - &retainer_index);
|
| - snapshot_->root()->SetNamedReference(HeapGraphEdge::kShortcut,
|
| - child_index,
|
| - collection_->GetName(child_index + 1),
|
| - child_entry,
|
| - retainer_index);
|
| - }
|
| - void SetStrongRootReference(Object* child_obj,
|
| - HeapEntry* child_entry) {
|
| - int child_index, retainer_index;
|
| - entries_->CountReference(HeapSnapshot::kGcRootsObject,
|
| - HeapObject::cast(child_obj),
|
| - &child_index,
|
| - &retainer_index);
|
| - snapshot_->gc_roots()->SetIndexedReference(HeapGraphEdge::kElement,
|
| - child_index,
|
| - child_index + 1,
|
| - child_entry,
|
| - retainer_index);
|
| - }
|
| - private:
|
| - HeapSnapshot* snapshot_;
|
| - HeapSnapshotsCollection* collection_;
|
| - HeapEntriesMap* entries_;
|
| -};
|
|
|
| -class SnapshotAllocator {
|
| - public:
|
| - explicit SnapshotAllocator(HeapSnapshot* snapshot)
|
| - : snapshot_(snapshot) { }
|
| - HeapEntry* GetEntry(
|
| - HeapObject* obj, int children_count, int retainers_count) {
|
| - HeapEntry* entry =
|
| - snapshot_->AddEntry(obj, children_count, retainers_count);
|
| - ASSERT(entry != NULL);
|
| - return entry;
|
| - }
|
| - private:
|
| - HeapSnapshot* snapshot_;
|
| -};
|
| +HeapEntry* V8HeapExplorer::AllocateEntry(
|
| + HeapThing ptr, int children_count, int retainers_count) {
|
| + return AddEntry(
|
| + reinterpret_cast<HeapObject*>(ptr), children_count, retainers_count);
|
| +}
|
|
|
| -class RootsReferencesExtractor : public ObjectVisitor {
|
| - public:
|
| - explicit RootsReferencesExtractor(HeapSnapshotGenerator* generator)
|
| - : generator_(generator) {
|
| +
|
| +HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object,
|
| + int children_count,
|
| + int retainers_count) {
|
| + if (object == kInternalRootObject) {
|
| + ASSERT(retainers_count == 0);
|
| + return snapshot_->AddRootEntry(children_count);
|
| + } else if (object == kGcRootsObject) {
|
| + return snapshot_->AddGcRootsEntry(children_count, retainers_count);
|
| + } else if (object->IsJSFunction()) {
|
| + JSFunction* func = JSFunction::cast(object);
|
| + SharedFunctionInfo* shared = func->shared();
|
| + return AddEntry(object,
|
| + HeapEntry::kClosure,
|
| + collection_->names()->GetName(String::cast(shared->name())),
|
| + children_count,
|
| + retainers_count);
|
| + } else if (object->IsJSRegExp()) {
|
| + JSRegExp* re = JSRegExp::cast(object);
|
| + return AddEntry(object,
|
| + HeapEntry::kRegExp,
|
| + collection_->names()->GetName(re->Pattern()),
|
| + children_count,
|
| + retainers_count);
|
| + } else if (object->IsJSObject()) {
|
| + return AddEntry(object,
|
| + HeapEntry::kObject,
|
| + collection_->names()->GetName(
|
| + GetConstructorNameForHeapProfile(
|
| + JSObject::cast(object))),
|
| + children_count,
|
| + retainers_count);
|
| + } else if (object->IsString()) {
|
| + return AddEntry(object,
|
| + HeapEntry::kString,
|
| + collection_->names()->GetName(String::cast(object)),
|
| + children_count,
|
| + retainers_count);
|
| + } else if (object->IsCode()) {
|
| + return AddEntry(object,
|
| + HeapEntry::kCode,
|
| + "",
|
| + children_count,
|
| + retainers_count);
|
| + } else if (object->IsSharedFunctionInfo()) {
|
| + SharedFunctionInfo* shared = SharedFunctionInfo::cast(object);
|
| + return AddEntry(object,
|
| + HeapEntry::kCode,
|
| + collection_->names()->GetName(String::cast(shared->name())),
|
| + children_count,
|
| + retainers_count);
|
| + } else if (object->IsScript()) {
|
| + Script* script = Script::cast(object);
|
| + return AddEntry(object,
|
| + HeapEntry::kCode,
|
| + script->name()->IsString() ?
|
| + collection_->names()->GetName(
|
| + String::cast(script->name()))
|
| + : "",
|
| + children_count,
|
| + retainers_count);
|
| + } else if (object->IsFixedArray()) {
|
| + return AddEntry(object,
|
| + HeapEntry::kArray,
|
| + "",
|
| + children_count,
|
| + retainers_count);
|
| + } else if (object->IsHeapNumber()) {
|
| + return AddEntry(object,
|
| + HeapEntry::kHeapNumber,
|
| + "number",
|
| + children_count,
|
| + retainers_count);
|
| }
|
| - void VisitPointers(Object** start, Object** end) {
|
| - for (Object** p = start; p < end; p++) generator_->SetGcRootsReference(*p);
|
| - }
|
| - private:
|
| - HeapSnapshotGenerator* generator_;
|
| -};
|
| + return AddEntry(object,
|
| + HeapEntry::kHidden,
|
| + "system",
|
| + children_count,
|
| + retainers_count);
|
| +}
|
|
|
|
|
| -bool HeapSnapshotGenerator::GenerateSnapshot() {
|
| - AssertNoAllocation no_alloc;
|
| +HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object,
|
| + HeapEntry::Type type,
|
| + const char* name,
|
| + int children_count,
|
| + int retainers_count) {
|
| + return snapshot_->AddEntry(type,
|
| + name,
|
| + collection_->GetObjectId(object->address()),
|
| + object->Size(),
|
| + children_count,
|
| + retainers_count);
|
| +}
|
|
|
| - SetProgressTotal(4); // 2 passes + dominators + sizes.
|
|
|
| - // Pass 1. Iterate heap contents to count entries and references.
|
| - if (!CountEntriesAndReferences()) return false;
|
| -
|
| - // Allocate and fill entries in the snapshot, allocate references.
|
| - snapshot_->AllocateEntries(entries_.entries_count(),
|
| - entries_.total_children_count(),
|
| - entries_.total_retainers_count());
|
| - SnapshotAllocator allocator(snapshot_);
|
| - entries_.UpdateEntries(&allocator);
|
| -
|
| - // Pass 2. Fill references.
|
| - if (!FillReferences()) return false;
|
| -
|
| - if (!SetEntriesDominators()) return false;
|
| - if (!ApproximateRetainedSizes()) return false;
|
| -
|
| - progress_counter_ = progress_total_;
|
| - if (!ReportProgress(true)) return false;
|
| - return true;
|
| +void V8HeapExplorer::AddRootEntries(SnapshotFillerInterface* filler) {
|
| + filler->AddEntry(kInternalRootObject, this);
|
| + filler->AddEntry(kGcRootsObject, this);
|
| }
|
|
|
|
|
| -HeapEntry* HeapSnapshotGenerator::GetEntry(Object* obj) {
|
| - if (!obj->IsHeapObject()) return NULL;
|
| - HeapObject* object = HeapObject::cast(obj);
|
| - HeapEntry* entry = entries_.Map(object);
|
| - // A new entry.
|
| - if (entry == NULL) entry = filler_->AddEntry(object);
|
| - return entry;
|
| +int V8HeapExplorer::EstimateObjectsCount() {
|
| + HeapIterator iterator(HeapIterator::kFilterUnreachable);
|
| + int objects_count = 0;
|
| + for (HeapObject* obj = iterator.next();
|
| + obj != NULL;
|
| + obj = iterator.next(), ++objects_count) {}
|
| + return objects_count;
|
| }
|
|
|
|
|
| class IndexedReferencesExtractor : public ObjectVisitor {
|
| public:
|
| - IndexedReferencesExtractor(HeapSnapshotGenerator* generator,
|
| + IndexedReferencesExtractor(V8HeapExplorer* generator,
|
| HeapObject* parent_obj,
|
| HeapEntry* parent_entry,
|
| HeapObjectsSet* known_references = NULL)
|
| @@ -1921,7 +1841,7 @@
|
| }
|
| }
|
| private:
|
| - HeapSnapshotGenerator* generator_;
|
| + V8HeapExplorer* generator_;
|
| HeapObject* parent_obj_;
|
| HeapEntry* parent_;
|
| HeapObjectsSet* known_references_;
|
| @@ -1929,7 +1849,7 @@
|
| };
|
|
|
|
|
| -void HeapSnapshotGenerator::ExtractReferences(HeapObject* obj) {
|
| +void V8HeapExplorer::ExtractReferences(HeapObject* obj) {
|
| HeapEntry* entry = GetEntry(obj);
|
| if (entry == NULL) return; // No interest in this object.
|
|
|
| @@ -1973,8 +1893,8 @@
|
| }
|
|
|
|
|
| -void HeapSnapshotGenerator::ExtractClosureReferences(JSObject* js_obj,
|
| - HeapEntry* entry) {
|
| +void V8HeapExplorer::ExtractClosureReferences(JSObject* js_obj,
|
| + HeapEntry* entry) {
|
| if (js_obj->IsJSFunction()) {
|
| HandleScope hs;
|
| JSFunction* func = JSFunction::cast(js_obj);
|
| @@ -1996,8 +1916,8 @@
|
| }
|
|
|
|
|
| -void HeapSnapshotGenerator::ExtractPropertyReferences(JSObject* js_obj,
|
| - HeapEntry* entry) {
|
| +void V8HeapExplorer::ExtractPropertyReferences(JSObject* js_obj,
|
| + HeapEntry* entry) {
|
| if (js_obj->HasFastProperties()) {
|
| DescriptorArray* descs = js_obj->map()->instance_descriptors();
|
| for (int i = 0; i < descs->number_of_descriptors(); i++) {
|
| @@ -2038,8 +1958,8 @@
|
| }
|
|
|
|
|
| -void HeapSnapshotGenerator::ExtractElementReferences(JSObject* js_obj,
|
| - HeapEntry* entry) {
|
| +void V8HeapExplorer::ExtractElementReferences(JSObject* js_obj,
|
| + HeapEntry* entry) {
|
| if (js_obj->HasFastElements()) {
|
| FixedArray* elements = FixedArray::cast(js_obj->elements());
|
| int length = js_obj->IsJSArray() ?
|
| @@ -2065,8 +1985,8 @@
|
| }
|
|
|
|
|
| -void HeapSnapshotGenerator::ExtractInternalReferences(JSObject* js_obj,
|
| - HeapEntry* entry) {
|
| +void V8HeapExplorer::ExtractInternalReferences(JSObject* js_obj,
|
| + HeapEntry* entry) {
|
| int length = js_obj->GetInternalFieldCount();
|
| for (int i = 0; i < length; ++i) {
|
| Object* o = js_obj->GetInternalField(i);
|
| @@ -2075,16 +1995,61 @@
|
| }
|
|
|
|
|
| -void HeapSnapshotGenerator::SetClosureReference(HeapObject* parent_obj,
|
| - HeapEntry* parent_entry,
|
| - String* reference_name,
|
| - Object* child_obj) {
|
| +HeapEntry* V8HeapExplorer::GetEntry(Object* obj) {
|
| + if (!obj->IsHeapObject()) return NULL;
|
| + return filler_->FindOrAddEntry(obj, this);
|
| +}
|
| +
|
| +
|
| +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_;
|
| +};
|
| +
|
| +
|
| +bool V8HeapExplorer::IterateAndExtractReferences(
|
| + SnapshotFillerInterface* filler) {
|
| + filler_ = filler;
|
| + HeapIterator iterator(HeapIterator::kFilterUnreachable);
|
| + bool interrupted = false;
|
| + // Heap iteration with filtering must be finished in any case.
|
| + for (HeapObject* obj = iterator.next();
|
| + obj != NULL;
|
| + obj = iterator.next(), progress_->ProgressStep()) {
|
| + if (!interrupted) {
|
| + ExtractReferences(obj);
|
| + if (!progress_->ProgressReport(false)) interrupted = true;
|
| + }
|
| + }
|
| + if (interrupted) {
|
| + filler_ = NULL;
|
| + return false;
|
| + }
|
| + SetRootGcRootsReference();
|
| + RootsReferencesExtractor extractor(this);
|
| + Heap::IterateRoots(&extractor, VISIT_ALL);
|
| + filler_ = NULL;
|
| + return progress_->ProgressReport(false);
|
| +}
|
| +
|
| +
|
| +void V8HeapExplorer::SetClosureReference(HeapObject* parent_obj,
|
| + HeapEntry* parent_entry,
|
| + String* reference_name,
|
| + Object* child_obj) {
|
| HeapEntry* child_entry = GetEntry(child_obj);
|
| if (child_entry != NULL) {
|
| filler_->SetNamedReference(HeapGraphEdge::kContextVariable,
|
| parent_obj,
|
| parent_entry,
|
| - collection_->GetName(reference_name),
|
| + collection_->names()->GetName(reference_name),
|
| child_obj,
|
| child_entry);
|
| known_references_.Insert(child_obj);
|
| @@ -2092,10 +2057,10 @@
|
| }
|
|
|
|
|
| -void HeapSnapshotGenerator::SetElementReference(HeapObject* parent_obj,
|
| - HeapEntry* parent_entry,
|
| - int index,
|
| - Object* child_obj) {
|
| +void V8HeapExplorer::SetElementReference(HeapObject* parent_obj,
|
| + HeapEntry* parent_entry,
|
| + int index,
|
| + Object* child_obj) {
|
| HeapEntry* child_entry = GetEntry(child_obj);
|
| if (child_entry != NULL) {
|
| filler_->SetIndexedReference(HeapGraphEdge::kElement,
|
| @@ -2109,10 +2074,10 @@
|
| }
|
|
|
|
|
| -void HeapSnapshotGenerator::SetInternalReference(HeapObject* parent_obj,
|
| - HeapEntry* parent_entry,
|
| - const char* reference_name,
|
| - Object* child_obj) {
|
| +void V8HeapExplorer::SetInternalReference(HeapObject* parent_obj,
|
| + HeapEntry* parent_entry,
|
| + const char* reference_name,
|
| + Object* child_obj) {
|
| HeapEntry* child_entry = GetEntry(child_obj);
|
| if (child_entry != NULL) {
|
| filler_->SetNamedReference(HeapGraphEdge::kInternal,
|
| @@ -2126,16 +2091,16 @@
|
| }
|
|
|
|
|
| -void HeapSnapshotGenerator::SetInternalReference(HeapObject* parent_obj,
|
| - HeapEntry* parent_entry,
|
| - int index,
|
| - Object* child_obj) {
|
| +void V8HeapExplorer::SetInternalReference(HeapObject* parent_obj,
|
| + HeapEntry* parent_entry,
|
| + int index,
|
| + Object* child_obj) {
|
| HeapEntry* child_entry = GetEntry(child_obj);
|
| if (child_entry != NULL) {
|
| filler_->SetNamedReference(HeapGraphEdge::kInternal,
|
| parent_obj,
|
| parent_entry,
|
| - collection_->GetName(index),
|
| + collection_->names()->GetName(index),
|
| child_obj,
|
| child_entry);
|
| known_references_.Insert(child_obj);
|
| @@ -2143,10 +2108,10 @@
|
| }
|
|
|
|
|
| -void HeapSnapshotGenerator::SetHiddenReference(HeapObject* parent_obj,
|
| - HeapEntry* parent_entry,
|
| - int index,
|
| - Object* child_obj) {
|
| +void V8HeapExplorer::SetHiddenReference(HeapObject* parent_obj,
|
| + HeapEntry* parent_entry,
|
| + int index,
|
| + Object* child_obj) {
|
| HeapEntry* child_entry = GetEntry(child_obj);
|
| if (child_entry != NULL) {
|
| filler_->SetIndexedReference(HeapGraphEdge::kHidden,
|
| @@ -2159,10 +2124,10 @@
|
| }
|
|
|
|
|
| -void HeapSnapshotGenerator::SetPropertyReference(HeapObject* parent_obj,
|
| - HeapEntry* parent_entry,
|
| - String* reference_name,
|
| - Object* child_obj) {
|
| +void V8HeapExplorer::SetPropertyReference(HeapObject* parent_obj,
|
| + HeapEntry* parent_entry,
|
| + String* reference_name,
|
| + Object* child_obj) {
|
| HeapEntry* child_entry = GetEntry(child_obj);
|
| if (child_entry != NULL) {
|
| HeapGraphEdge::Type type = reference_name->length() > 0 ?
|
| @@ -2170,7 +2135,7 @@
|
| filler_->SetNamedReference(type,
|
| parent_obj,
|
| parent_entry,
|
| - collection_->GetName(reference_name),
|
| + collection_->names()->GetName(reference_name),
|
| child_obj,
|
| child_entry);
|
| known_references_.Insert(child_obj);
|
| @@ -2178,7 +2143,7 @@
|
| }
|
|
|
|
|
| -void HeapSnapshotGenerator::SetPropertyShortcutReference(
|
| +void V8HeapExplorer::SetPropertyShortcutReference(
|
| HeapObject* parent_obj,
|
| HeapEntry* parent_entry,
|
| String* reference_name,
|
| @@ -2188,59 +2153,427 @@
|
| filler_->SetNamedReference(HeapGraphEdge::kShortcut,
|
| parent_obj,
|
| parent_entry,
|
| - collection_->GetName(reference_name),
|
| + collection_->names()->GetName(reference_name),
|
| child_obj,
|
| child_entry);
|
| }
|
| }
|
|
|
|
|
| -void HeapSnapshotGenerator::SetRootGcRootsReference() {
|
| - filler_->SetRootGcRootsReference();
|
| +void V8HeapExplorer::SetRootGcRootsReference() {
|
| + filler_->SetIndexedAutoIndexReference(
|
| + HeapGraphEdge::kElement,
|
| + kInternalRootObject, snapshot_->root(),
|
| + kGcRootsObject, snapshot_->gc_roots());
|
| }
|
|
|
|
|
| -void HeapSnapshotGenerator::SetRootShortcutReference(Object* child_obj) {
|
| +void V8HeapExplorer::SetRootShortcutReference(Object* child_obj) {
|
| HeapEntry* child_entry = GetEntry(child_obj);
|
| ASSERT(child_entry != NULL);
|
| - filler_->SetRootShortcutReference(child_obj, child_entry);
|
| + filler_->SetNamedAutoIndexReference(
|
| + HeapGraphEdge::kShortcut,
|
| + kInternalRootObject, snapshot_->root(),
|
| + child_obj, child_entry);
|
| }
|
|
|
|
|
| -void HeapSnapshotGenerator::SetGcRootsReference(Object* child_obj) {
|
| +void V8HeapExplorer::SetGcRootsReference(Object* child_obj) {
|
| HeapEntry* child_entry = GetEntry(child_obj);
|
| if (child_entry != NULL) {
|
| - filler_->SetStrongRootReference(child_obj, child_entry);
|
| + filler_->SetIndexedAutoIndexReference(
|
| + HeapGraphEdge::kElement,
|
| + kGcRootsObject, snapshot_->gc_roots(),
|
| + child_obj, child_entry);
|
| }
|
| }
|
|
|
|
|
| +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>(
|
| + static_cast<intptr_t>(HeapObjectsMap::kNativesRootObjectId));
|
| +
|
| +
|
| +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_->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);
|
| + }
|
| +}
|
| +
|
| +
|
| +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::CallGlobalGCPrologueCallback();
|
| + 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::CallGlobalGCEpilogueCallback();
|
| + // 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_->natives_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_->natives_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),
|
| + dom_explorer_(snapshot_, this) {
|
| +}
|
| +
|
| +
|
| +class SnapshotCounter : public SnapshotFillerInterface {
|
| + public:
|
| + explicit SnapshotCounter(HeapEntriesMap* entries) : entries_(entries) { }
|
| + HeapEntry* AddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
|
| + entries_->Pair(ptr, allocator, HeapEntriesMap::kHeapEntryPlaceholder);
|
| + return HeapEntriesMap::kHeapEntryPlaceholder;
|
| + }
|
| + 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,
|
| + HeapEntry*,
|
| + int,
|
| + HeapThing child_ptr,
|
| + HeapEntry*) {
|
| + entries_->CountReference(parent_ptr, child_ptr);
|
| + }
|
| + void SetIndexedAutoIndexReference(HeapGraphEdge::Type,
|
| + HeapThing parent_ptr,
|
| + HeapEntry*,
|
| + HeapThing child_ptr,
|
| + HeapEntry*) {
|
| + entries_->CountReference(parent_ptr, child_ptr);
|
| + }
|
| + void SetNamedReference(HeapGraphEdge::Type,
|
| + HeapThing parent_ptr,
|
| + HeapEntry*,
|
| + const char*,
|
| + HeapThing child_ptr,
|
| + HeapEntry*) {
|
| + entries_->CountReference(parent_ptr, child_ptr);
|
| + }
|
| + void SetNamedAutoIndexReference(HeapGraphEdge::Type,
|
| + HeapThing parent_ptr,
|
| + HeapEntry*,
|
| + HeapThing child_ptr,
|
| + HeapEntry*) {
|
| + entries_->CountReference(parent_ptr, child_ptr);
|
| + }
|
| + private:
|
| + HeapEntriesMap* entries_;
|
| +};
|
| +
|
| +
|
| +class SnapshotFiller : public SnapshotFillerInterface {
|
| + public:
|
| + explicit SnapshotFiller(HeapSnapshot* snapshot, HeapEntriesMap* entries)
|
| + : snapshot_(snapshot),
|
| + collection_(snapshot->collection()),
|
| + entries_(entries) { }
|
| + HeapEntry* AddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
|
| + UNREACHABLE();
|
| + return NULL;
|
| + }
|
| + 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,
|
| + HeapEntry* parent_entry,
|
| + int index,
|
| + HeapThing child_ptr,
|
| + HeapEntry* child_entry) {
|
| + int child_index, retainer_index;
|
| + entries_->CountReference(
|
| + parent_ptr, child_ptr, &child_index, &retainer_index);
|
| + parent_entry->SetIndexedReference(
|
| + type, child_index, index, child_entry, retainer_index);
|
| + }
|
| + void SetIndexedAutoIndexReference(HeapGraphEdge::Type type,
|
| + HeapThing parent_ptr,
|
| + HeapEntry* parent_entry,
|
| + HeapThing child_ptr,
|
| + HeapEntry* child_entry) {
|
| + int child_index, retainer_index;
|
| + entries_->CountReference(
|
| + parent_ptr, child_ptr, &child_index, &retainer_index);
|
| + parent_entry->SetIndexedReference(
|
| + type, child_index, child_index + 1, child_entry, retainer_index);
|
| + }
|
| + void SetNamedReference(HeapGraphEdge::Type type,
|
| + HeapThing parent_ptr,
|
| + HeapEntry* parent_entry,
|
| + const char* reference_name,
|
| + HeapThing child_ptr,
|
| + HeapEntry* child_entry) {
|
| + int child_index, retainer_index;
|
| + entries_->CountReference(
|
| + parent_ptr, child_ptr, &child_index, &retainer_index);
|
| + parent_entry->SetNamedReference(
|
| + type, child_index, reference_name, child_entry, retainer_index);
|
| + }
|
| + void SetNamedAutoIndexReference(HeapGraphEdge::Type type,
|
| + HeapThing parent_ptr,
|
| + HeapEntry* parent_entry,
|
| + HeapThing child_ptr,
|
| + HeapEntry* child_entry) {
|
| + int child_index, retainer_index;
|
| + entries_->CountReference(
|
| + parent_ptr, child_ptr, &child_index, &retainer_index);
|
| + parent_entry->SetNamedReference(type,
|
| + child_index,
|
| + collection_->names()->GetName(child_index + 1),
|
| + child_entry,
|
| + retainer_index);
|
| + }
|
| + private:
|
| + HeapSnapshot* snapshot_;
|
| + HeapSnapshotsCollection* collection_;
|
| + HeapEntriesMap* entries_;
|
| +};
|
| +
|
| +
|
| +bool HeapSnapshotGenerator::GenerateSnapshot() {
|
| + AssertNoAllocation no_alloc;
|
| +
|
| + SetProgressTotal(4); // 2 passes + dominators + sizes.
|
| +
|
| + // Pass 1. Iterate heap contents to count entries and references.
|
| + if (!CountEntriesAndReferences()) return false;
|
| +
|
| + // Allocate and fill entries in the snapshot, allocate references.
|
| + snapshot_->AllocateEntries(entries_.entries_count(),
|
| + entries_.total_children_count(),
|
| + entries_.total_retainers_count());
|
| + entries_.AllocateEntries();
|
| +
|
| + // Pass 2. Fill references.
|
| + if (!FillReferences()) return false;
|
| +
|
| + if (!SetEntriesDominators()) return false;
|
| + if (!ApproximateRetainedSizes()) return false;
|
| +
|
| + progress_counter_ = progress_total_;
|
| + if (!ProgressReport(true)) return false;
|
| + return true;
|
| +}
|
| +
|
| +
|
| +void HeapSnapshotGenerator::ProgressStep() {
|
| + ++progress_counter_;
|
| +}
|
| +
|
| +
|
| +bool HeapSnapshotGenerator::ProgressReport(bool force) {
|
| + const int kProgressReportGranularity = 10000;
|
| + if (control_ != NULL
|
| + && (force || progress_counter_ % kProgressReportGranularity == 0)) {
|
| + return
|
| + control_->ReportProgressValue(progress_counter_, progress_total_) ==
|
| + v8::ActivityControl::kContinue;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +
|
| void HeapSnapshotGenerator::SetProgressTotal(int iterations_count) {
|
| if (control_ == NULL) return;
|
| -
|
| - HeapIterator iterator(HeapIterator::kFilterUnreachable);
|
| - int objects_count = 0;
|
| - for (HeapObject* obj = iterator.next();
|
| - obj != NULL;
|
| - obj = iterator.next(), ++objects_count) {}
|
| - progress_total_ = objects_count * iterations_count;
|
| + progress_total_ = (
|
| + v8_heap_explorer_.EstimateObjectsCount() +
|
| + dom_explorer_.EstimateObjectsCount()) * iterations_count;
|
| progress_counter_ = 0;
|
| }
|
|
|
|
|
| bool HeapSnapshotGenerator::CountEntriesAndReferences() {
|
| SnapshotCounter counter(&entries_);
|
| - filler_ = &counter;
|
| - filler_->AddEntry(HeapSnapshot::kInternalRootObject);
|
| - filler_->AddEntry(HeapSnapshot::kGcRootsObject);
|
| - return IterateAndExtractReferences();
|
| + v8_heap_explorer_.AddRootEntries(&counter);
|
| + dom_explorer_.AddRootEntries(&counter);
|
| + return
|
| + v8_heap_explorer_.IterateAndExtractReferences(&counter) &&
|
| + dom_explorer_.IterateAndExtractReferences(&counter);
|
| }
|
|
|
|
|
| bool HeapSnapshotGenerator::FillReferences() {
|
| SnapshotFiller filler(snapshot_, &entries_);
|
| - filler_ = &filler;
|
| - return IterateAndExtractReferences();
|
| + return
|
| + v8_heap_explorer_.IterateAndExtractReferences(&filler) &&
|
| + dom_explorer_.IterateAndExtractReferences(&filler);
|
| }
|
|
|
|
|
| @@ -2326,7 +2659,7 @@
|
| int remaining = entries_length - changed;
|
| if (remaining < 0) remaining = 0;
|
| progress_counter_ = base_progress_counter + remaining;
|
| - if (!ReportProgress(true)) return false;
|
| + if (!ProgressReport(true)) return false;
|
| }
|
| return true;
|
| }
|
| @@ -2356,7 +2689,7 @@
|
| }
|
| for (int i = 0;
|
| i < snapshot_->entries()->length();
|
| - ++i, IncProgressCounter()) {
|
| + ++i, ProgressStep()) {
|
| HeapEntry* entry = snapshot_->entries()->at(i);
|
| int entry_size = entry->self_size();
|
| for (HeapEntry* dominator = entry->dominator();
|
| @@ -2364,32 +2697,12 @@
|
| entry = dominator, dominator = entry->dominator()) {
|
| dominator->add_retained_size(entry_size);
|
| }
|
| - if (!ReportProgress()) return false;
|
| + if (!ProgressReport()) return false;
|
| }
|
| return true;
|
| }
|
|
|
|
|
| -bool HeapSnapshotGenerator::IterateAndExtractReferences() {
|
| - HeapIterator iterator(HeapIterator::kFilterUnreachable);
|
| - bool interrupted = false;
|
| - // Heap iteration with filtering must be finished in any case.
|
| - for (HeapObject* obj = iterator.next();
|
| - obj != NULL;
|
| - obj = iterator.next(), IncProgressCounter()) {
|
| - if (!interrupted) {
|
| - ExtractReferences(obj);
|
| - if (!ReportProgress()) interrupted = true;
|
| - }
|
| - }
|
| - if (interrupted) return false;
|
| - SetRootGcRootsReference();
|
| - RootsReferencesExtractor extractor(this);
|
| - Heap::IterateRoots(&extractor, VISIT_ALL);
|
| - return ReportProgress();
|
| -}
|
| -
|
| -
|
| void HeapSnapshotsDiff::CreateRoots(int additions_count, int deletions_count) {
|
| raw_additions_root_ =
|
| NewArray<char>(HeapEntry::EntriesSize(1, additions_count, 0));
|
| @@ -2678,7 +2991,8 @@
|
| "," 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")
|
|
|