summaryrefslogtreecommitdiff
path: root/deps/v8/src/profile-generator.cc
diff options
context:
space:
mode:
authorRyan Dahl <ry@tinyclouds.org>2011-06-29 17:26:51 +0200
committerRyan Dahl <ry@tinyclouds.org>2011-06-29 17:26:51 +0200
commit33af2720f26c2b25bc7f75ce7eb454ff99db6d35 (patch)
tree9a38f0c96420edf503eebd6325dd8d2d8249f653 /deps/v8/src/profile-generator.cc
parent6afdca885adeeeed9eef8cbb01c3d97af0bc084d (diff)
downloadnode-33af2720f26c2b25bc7f75ce7eb454ff99db6d35.tar.gz
Upgrade V8 to 3.4.8
Diffstat (limited to 'deps/v8/src/profile-generator.cc')
-rw-r--r--deps/v8/src/profile-generator.cc1014
1 files changed, 655 insertions, 359 deletions
diff --git a/deps/v8/src/profile-generator.cc b/deps/v8/src/profile-generator.cc
index 7612eab99..b2c9de852 100644
--- a/deps/v8/src/profile-generator.cc
+++ b/deps/v8/src/profile-generator.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -28,14 +28,15 @@
#ifdef ENABLE_LOGGING_AND_PROFILING
#include "v8.h"
+
+#include "profile-generator-inl.h"
+
#include "global-handles.h"
+#include "heap-profiler.h"
#include "scopeinfo.h"
-#include "top.h"
#include "unicode.h"
#include "zone-inl.h"
-#include "profile-generator-inl.h"
-
namespace v8 {
namespace internal {
@@ -47,24 +48,27 @@ TokenEnumerator::TokenEnumerator()
TokenEnumerator::~TokenEnumerator() {
+ Isolate* isolate = Isolate::Current();
for (int i = 0; i < token_locations_.length(); ++i) {
if (!token_removed_[i]) {
- GlobalHandles::ClearWeakness(token_locations_[i]);
- GlobalHandles::Destroy(token_locations_[i]);
+ isolate->global_handles()->ClearWeakness(token_locations_[i]);
+ isolate->global_handles()->Destroy(token_locations_[i]);
}
}
}
int TokenEnumerator::GetTokenId(Object* token) {
+ Isolate* isolate = Isolate::Current();
if (token == NULL) return TokenEnumerator::kNoSecurityToken;
for (int i = 0; i < token_locations_.length(); ++i) {
if (*token_locations_[i] == token && !token_removed_[i]) return i;
}
- Handle<Object> handle = GlobalHandles::Create(token);
+ Handle<Object> handle = isolate->global_handles()->Create(token);
// handle.location() points to a memory cell holding a pointer
// to a token object in the V8's heap.
- GlobalHandles::MakeWeak(handle.location(), this, TokenRemovedCallback);
+ isolate->global_handles()->MakeWeak(handle.location(), this,
+ TokenRemovedCallback);
token_locations_.Add(handle.location());
token_removed_.Add(false);
return token_locations_.length() - 1;
@@ -94,55 +98,74 @@ 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::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);
}
-const char* CodeEntry::kEmptyNamePrefix = "";
+const char* const CodeEntry::kEmptyNamePrefix = "";
void CodeEntry::CopyData(const CodeEntry& source) {
@@ -298,7 +321,7 @@ struct NodesPair {
class FilteredCloneCallback {
public:
- explicit FilteredCloneCallback(ProfileNode* dst_root, int security_token_id)
+ FilteredCloneCallback(ProfileNode* dst_root, int security_token_id)
: stack_(10),
security_token_id_(security_token_id) {
stack_.Add(NodesPair(NULL, dst_root));
@@ -465,7 +488,7 @@ void CpuProfile::Print() {
}
-CodeEntry* const CodeMap::kSfiCodeEntry = NULL;
+CodeEntry* const CodeMap::kSharedFunctionCodeEntry = NULL;
const CodeMap::CodeTreeConfig::Key CodeMap::CodeTreeConfig::kNoKey = NULL;
const CodeMap::CodeTreeConfig::Value CodeMap::CodeTreeConfig::kNoValue =
CodeMap::CodeEntryInfo(NULL, 0);
@@ -483,18 +506,18 @@ CodeEntry* CodeMap::FindEntry(Address addr) {
}
-int CodeMap::GetSFITag(Address addr) {
+int CodeMap::GetSharedId(Address addr) {
CodeTree::Locator locator;
- // For SFI entries, 'size' field is used to store their IDs.
+ // 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 == kSfiCodeEntry);
+ ASSERT(entry.entry == kSharedFunctionCodeEntry);
return entry.size;
} else {
tree_.Insert(addr, &locator);
- int tag = next_sfi_tag_++;
- locator.set_value(CodeEntryInfo(kSfiCodeEntry, tag));
- return tag;
+ int id = next_shared_id_++;
+ locator.set_value(CodeEntryInfo(kSharedFunctionCodeEntry, id));
+ return id;
}
}
@@ -528,13 +551,16 @@ static void DeleteCpuProfile(CpuProfile** profile_ptr) {
}
static void DeleteProfilesList(List<CpuProfile*>** list_ptr) {
- (*list_ptr)->Iterate(DeleteCpuProfile);
- delete *list_ptr;
+ if (*list_ptr != NULL) {
+ (*list_ptr)->Iterate(DeleteCpuProfile);
+ delete *list_ptr;
+ }
}
CpuProfilesCollection::~CpuProfilesCollection() {
delete current_profiles_semaphore_;
current_profiles_.Iterate(DeleteCpuProfile);
+ detached_profiles_.Iterate(DeleteCpuProfile);
profiles_by_token_.Iterate(DeleteProfilesList);
code_entries_.Iterate(DeleteCodeEntry);
}
@@ -599,15 +625,8 @@ CpuProfile* CpuProfilesCollection::StopProfiling(int security_token_id,
CpuProfile* CpuProfilesCollection::GetProfile(int security_token_id,
unsigned uid) {
- HashMap::Entry* entry = profiles_uids_.Lookup(reinterpret_cast<void*>(uid),
- static_cast<uint32_t>(uid),
- false);
- int index;
- if (entry != NULL) {
- index = static_cast<int>(reinterpret_cast<intptr_t>(entry->value));
- } else {
- return NULL;
- }
+ int index = GetProfileIndex(uid);
+ if (index < 0) return NULL;
List<CpuProfile*>* unabridged_list =
profiles_by_token_[TokenToIndex(TokenEnumerator::kNoSecurityToken)];
if (security_token_id == TokenEnumerator::kNoSecurityToken) {
@@ -622,6 +641,15 @@ CpuProfile* CpuProfilesCollection::GetProfile(int security_token_id,
}
+int CpuProfilesCollection::GetProfileIndex(unsigned uid) {
+ HashMap::Entry* entry = profiles_uids_.Lookup(reinterpret_cast<void*>(uid),
+ static_cast<uint32_t>(uid),
+ false);
+ return entry != NULL ?
+ static_cast<int>(reinterpret_cast<intptr_t>(entry->value)) : -1;
+}
+
+
bool CpuProfilesCollection::IsLastProfile(const char* title) {
// Called from VM thread, and only it can mutate the list,
// so no locking is needed here.
@@ -631,6 +659,39 @@ bool CpuProfilesCollection::IsLastProfile(const char* title) {
}
+void CpuProfilesCollection::RemoveProfile(CpuProfile* profile) {
+ // Called from VM thread for a completed profile.
+ unsigned uid = profile->uid();
+ int index = GetProfileIndex(uid);
+ if (index < 0) {
+ detached_profiles_.RemoveElement(profile);
+ return;
+ }
+ profiles_uids_.Remove(reinterpret_cast<void*>(uid),
+ static_cast<uint32_t>(uid));
+ // Decrement all indexes above the deleted one.
+ for (HashMap::Entry* p = profiles_uids_.Start();
+ p != NULL;
+ p = profiles_uids_.Next(p)) {
+ intptr_t p_index = reinterpret_cast<intptr_t>(p->value);
+ if (p_index > index) {
+ p->value = reinterpret_cast<void*>(p_index - 1);
+ }
+ }
+ for (int i = 0; i < profiles_by_token_.length(); ++i) {
+ List<CpuProfile*>* list = profiles_by_token_[i];
+ if (list != NULL && index < list->length()) {
+ // Move all filtered clones into detached_profiles_,
+ // so we can know that they are still in use.
+ CpuProfile* cloned_profile = list->Remove(index);
+ if (cloned_profile != NULL && cloned_profile != profile) {
+ detached_profiles_.Add(cloned_profile);
+ }
+ }
+ }
+}
+
+
int CpuProfilesCollection::TokenToIndex(int security_token_id) {
ASSERT(TokenEnumerator::kNoSecurityToken == -1);
return security_token_id + 1; // kNoSecurityToken -> 0, 0 -> 1, ...
@@ -763,10 +824,12 @@ void SampleRateCalculator::UpdateMeasurements(double current_time) {
}
-const char* ProfileGenerator::kAnonymousFunctionName = "(anonymous function)";
-const char* ProfileGenerator::kProgramEntryName = "(program)";
-const char* ProfileGenerator::kGarbageCollectorEntryName =
- "(garbage collector)";
+const char* const ProfileGenerator::kAnonymousFunctionName =
+ "(anonymous function)";
+const char* const ProfileGenerator::kProgramEntryName =
+ "(program)";
+const char* const ProfileGenerator::kGarbageCollectorEntryName =
+ "(garbage collector)";
ProfileGenerator::ProfileGenerator(CpuProfilesCollection* profiles)
@@ -789,7 +852,15 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) {
if (sample.pc != NULL) {
*entry++ = code_map_.FindEntry(sample.pc);
- if (sample.tos != NULL) {
+ if (sample.has_external_callback) {
+ // Don't use PC when in external callback code, as it can point
+ // inside callback's code, and we will erroneously report
+ // that a callback calls itself.
+ *(entries.start()) = NULL;
+ *entry++ = code_map_.FindEntry(sample.external_callback);
+ } else if (sample.tos != NULL) {
+ // Find out, if top of stack was pointing inside a JS function
+ // meaning that we have encountered a frameless invocation.
*entry = code_map_.FindEntry(sample.tos);
if (*entry != NULL && !(*entry)->is_js_function()) {
*entry = NULL;
@@ -914,11 +985,6 @@ int HeapEntry::RetainedSize(bool exact) {
}
-List<HeapGraphPath*>* HeapEntry::GetRetainingPaths() {
- return snapshot_->GetRetainingPaths(this);
-}
-
-
template<class Visitor>
void HeapEntry::ApplyAndPaintAllReachable(Visitor* visitor) {
List<HeapEntry*> list(10);
@@ -1009,6 +1075,7 @@ const char* HeapEntry::TypeAsString() {
case kArray: return "/array/";
case kRegExp: return "/regexp/";
case kHeapNumber: return "/number/";
+ case kNative: return "/native/";
default: return "???";
}
}
@@ -1029,7 +1096,7 @@ class RetainedSizeCalculator {
: retained_size_(0) {
}
- int reained_size() const { return retained_size_; }
+ int retained_size() const { return retained_size_; }
void Apply(HeapEntry** entry_ptr) {
if ((*entry_ptr)->painted_reachable()) {
@@ -1070,113 +1137,12 @@ void HeapEntry::CalculateExactRetainedSize() {
RetainedSizeCalculator ret_size_calc;
snapshot()->IterateEntries(&ret_size_calc);
- retained_size_ = ret_size_calc.reained_size();
+ retained_size_ = ret_size_calc.retained_size();
ASSERT((retained_size_ & kExactRetainedSizeTag) == 0);
retained_size_ |= kExactRetainedSizeTag;
}
-class CachedHeapGraphPath {
- public:
- CachedHeapGraphPath()
- : nodes_(NodesMatch) { }
- CachedHeapGraphPath(const CachedHeapGraphPath& src)
- : nodes_(NodesMatch, &HashMap::DefaultAllocator, src.nodes_.capacity()),
- path_(src.path_.length() + 1) {
- for (HashMap::Entry* p = src.nodes_.Start();
- p != NULL;
- p = src.nodes_.Next(p)) {
- nodes_.Lookup(p->key, p->hash, true);
- }
- path_.AddAll(src.path_);
- }
- void Add(HeapGraphEdge* edge) {
- nodes_.Lookup(edge->to(), Hash(edge->to()), true);
- path_.Add(edge);
- }
- bool ContainsNode(HeapEntry* node) {
- return nodes_.Lookup(node, Hash(node), false) != NULL;
- }
- const List<HeapGraphEdge*>* path() const { return &path_; }
-
- private:
- static uint32_t Hash(HeapEntry* entry) {
- return static_cast<uint32_t>(reinterpret_cast<intptr_t>(entry));
- }
- static bool NodesMatch(void* key1, void* key2) { return key1 == key2; }
-
- HashMap nodes_;
- List<HeapGraphEdge*> path_;
-};
-
-
-List<HeapGraphPath*>* HeapEntry::CalculateRetainingPaths() {
- List<HeapGraphPath*>* retaining_paths = new List<HeapGraphPath*>(4);
- CachedHeapGraphPath path;
- FindRetainingPaths(&path, retaining_paths);
- return retaining_paths;
-}
-
-
-void HeapEntry::FindRetainingPaths(CachedHeapGraphPath* prev_path,
- List<HeapGraphPath*>* retaining_paths) {
- Vector<HeapGraphEdge*> rets = retainers();
- for (int i = 0; i < rets.length(); ++i) {
- HeapGraphEdge* ret_edge = rets[i];
- if (prev_path->ContainsNode(ret_edge->From())) continue;
- if (ret_edge->From() != snapshot()->root()) {
- CachedHeapGraphPath path(*prev_path);
- path.Add(ret_edge);
- ret_edge->From()->FindRetainingPaths(&path, retaining_paths);
- } else {
- HeapGraphPath* ret_path = new HeapGraphPath(*prev_path->path());
- ret_path->Set(0, ret_edge);
- retaining_paths->Add(ret_path);
- }
- }
-}
-
-
-HeapGraphPath::HeapGraphPath(const List<HeapGraphEdge*>& path)
- : path_(path.length() + 1) {
- Add(NULL);
- for (int i = path.length() - 1; i >= 0; --i) {
- Add(path[i]);
- }
-}
-
-
-void HeapGraphPath::Print() {
- path_[0]->From()->Print(1, 0);
- for (int i = 0; i < path_.length(); ++i) {
- OS::Print(" -> ");
- HeapGraphEdge* edge = path_[i];
- switch (edge->type()) {
- case HeapGraphEdge::kContextVariable:
- OS::Print("[#%s] ", edge->name());
- break;
- case HeapGraphEdge::kElement:
- case HeapGraphEdge::kHidden:
- OS::Print("[%d] ", edge->index());
- break;
- case HeapGraphEdge::kInternal:
- OS::Print("[$%s] ", edge->name());
- break;
- case HeapGraphEdge::kProperty:
- OS::Print("[%s] ", edge->name());
- break;
- case HeapGraphEdge::kShortcut:
- OS::Print("[^%s] ", edge->name());
- break;
- default:
- OS::Print("!!! unknown edge type: %d ", edge->type());
- }
- edge->to()->Print(1, 0);
- }
- OS::Print("\n");
-}
-
-
// It is very important to keep objects that form a heap snapshot
// as small as possible.
namespace { // Avoid littering the global namespace.
@@ -1205,9 +1171,9 @@ HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection,
uid_(uid),
root_entry_(NULL),
gc_roots_entry_(NULL),
+ natives_root_entry_(NULL),
raw_entries_(NULL),
- entries_sorted_(false),
- retaining_paths_(HeapEntry::Match) {
+ entries_sorted_(false) {
STATIC_ASSERT(
sizeof(HeapGraphEdge) ==
SnapshotSizeConstants<sizeof(void*)>::kExpectedHeapGraphEdgeSize); // NOLINT
@@ -1216,21 +1182,14 @@ HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection,
SnapshotSizeConstants<sizeof(void*)>::kExpectedHeapEntrySize); // NOLINT
}
-
-static void DeleteHeapGraphPath(HeapGraphPath** path_ptr) {
- delete *path_ptr;
-}
-
HeapSnapshot::~HeapSnapshot() {
DeleteArray(raw_entries_);
- for (HashMap::Entry* p = retaining_paths_.Start();
- p != NULL;
- p = retaining_paths_.Next(p)) {
- List<HeapGraphPath*>* list =
- reinterpret_cast<List<HeapGraphPath*>*>(p->value);
- list->Iterate(DeleteHeapGraphPath);
- delete list;
- }
+}
+
+
+void HeapSnapshot::Delete() {
+ collection_->RemoveSnapshot(this);
+ delete this;
}
@@ -1279,6 +1238,19 @@ 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::kObject,
+ "(Native objects)",
+ HeapObjectsMap::kNativesRootObjectId,
+ 0,
+ children_count,
+ retainers_count));
+}
+
+
HeapEntry* HeapSnapshot::AddEntry(HeapEntry::Type type,
const char* name,
uint64_t id,
@@ -1313,14 +1285,7 @@ HeapEntry* HeapSnapshot::GetNextEntryToInit() {
}
-HeapSnapshotsDiff* HeapSnapshot::CompareWith(HeapSnapshot* snapshot) {
- return collection_->CompareSnapshots(this, snapshot);
-}
-
-
HeapEntry* HeapSnapshot::GetEntryById(uint64_t id) {
- // GetSortedEntriesList is used in diff algorithm and sorts
- // entries by their id.
List<HeapEntry*>* entries_by_id = GetSortedEntriesList();
// Perform a binary search by id.
@@ -1341,16 +1306,6 @@ HeapEntry* HeapSnapshot::GetEntryById(uint64_t id) {
}
-List<HeapGraphPath*>* HeapSnapshot::GetRetainingPaths(HeapEntry* entry) {
- HashMap::Entry* p =
- retaining_paths_.Lookup(entry, HeapEntry::Hash(entry), true);
- if (p->value == NULL) {
- p->value = entry->CalculateRetainingPaths();
- }
- return reinterpret_cast<List<HeapGraphPath*>*>(p->value);
-}
-
-
template<class T>
static int SortByIds(const T* entry1_ptr,
const T* entry2_ptr) {
@@ -1372,10 +1327,13 @@ void HeapSnapshot::Print(int max_depth) {
}
-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),
@@ -1400,7 +1358,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 +1427,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, 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),
@@ -1517,10 +1487,11 @@ HeapSnapshot* HeapSnapshotsCollection::GetSnapshot(unsigned uid) {
}
-HeapSnapshotsDiff* HeapSnapshotsCollection::CompareSnapshots(
- HeapSnapshot* snapshot1,
- HeapSnapshot* snapshot2) {
- return comparator_.Compare(snapshot1, snapshot2);
+void HeapSnapshotsCollection::RemoveSnapshot(HeapSnapshot* snapshot) {
+ snapshots_.RemoveElement(snapshot);
+ unsigned uid = snapshot->uid();
+ snapshots_uids_.Remove(reinterpret_cast<void*>(uid),
+ static_cast<uint32_t>(uid));
}
@@ -1551,6 +1522,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;
}
@@ -1629,10 +1602,34 @@ void HeapObjectsSet::Insert(Object* obj) {
}
+const char* HeapObjectsSet::GetTag(Object* obj) {
+ HeapObject* object = HeapObject::cast(obj);
+ HashMap::Entry* cache_entry =
+ entries_.Lookup(object, HeapEntriesMap::Hash(object), false);
+ if (cache_entry != NULL
+ && cache_entry->value != HeapEntriesMap::kHeapEntryPlaceholder) {
+ return reinterpret_cast<const char*>(cache_entry->value);
+ } else {
+ return NULL;
+ }
+}
+
+
+void HeapObjectsSet::SetTag(Object* obj, const char* tag) {
+ if (!obj->IsHeapObject()) return;
+ HeapObject* object = HeapObject::cast(obj);
+ HashMap::Entry* cache_entry =
+ entries_.Lookup(object, HeapEntriesMap::Hash(object), true);
+ cache_entry->value = const_cast<char*>(tag);
+}
+
+
HeapObject *const V8HeapExplorer::kInternalRootObject =
- reinterpret_cast<HeapObject*>(1);
+ reinterpret_cast<HeapObject*>(
+ static_cast<intptr_t>(HeapObjectsMap::kInternalRootObjectId));
HeapObject *const V8HeapExplorer::kGcRootsObject =
- reinterpret_cast<HeapObject*>(2);
+ reinterpret_cast<HeapObject*>(
+ static_cast<intptr_t>(HeapObjectsMap::kGcRootsObjectId));
V8HeapExplorer::V8HeapExplorer(
@@ -1664,32 +1661,45 @@ HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object,
return snapshot_->AddRootEntry(children_count);
} else if (object == kGcRootsObject) {
return snapshot_->AddGcRootsEntry(children_count, retainers_count);
+ } else if (object->IsJSGlobalObject()) {
+ const char* tag = objects_tags_.GetTag(object);
+ const char* name = collection_->names()->GetName(
+ GetConstructorNameForHeapProfile(JSObject::cast(object)));
+ if (tag != NULL) {
+ name = collection_->names()->GetFormatted("%s / %s", name, tag);
+ }
+ return AddEntry(object,
+ HeapEntry::kObject,
+ name,
+ 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())),
+ 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_->GetName(re->Pattern()),
+ collection_->names()->GetName(re->Pattern()),
children_count,
retainers_count);
} else if (object->IsJSObject()) {
return AddEntry(object,
HeapEntry::kObject,
- collection_->GetName(GetConstructorNameForHeapProfile(
- JSObject::cast(object))),
+ collection_->names()->GetName(
+ GetConstructorNameForHeapProfile(
+ JSObject::cast(object))),
children_count,
retainers_count);
} else if (object->IsString()) {
return AddEntry(object,
HeapEntry::kString,
- collection_->GetName(String::cast(object)),
+ collection_->names()->GetName(String::cast(object)),
children_count,
retainers_count);
} else if (object->IsCode()) {
@@ -1702,7 +1712,7 @@ HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object,
SharedFunctionInfo* shared = SharedFunctionInfo::cast(object);
return AddEntry(object,
HeapEntry::kCode,
- collection_->GetName(String::cast(shared->name())),
+ collection_->names()->GetName(String::cast(shared->name())),
children_count,
retainers_count);
} else if (object->IsScript()) {
@@ -1710,10 +1720,12 @@ HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object,
return AddEntry(object,
HeapEntry::kCode,
script->name()->IsString() ?
- collection_->GetName(String::cast(script->name())) : "",
+ collection_->names()->GetName(
+ String::cast(script->name()))
+ : "",
children_count,
retainers_count);
- } else if (object->IsFixedArray()) {
+ } else if (object->IsFixedArray() || object->IsByteArray()) {
return AddEntry(object,
HeapEntry::kArray,
"",
@@ -1728,7 +1740,7 @@ HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object,
}
return AddEntry(object,
HeapEntry::kHidden,
- "system",
+ GetSystemEntryName(object),
children_count,
retainers_count);
}
@@ -1749,8 +1761,23 @@ HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object,
void V8HeapExplorer::AddRootEntries(SnapshotFillerInterface* filler) {
- filler->AddEntry(kInternalRootObject);
- filler->AddEntry(kGcRootsObject);
+ filler->AddEntry(kInternalRootObject, this);
+ filler->AddEntry(kGcRootsObject, this);
+}
+
+
+const char* V8HeapExplorer::GetSystemEntryName(HeapObject* object) {
+ switch (object->map()->instance_type()) {
+ case MAP_TYPE: return "system / Map";
+ case JS_GLOBAL_PROPERTY_CELL_TYPE: return "system / JSGlobalPropertyCell";
+ case FOREIGN_TYPE: return "system / Foreign";
+ case ODDBALL_TYPE: return "system / Oddball";
+#define MAKE_STRUCT_CASE(NAME, Name, name) \
+ case NAME##_TYPE: return "system / "#Name;
+ STRUCT_LIST(MAKE_STRUCT_CASE)
+#undef MAKE_STRUCT_CASE
+ default: return "system";
+ }
}
@@ -1768,26 +1795,39 @@ class IndexedReferencesExtractor : public ObjectVisitor {
public:
IndexedReferencesExtractor(V8HeapExplorer* generator,
HeapObject* parent_obj,
- HeapEntry* parent_entry,
- HeapObjectsSet* known_references = NULL)
+ HeapEntry* parent_entry)
: generator_(generator),
parent_obj_(parent_obj),
parent_(parent_entry),
- known_references_(known_references),
next_index_(1) {
}
void VisitPointers(Object** start, Object** end) {
for (Object** p = start; p < end; p++) {
- if (!known_references_ || !known_references_->Contains(*p)) {
- generator_->SetHiddenReference(parent_obj_, parent_, next_index_++, *p);
- }
+ if (CheckVisitedAndUnmark(p)) continue;
+ generator_->SetHiddenReference(parent_obj_, parent_, next_index_++, *p);
}
}
+ static void MarkVisitedField(HeapObject* obj, int offset) {
+ if (offset < 0) return;
+ Address field = obj->address() + offset;
+ ASSERT(!Memory::Object_at(field)->IsFailure());
+ ASSERT(Memory::Object_at(field)->IsHeapObject());
+ *field |= kFailureTag;
+ }
+
private:
+ bool CheckVisitedAndUnmark(Object** field) {
+ if ((*field)->IsFailure()) {
+ intptr_t untagged = reinterpret_cast<intptr_t>(*field) & ~kFailureTagMask;
+ *field = reinterpret_cast<Object*>(untagged | kHeapObjectTag);
+ ASSERT((*field)->IsHeapObject());
+ return true;
+ }
+ return false;
+ }
V8HeapExplorer* generator_;
HeapObject* parent_obj_;
HeapEntry* parent_;
- HeapObjectsSet* known_references_;
int next_index_;
};
@@ -1796,13 +1836,13 @@ void V8HeapExplorer::ExtractReferences(HeapObject* obj) {
HeapEntry* entry = GetEntry(obj);
if (entry == NULL) return; // No interest in this object.
- known_references_.Clear();
if (obj->IsJSGlobalProxy()) {
// We need to reference JS global objects from snapshot's root.
// We use JSGlobalProxy because this is what embedder (e.g. browser)
// uses for the global object.
JSGlobalProxy* proxy = JSGlobalProxy::cast(obj);
SetRootShortcutReference(proxy->map()->prototype());
+ SetInternalReference(obj, entry, "map", obj->map(), HeapObject::kMapOffset);
IndexedReferencesExtractor refs_extractor(this, obj, entry);
obj->Iterate(&refs_extractor);
} else if (obj->IsJSObject()) {
@@ -1812,16 +1852,40 @@ void V8HeapExplorer::ExtractReferences(HeapObject* obj) {
ExtractElementReferences(js_obj, entry);
ExtractInternalReferences(js_obj, entry);
SetPropertyReference(
- obj, entry, Heap::Proto_symbol(), js_obj->GetPrototype());
+ obj, entry, HEAP->Proto_symbol(), js_obj->GetPrototype());
if (obj->IsJSFunction()) {
- JSFunction* js_fun = JSFunction::cast(obj);
- if (js_fun->has_prototype()) {
- SetPropertyReference(
- obj, entry, Heap::prototype_symbol(), js_fun->prototype());
+ JSFunction* js_fun = JSFunction::cast(js_obj);
+ Object* proto_or_map = js_fun->prototype_or_initial_map();
+ if (!proto_or_map->IsTheHole()) {
+ if (!proto_or_map->IsMap()) {
+ SetPropertyReference(
+ obj, entry,
+ HEAP->prototype_symbol(), proto_or_map,
+ JSFunction::kPrototypeOrInitialMapOffset);
+ } else {
+ SetPropertyReference(
+ obj, entry,
+ HEAP->prototype_symbol(), js_fun->prototype());
+ }
}
+ SetInternalReference(js_fun, entry,
+ "shared", js_fun->shared(),
+ JSFunction::kSharedFunctionInfoOffset);
+ SetInternalReference(js_fun, entry,
+ "context", js_fun->unchecked_context(),
+ JSFunction::kContextOffset);
+ SetInternalReference(js_fun, entry,
+ "literals", js_fun->literals(),
+ JSFunction::kLiteralsOffset);
}
- IndexedReferencesExtractor refs_extractor(
- this, obj, entry, &known_references_);
+ SetInternalReference(obj, entry,
+ "properties", js_obj->properties(),
+ JSObject::kPropertiesOffset);
+ SetInternalReference(obj, entry,
+ "elements", js_obj->elements(),
+ JSObject::kElementsOffset);
+ SetInternalReference(obj, entry, "map", obj->map(), HeapObject::kMapOffset);
+ IndexedReferencesExtractor refs_extractor(this, obj, entry);
obj->Iterate(&refs_extractor);
} else if (obj->IsString()) {
if (obj->IsConsString()) {
@@ -1829,7 +1893,43 @@ void V8HeapExplorer::ExtractReferences(HeapObject* obj) {
SetInternalReference(obj, entry, 1, cs->first());
SetInternalReference(obj, entry, 2, cs->second());
}
+ } else if (obj->IsMap()) {
+ Map* map = Map::cast(obj);
+ SetInternalReference(obj, entry,
+ "prototype", map->prototype(), Map::kPrototypeOffset);
+ SetInternalReference(obj, entry,
+ "constructor", map->constructor(),
+ Map::kConstructorOffset);
+ if (!map->instance_descriptors()->IsEmpty()) {
+ SetInternalReference(obj, entry,
+ "descriptors", map->instance_descriptors(),
+ Map::kInstanceDescriptorsOrBitField3Offset);
+ }
+ SetInternalReference(obj, entry,
+ "code_cache", map->code_cache(),
+ Map::kCodeCacheOffset);
+ SetInternalReference(obj, entry, "map", obj->map(), HeapObject::kMapOffset);
+ IndexedReferencesExtractor refs_extractor(this, obj, entry);
+ obj->Iterate(&refs_extractor);
+ } else if (obj->IsSharedFunctionInfo()) {
+ SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
+ SetInternalReference(obj, entry,
+ "name", shared->name(),
+ SharedFunctionInfo::kNameOffset);
+ SetInternalReference(obj, entry,
+ "code", shared->unchecked_code(),
+ SharedFunctionInfo::kCodeOffset);
+ SetInternalReference(obj, entry,
+ "instance_class_name", shared->instance_class_name(),
+ SharedFunctionInfo::kInstanceClassNameOffset);
+ SetInternalReference(obj, entry,
+ "script", shared->script(),
+ SharedFunctionInfo::kScriptOffset);
+ SetInternalReference(obj, entry, "map", obj->map(), HeapObject::kMapOffset);
+ IndexedReferencesExtractor refs_extractor(this, obj, entry);
+ obj->Iterate(&refs_extractor);
} else {
+ SetInternalReference(obj, entry, "map", obj->map(), HeapObject::kMapOffset);
IndexedReferencesExtractor refs_extractor(this, obj, entry);
obj->Iterate(&refs_extractor);
}
@@ -1842,7 +1942,7 @@ void V8HeapExplorer::ExtractClosureReferences(JSObject* js_obj,
HandleScope hs;
JSFunction* func = JSFunction::cast(js_obj);
Context* context = func->context();
- ZoneScope zscope(DELETE_ON_EXIT);
+ ZoneScope zscope(Isolate::Current(), DELETE_ON_EXIT);
SerializedScopeInfo* serialized_scope_info =
context->closure()->shared()->scope_info();
ScopeInfo<ZoneListAllocationPolicy> zone_scope_info(serialized_scope_info);
@@ -1854,7 +1954,6 @@ void V8HeapExplorer::ExtractClosureReferences(JSObject* js_obj,
SetClosureReference(js_obj, entry, local_name, context->get(idx));
}
}
- SetInternalReference(js_obj, entry, "code", func->shared());
}
}
@@ -1867,13 +1966,22 @@ void V8HeapExplorer::ExtractPropertyReferences(JSObject* js_obj,
switch (descs->GetType(i)) {
case FIELD: {
int index = descs->GetFieldIndex(i);
- SetPropertyReference(
- js_obj, entry, descs->GetKey(i), js_obj->FastPropertyAt(index));
+ if (index < js_obj->map()->inobject_properties()) {
+ SetPropertyReference(
+ js_obj, entry,
+ descs->GetKey(i), js_obj->InObjectPropertyAt(index),
+ js_obj->GetInObjectPropertyOffset(index));
+ } else {
+ SetPropertyReference(
+ js_obj, entry,
+ descs->GetKey(i), js_obj->FastPropertyAt(index));
+ }
break;
}
case CONSTANT_FUNCTION:
SetPropertyReference(
- js_obj, entry, descs->GetKey(i), descs->GetConstantFunction(i));
+ js_obj, entry,
+ descs->GetKey(i), descs->GetConstantFunction(i));
break;
default: ;
}
@@ -1933,14 +2041,15 @@ void V8HeapExplorer::ExtractInternalReferences(JSObject* js_obj,
int length = js_obj->GetInternalFieldCount();
for (int i = 0; i < length; ++i) {
Object* o = js_obj->GetInternalField(i);
- SetInternalReference(js_obj, entry, i, o);
+ SetInternalReference(
+ js_obj, entry, i, o, js_obj->GetInternalFieldOffset(i));
}
}
HeapEntry* V8HeapExplorer::GetEntry(Object* obj) {
if (!obj->IsHeapObject()) return NULL;
- return filler_->FindOrAddEntry(obj);
+ return filler_->FindOrAddEntry(obj, this);
}
@@ -1977,7 +2086,7 @@ bool V8HeapExplorer::IterateAndExtractReferences(
}
SetRootGcRootsReference();
RootsReferencesExtractor extractor(this);
- Heap::IterateRoots(&extractor, VISIT_ALL);
+ HEAP->IterateRoots(&extractor, VISIT_ALL);
filler_ = NULL;
return progress_->ProgressReport(false);
}
@@ -1992,10 +2101,9 @@ void V8HeapExplorer::SetClosureReference(HeapObject* parent_obj,
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);
}
}
@@ -2012,7 +2120,6 @@ void V8HeapExplorer::SetElementReference(HeapObject* parent_obj,
index,
child_obj,
child_entry);
- known_references_.Insert(child_obj);
}
}
@@ -2020,7 +2127,8 @@ void V8HeapExplorer::SetElementReference(HeapObject* parent_obj,
void V8HeapExplorer::SetInternalReference(HeapObject* parent_obj,
HeapEntry* parent_entry,
const char* reference_name,
- Object* child_obj) {
+ Object* child_obj,
+ int field_offset) {
HeapEntry* child_entry = GetEntry(child_obj);
if (child_entry != NULL) {
filler_->SetNamedReference(HeapGraphEdge::kInternal,
@@ -2029,7 +2137,7 @@ void V8HeapExplorer::SetInternalReference(HeapObject* parent_obj,
reference_name,
child_obj,
child_entry);
- known_references_.Insert(child_obj);
+ IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
}
}
@@ -2037,16 +2145,17 @@ void V8HeapExplorer::SetInternalReference(HeapObject* parent_obj,
void V8HeapExplorer::SetInternalReference(HeapObject* parent_obj,
HeapEntry* parent_entry,
int index,
- Object* child_obj) {
+ Object* child_obj,
+ int field_offset) {
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);
+ IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
}
}
@@ -2070,7 +2179,8 @@ void V8HeapExplorer::SetHiddenReference(HeapObject* parent_obj,
void V8HeapExplorer::SetPropertyReference(HeapObject* parent_obj,
HeapEntry* parent_entry,
String* reference_name,
- Object* child_obj) {
+ Object* child_obj,
+ int field_offset) {
HeapEntry* child_entry = GetEntry(child_obj);
if (child_entry != NULL) {
HeapGraphEdge::Type type = reference_name->length() > 0 ?
@@ -2078,25 +2188,24 @@ void V8HeapExplorer::SetPropertyReference(HeapObject* parent_obj,
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);
+ IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
}
}
-void V8HeapExplorer::SetPropertyShortcutReference(
- HeapObject* parent_obj,
- HeapEntry* parent_entry,
- String* reference_name,
- Object* child_obj) {
+void V8HeapExplorer::SetPropertyShortcutReference(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::kShortcut,
parent_obj,
parent_entry,
- collection_->GetName(reference_name),
+ collection_->names()->GetName(reference_name),
child_obj,
child_entry);
}
@@ -2132,25 +2241,275 @@ void V8HeapExplorer::SetGcRootsReference(Object* child_obj) {
}
+class GlobalObjectsEnumerator : public ObjectVisitor {
+ public:
+ virtual void VisitPointers(Object** start, Object** end) {
+ for (Object** p = start; p < end; p++) {
+ if ((*p)->IsGlobalContext()) {
+ Context* context = Context::cast(*p);
+ JSObject* proxy = context->global_proxy();
+ if (proxy->IsJSGlobalProxy()) {
+ Object* global = proxy->map()->prototype();
+ if (global->IsJSGlobalObject()) {
+ objects_.Add(Handle<JSGlobalObject>(JSGlobalObject::cast(global)));
+ }
+ }
+ }
+ }
+ }
+ int count() { return objects_.length(); }
+ Handle<JSGlobalObject>& at(int i) { return objects_[i]; }
+
+ private:
+ List<Handle<JSGlobalObject> > objects_;
+};
+
+
+// Modifies heap. Must not be run during heap traversal.
+void V8HeapExplorer::TagGlobalObjects() {
+ Isolate* isolate = Isolate::Current();
+ GlobalObjectsEnumerator enumerator;
+ isolate->global_handles()->IterateAllRoots(&enumerator);
+ Handle<String> document_string =
+ isolate->factory()->NewStringFromAscii(CStrVector("document"));
+ Handle<String> url_string =
+ isolate->factory()->NewStringFromAscii(CStrVector("URL"));
+ const char** urls = NewArray<const char*>(enumerator.count());
+ for (int i = 0, l = enumerator.count(); i < l; ++i) {
+ urls[i] = NULL;
+ Handle<JSGlobalObject> global_obj = enumerator.at(i);
+ Object* obj_document;
+ if (global_obj->GetProperty(*document_string)->ToObject(&obj_document) &&
+ obj_document->IsJSObject()) {
+ JSObject* document = JSObject::cast(obj_document);
+ Object* obj_url;
+ if (document->GetProperty(*url_string)->ToObject(&obj_url) &&
+ obj_url->IsString()) {
+ urls[i] = collection_->names()->GetName(String::cast(obj_url));
+ }
+ }
+ }
+
+ AssertNoAllocation no_allocation;
+ for (int i = 0, l = enumerator.count(); i < l; ++i) {
+ objects_tags_.SetTag(*enumerator.at(i), urls[i]);
+ }
+
+ DeleteArray(urls);
+}
+
+
+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;
+ Isolate* isolate = Isolate::Current();
+ // Record objects that are joined into ObjectGroups.
+ isolate->heap()->CallGlobalGCPrologueCallback();
+ List<ObjectGroup*>* groups = isolate->global_handles()->object_groups();
+ 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 (size_t j = 0; j < group->length_; ++j) {
+ HeapObject* obj = HeapObject::cast(*group->objects_[j]);
+ list->Add(obj);
+ in_groups_.Insert(obj);
+ }
+ group->info_ = NULL; // Acquire info object ownership.
+ }
+ isolate->global_handles()->RemoveObjectGroups();
+ isolate->heap()->CallGlobalGCEpilogueCallback();
+ // Record objects that are not in ObjectGroups, but have class ID.
+ GlobalHandlesExtractor extractor(this);
+ isolate->global_handles()->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;
+ Isolate* isolate = Isolate::Current();
+ v8::RetainedObjectInfo* info =
+ isolate->heap_profiler()->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,
@@ -2182,8 +2541,8 @@ class SnapshotCounter : public SnapshotFillerInterface {
HeapEntry*) {
entries_->CountReference(parent_ptr, child_ptr);
}
+
private:
- HeapEntriesAllocator* allocator_;
HeapEntriesMap* entries_;
};
@@ -2194,13 +2553,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,
@@ -2247,10 +2609,11 @@ class SnapshotFiller : public SnapshotFillerInterface {
parent_ptr, child_ptr, &child_index, &retainer_index);
parent_entry->SetNamedReference(type,
child_index,
- collection_->GetName(child_index + 1),
+ collection_->names()->GetName(child_index + 1),
child_entry,
retainer_index);
}
+
private:
HeapSnapshot* snapshot_;
HeapSnapshotsCollection* collection_;
@@ -2259,6 +2622,8 @@ class SnapshotFiller : public SnapshotFillerInterface {
bool HeapSnapshotGenerator::GenerateSnapshot() {
+ v8_heap_explorer_.TagGlobalObjects();
+
AssertNoAllocation no_alloc;
SetProgressTotal(4); // 2 passes + dominators + sizes.
@@ -2303,21 +2668,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);
}
@@ -2447,83 +2819,6 @@ bool HeapSnapshotGenerator::ApproximateRetainedSizes() {
}
-void HeapSnapshotsDiff::CreateRoots(int additions_count, int deletions_count) {
- raw_additions_root_ =
- NewArray<char>(HeapEntry::EntriesSize(1, additions_count, 0));
- additions_root()->Init(
- snapshot2_, HeapEntry::kHidden, "", 0, 0, additions_count, 0);
- raw_deletions_root_ =
- NewArray<char>(HeapEntry::EntriesSize(1, deletions_count, 0));
- deletions_root()->Init(
- snapshot1_, HeapEntry::kHidden, "", 0, 0, deletions_count, 0);
-}
-
-
-static void DeleteHeapSnapshotsDiff(HeapSnapshotsDiff** diff_ptr) {
- delete *diff_ptr;
-}
-
-HeapSnapshotsComparator::~HeapSnapshotsComparator() {
- diffs_.Iterate(DeleteHeapSnapshotsDiff);
-}
-
-
-HeapSnapshotsDiff* HeapSnapshotsComparator::Compare(HeapSnapshot* snapshot1,
- HeapSnapshot* snapshot2) {
- snapshot1->ClearPaint();
- snapshot1->root()->PaintAllReachable();
- snapshot2->ClearPaint();
- snapshot2->root()->PaintAllReachable();
-
- List<HeapEntry*>* entries1 = snapshot1->GetSortedEntriesList();
- List<HeapEntry*>* entries2 = snapshot2->GetSortedEntriesList();
- int i = 0, j = 0;
- List<HeapEntry*> added_entries, deleted_entries;
- while (i < entries1->length() && j < entries2->length()) {
- uint64_t id1 = entries1->at(i)->id();
- uint64_t id2 = entries2->at(j)->id();
- if (id1 == id2) {
- HeapEntry* entry1 = entries1->at(i++);
- HeapEntry* entry2 = entries2->at(j++);
- if (entry1->painted_reachable() != entry2->painted_reachable()) {
- if (entry1->painted_reachable())
- deleted_entries.Add(entry1);
- else
- added_entries.Add(entry2);
- }
- } else if (id1 < id2) {
- HeapEntry* entry = entries1->at(i++);
- deleted_entries.Add(entry);
- } else {
- HeapEntry* entry = entries2->at(j++);
- added_entries.Add(entry);
- }
- }
- while (i < entries1->length()) {
- HeapEntry* entry = entries1->at(i++);
- deleted_entries.Add(entry);
- }
- while (j < entries2->length()) {
- HeapEntry* entry = entries2->at(j++);
- added_entries.Add(entry);
- }
-
- HeapSnapshotsDiff* diff = new HeapSnapshotsDiff(snapshot1, snapshot2);
- diffs_.Add(diff);
- diff->CreateRoots(added_entries.length(), deleted_entries.length());
-
- for (int i = 0; i < deleted_entries.length(); ++i) {
- HeapEntry* entry = deleted_entries[i];
- diff->AddDeletedEntry(i, i + 1, entry);
- }
- for (int i = 0; i < added_entries.length(); ++i) {
- HeapEntry* entry = added_entries[i];
- diff->AddAddedEntry(i, i + 1, entry);
- }
- return diff;
-}
-
-
class OutputStreamWriter {
public:
explicit OutputStreamWriter(v8::OutputStream* stream)
@@ -2735,7 +3030,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")
@@ -2890,7 +3186,7 @@ void HeapSnapshotJSONSerializer::SortHashMap(
String* GetConstructorNameForHeapProfile(JSObject* object) {
- if (object->IsJSFunction()) return Heap::closure_symbol();
+ if (object->IsJSFunction()) return HEAP->closure_symbol();
return object->constructor_name();
}