Heap profiler: allow returning aggregated snapshots via the new API.

This is intended for smoother migration to the new API in Chromium.
Also, aggregated heap snapshots can be used for cheaply obtaining
heap statistics, e.g. in tests.

Review URL: http://codereview.chromium.org/3124024

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5297 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 2f5f2cad
......@@ -260,10 +260,17 @@ class V8EXPORT HeapGraphNode {
/**
* Returns node id. For the same heap object, the id remains the same
* across all snapshots.
* across all snapshots. Not applicable to aggregated heap snapshots
* as they only contain aggregated instances.
*/
uint64_t GetId() const;
/**
* Returns the number of instances. Only applicable to aggregated
* heap snapshots.
*/
int GetInstancesCount() const;
/** Returns node's own size, in bytes. */
int GetSelfSize() const;
......@@ -313,6 +320,15 @@ class V8EXPORT HeapSnapshotsDiff {
*/
class V8EXPORT HeapSnapshot {
public:
enum Type {
kFull = 0, // Heap snapshot with all instances and references.
kAggregated = 1 // Snapshot doesn't contain individual heap entries,
//instead they are grouped by constructor name.
};
/** Returns heap snapshot type. */
Type GetType() const;
/** Returns heap snapshot UID (assigned by the profiler.) */
unsigned GetUid() const;
......@@ -322,7 +338,10 @@ class V8EXPORT HeapSnapshot {
/** Returns the root node of the heap graph. */
const HeapGraphNode* GetRoot() const;
/** Returns a diff between this snapshot and another one. */
/**
* Returns a diff between this snapshot and another one. Only snapshots
* of the same type can be compared.
*/
const HeapSnapshotsDiff* CompareWith(const HeapSnapshot* snapshot) const;
};
......@@ -341,8 +360,13 @@ class V8EXPORT HeapProfiler {
/** Returns a profile by uid. */
static const HeapSnapshot* FindSnapshot(unsigned uid);
/** Takes a heap snapshot and returns it. Title may be an empty string. */
static const HeapSnapshot* TakeSnapshot(Handle<String> title);
/**
* Takes a heap snapshot and returns it. Title may be an empty string.
* See HeapSnapshot::Type for types description.
*/
static const HeapSnapshot* TakeSnapshot(
Handle<String> title,
HeapSnapshot::Type type = HeapSnapshot::kFull);
};
......
......@@ -4592,10 +4592,18 @@ Handle<String> HeapGraphNode::GetName() const {
uint64_t HeapGraphNode::GetId() const {
IsDeadCheck("v8::HeapGraphNode::GetId");
ASSERT(ToInternal(this)->snapshot()->type() != i::HeapSnapshot::kAggregated);
return ToInternal(this)->id();
}
int HeapGraphNode::GetInstancesCount() const {
IsDeadCheck("v8::HeapGraphNode::GetInstancesCount");
ASSERT(ToInternal(this)->snapshot()->type() == i::HeapSnapshot::kAggregated);
return static_cast<int>(ToInternal(this)->id());
}
int HeapGraphNode::GetSelfSize() const {
IsDeadCheck("v8::HeapGraphNode::GetSelfSize");
return ToInternal(this)->self_size();
......@@ -4677,6 +4685,12 @@ static i::HeapSnapshot* ToInternal(const HeapSnapshot* snapshot) {
}
HeapSnapshot::Type HeapSnapshot::GetType() const {
IsDeadCheck("v8::HeapSnapshot::GetType");
return static_cast<HeapSnapshot::Type>(ToInternal(this)->type());
}
unsigned HeapSnapshot::GetUid() const {
IsDeadCheck("v8::HeapSnapshot::GetUid");
return ToInternal(this)->uid();
......@@ -4724,10 +4738,22 @@ const HeapSnapshot* HeapProfiler::FindSnapshot(unsigned uid) {
}
const HeapSnapshot* HeapProfiler::TakeSnapshot(Handle<String> title) {
const HeapSnapshot* HeapProfiler::TakeSnapshot(Handle<String> title,
HeapSnapshot::Type type) {
IsDeadCheck("v8::HeapProfiler::TakeSnapshot");
i::HeapSnapshot::Type internal_type = i::HeapSnapshot::kFull;
switch (type) {
case HeapSnapshot::kFull:
internal_type = i::HeapSnapshot::kFull;
break;
case HeapSnapshot::kAggregated:
internal_type = i::HeapSnapshot::kAggregated;
break;
default:
UNREACHABLE();
}
return reinterpret_cast<const HeapSnapshot*>(
i::HeapProfiler::TakeSnapshot(*Utils::OpenHandle(*title)));
i::HeapProfiler::TakeSnapshot(*Utils::OpenHandle(*title), internal_type));
}
#endif // ENABLE_LOGGING_AND_PROFILING
......
This diff is collapsed.
......@@ -56,8 +56,8 @@ class HeapProfiler {
static void TearDown();
#ifdef ENABLE_LOGGING_AND_PROFILING
static HeapSnapshot* TakeSnapshot(const char* name);
static HeapSnapshot* TakeSnapshot(String* name);
static HeapSnapshot* TakeSnapshot(const char* name, int type);
static HeapSnapshot* TakeSnapshot(String* name, int type);
static int GetSnapshotsCount();
static HeapSnapshot* GetSnapshot(int index);
static HeapSnapshot* FindSnapshot(unsigned uid);
......@@ -75,12 +75,8 @@ class HeapProfiler {
private:
HeapProfiler();
~HeapProfiler();
HeapSnapshot* TakeSnapshotImpl(const char* name);
HeapSnapshot* TakeSnapshotImpl(String* name);
// Obsolete interface.
// Update the array info with stats from obj.
static void CollectStats(HeapObject* obj, HistogramInfo* info);
HeapSnapshot* TakeSnapshotImpl(const char* name, int type);
HeapSnapshot* TakeSnapshotImpl(String* name, int type);
HeapSnapshotsCollection* snapshots_;
unsigned next_snapshot_uid_;
......@@ -132,7 +128,9 @@ class JSObjectsCluster BASE_EMBEDDED {
bool is_null() const { return constructor_ == NULL; }
bool can_be_coarsed() const { return instance_ != NULL; }
String* constructor() const { return constructor_; }
Object* instance() const { return instance_; }
const char* GetSpecialCaseName() const;
void Print(StringStream* accumulator) const;
// Allows null clusters to be printed.
void DebugPrint(StringStream* accumulator) const;
......@@ -179,6 +177,9 @@ class ConstructorHeapProfile BASE_EMBEDDED {
virtual ~ConstructorHeapProfile() {}
void CollectStats(HeapObject* obj);
void PrintStats();
template<class Callback>
void ForEach(Callback* callback) { js_objects_info_tree_.ForEach(callback); }
// Used by ZoneSplayTree::ForEach. Made virtual to allow overriding in tests.
virtual void Call(const JSObjectsCluster& cluster,
const NumberAndSizeInfo& number_and_size);
......@@ -282,6 +283,8 @@ class ClustersCoarser BASE_EMBEDDED {
// "retainer profile" of JS objects allocated on heap.
// It is run during garbage collection cycle, thus it doesn't need
// to use handles.
class RetainerTreeAggregator;
class RetainerHeapProfile BASE_EMBEDDED {
public:
class Printer {
......@@ -292,7 +295,14 @@ class RetainerHeapProfile BASE_EMBEDDED {
};
RetainerHeapProfile();
~RetainerHeapProfile();
RetainerTreeAggregator* aggregator() { return aggregator_; }
ClustersCoarser* coarser() { return &coarser_; }
JSObjectsRetainerTree* retainers_tree() { return &retainers_tree_; }
void CollectStats(HeapObject* obj);
void CoarseAndAggregate();
void PrintStats();
void DebugPrintStats(Printer* printer);
void StoreReference(const JSObjectsCluster& cluster, HeapObject* ref);
......@@ -301,6 +311,44 @@ class RetainerHeapProfile BASE_EMBEDDED {
ZoneScope zscope_;
JSObjectsRetainerTree retainers_tree_;
ClustersCoarser coarser_;
RetainerTreeAggregator* aggregator_;
};
class AggregatedHeapSnapshot {
public:
AggregatedHeapSnapshot();
~AggregatedHeapSnapshot();
HistogramInfo* info() { return info_; }
ConstructorHeapProfile* js_cons_profile() { return &js_cons_profile_; }
RetainerHeapProfile* js_retainer_profile() { return &js_retainer_profile_; }
private:
HistogramInfo* info_;
ConstructorHeapProfile js_cons_profile_;
RetainerHeapProfile js_retainer_profile_;
};
class HeapEntriesMap;
class HeapSnapshot;
class AggregatedHeapSnapshotGenerator {
public:
explicit AggregatedHeapSnapshotGenerator(AggregatedHeapSnapshot* snapshot);
void GenerateSnapshot();
void FillHeapSnapshot(HeapSnapshot* snapshot);
static const int kAllStringsType = LAST_TYPE + 1;
private:
void CalculateStringsStats();
void CollectStats(HeapObject* obj);
template<class Iterator>
void IterateRetainers(HeapEntriesMap* entries_map);
AggregatedHeapSnapshot* agg_snapshot_;
};
......
......@@ -35,6 +35,16 @@
namespace v8 {
namespace internal {
const char* StringsStorage::GetFunctionName(String* name) {
return GetFunctionName(GetName(name));
}
const char* StringsStorage::GetFunctionName(const char* name) {
return strlen(name) > 0 ? name : ProfileGenerator::kAnonymousFunctionName;
}
CodeEntry::CodeEntry(int security_token_id)
: call_uid_(0),
tag_(Logger::FUNCTION_TAG),
......@@ -97,13 +107,21 @@ void CodeMap::DeleteCode(Address addr) {
}
const char* CpuProfilesCollection::GetFunctionName(String* name) {
return GetFunctionName(GetName(name));
}
const char* CpuProfilesCollection::GetFunctionName(const char* name) {
return strlen(name) > 0 ? name : ProfileGenerator::kAnonymousFunctionName;
template<class Visitor>
void HeapEntriesMap::UpdateEntries(Visitor* visitor) {
for (HashMap::Entry* p = entries_.Start();
p != NULL;
p = entries_.Next(p)) {
if (!IsAlias(p->value)) {
EntryInfo* entry_info = reinterpret_cast<EntryInfo*>(p->value);
entry_info->entry = visitor->GetEntry(
reinterpret_cast<HeapObject*>(p->key),
entry_info->children_count,
entry_info->retainers_count);
entry_info->children_count = 0;
entry_info->retainers_count = 0;
}
}
}
......
......@@ -823,13 +823,6 @@ HeapEntry* HeapGraphEdge::From() {
}
void HeapEntry::Init(HeapSnapshot* snapshot,
int children_count,
int retainers_count) {
Init(snapshot, kInternal, "", 0, 0, children_count, retainers_count);
}
void HeapEntry::Init(HeapSnapshot* snapshot,
Type type,
const char* name,
......@@ -1210,9 +1203,11 @@ template <> struct SnapshotSizeConstants<8> {
} // namespace
HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection,
HeapSnapshot::Type type,
const char* title,
unsigned uid)
: collection_(collection),
type_(type),
title_(title),
uid_(uid),
root_entry_index_(-1),
......@@ -1243,6 +1238,10 @@ void HeapSnapshot::AllocateEntries(int entries_count,
ASSERT(raw_entries_ == NULL);
raw_entries_ = NewArray<char>(
HeapEntry::EntriesSize(entries_count, children_count, retainers_count));
#ifdef DEBUG
raw_entries_size_ =
HeapEntry::EntriesSize(entries_count, children_count, retainers_count);
#endif
}
......@@ -1252,9 +1251,9 @@ HeapEntry* HeapSnapshot::AddEntry(HeapObject* object,
if (object == kInternalRootObject) {
ASSERT(root_entry_index_ == -1);
root_entry_index_ = entries_.length();
HeapEntry* entry = GetNextEntryToInit();
entry->Init(this, children_count, retainers_count);
return entry;
ASSERT(retainers_count == 0);
return AddEntry(
HeapEntry::kInternal, "", 0, 0, children_count, retainers_count);
} else if (object->IsJSFunction()) {
JSFunction* func = JSFunction::cast(object);
SharedFunctionInfo* shared = func->shared();
......@@ -1262,7 +1261,7 @@ HeapEntry* HeapSnapshot::AddEntry(HeapObject* object,
String::cast(shared->name()) : shared->inferred_name();
return AddEntry(object,
HeapEntry::kClosure,
collection_->GetName(name),
collection_->GetFunctionName(name),
children_count,
retainers_count);
} else if (object->IsJSObject()) {
......@@ -1290,7 +1289,7 @@ HeapEntry* HeapSnapshot::AddEntry(HeapObject* object,
String::cast(shared->name()) : shared->inferred_name();
return AddEntry(object,
HeapEntry::kCode,
collection_->GetName(name),
collection_->GetFunctionName(name),
children_count,
retainers_count);
} else if (object->IsScript()) {
......@@ -1345,14 +1344,23 @@ HeapEntry* HeapSnapshot::AddEntry(HeapObject* object,
const char* name,
int children_count,
int retainers_count) {
return AddEntry(type,
name,
collection_->GetObjectId(object->address()),
GetObjectSize(object),
children_count,
retainers_count);
}
HeapEntry* HeapSnapshot::AddEntry(HeapEntry::Type type,
const char* name,
uint64_t id,
int size,
int children_count,
int retainers_count) {
HeapEntry* entry = GetNextEntryToInit();
entry->Init(this,
type,
name,
collection_->GetObjectId(object->address()),
GetObjectSize(object),
children_count,
retainers_count);
entry->Init(this, type, name, id, size, children_count, retainers_count);
return entry;
}
......@@ -1365,6 +1373,8 @@ HeapEntry* HeapSnapshot::GetNextEntryToInit() {
} else {
entries_.Add(reinterpret_cast<HeapEntry*>(raw_entries_));
}
ASSERT(reinterpret_cast<char*>(entries_.last()) <
(raw_entries_ + raw_entries_size_));
return entries_.last();
}
......@@ -1534,10 +1544,11 @@ HeapSnapshotsCollection::~HeapSnapshotsCollection() {
}
HeapSnapshot* HeapSnapshotsCollection::NewSnapshot(const char* name,
HeapSnapshot* HeapSnapshotsCollection::NewSnapshot(HeapSnapshot::Type type,
const char* name,
unsigned uid) {
is_tracking_objects_ = true; // Start watching for heap objects moves.
HeapSnapshot* snapshot = new HeapSnapshot(this, name, uid);
HeapSnapshot* snapshot = new HeapSnapshot(this, type, name, uid);
snapshots_.Add(snapshot);
HashMap::Entry* entry =
snapshots_uids_.Lookup(reinterpret_cast<void*>(snapshot->uid()),
......@@ -1564,6 +1575,9 @@ HeapSnapshotsDiff* HeapSnapshotsCollection::CompareSnapshots(
}
HeapEntry *const HeapEntriesMap::kHeapEntryPlaceholder =
reinterpret_cast<HeapEntry*>(1);
HeapEntriesMap::HeapEntriesMap()
: entries_(HeapObjectsMatch),
entries_count_(0),
......@@ -1612,7 +1626,7 @@ void HeapEntriesMap::Pair(HeapObject* object, HeapEntry* entry) {
void HeapEntriesMap::CountReference(HeapObject* from, HeapObject* to,
int* prev_children_count,
int* prev_retainers_count) {
HashMap::Entry* from_cache_entry = entries_.Lookup(from, Hash(from), true);
HashMap::Entry* from_cache_entry = entries_.Lookup(from, Hash(from), false);
HashMap::Entry* to_cache_entry = entries_.Lookup(to, Hash(to), false);
ASSERT(from_cache_entry != NULL);
ASSERT(to_cache_entry != NULL);
......@@ -1631,42 +1645,19 @@ void HeapEntriesMap::CountReference(HeapObject* from, HeapObject* to,
}
template<class Visitor>
void HeapEntriesMap::UpdateEntries(Visitor* visitor) {
for (HashMap::Entry* p = entries_.Start();
p != NULL;
p = entries_.Next(p)) {
if (!IsAlias(p->value)) {
EntryInfo* entry_info = reinterpret_cast<EntryInfo*>(p->value);
entry_info->entry = visitor->GetEntry(
reinterpret_cast<HeapObject*>(p->key),
entry_info->children_count,
entry_info->retainers_count);
entry_info->children_count = 0;
entry_info->retainers_count = 0;
}
}
}
HeapSnapshotGenerator::HeapSnapshotGenerator(HeapSnapshot* snapshot)
: snapshot_(snapshot),
collection_(snapshot->collection()),
filler_(NULL) {
}
HeapEntry *const
HeapSnapshotGenerator::SnapshotFillerInterface::kHeapEntryPlaceholder =
reinterpret_cast<HeapEntry*>(1);
class SnapshotCounter : public HeapSnapshotGenerator::SnapshotFillerInterface {
public:
explicit SnapshotCounter(HeapEntriesMap* entries)
: entries_(entries) { }
HeapEntry* AddEntry(HeapObject* obj) {
entries_->Pair(obj, kHeapEntryPlaceholder);
return kHeapEntryPlaceholder;
entries_->Pair(obj, HeapEntriesMap::kHeapEntryPlaceholder);
return HeapEntriesMap::kHeapEntryPlaceholder;
}
void SetElementReference(HeapObject* parent_obj,
HeapEntry*,
......@@ -2057,10 +2048,12 @@ void HeapSnapshotGenerator::SetRootReference(Object* child_obj) {
void HeapSnapshotsDiff::CreateRoots(int additions_count, int deletions_count) {
raw_additions_root_ =
NewArray<char>(HeapEntry::EntriesSize(1, additions_count, 0));
additions_root()->Init(snapshot2_, additions_count, 0);
additions_root()->Init(
snapshot2_, HeapEntry::kInternal, "", 0, 0, additions_count, 0);
raw_deletions_root_ =
NewArray<char>(HeapEntry::EntriesSize(1, deletions_count, 0));
deletions_root()->Init(snapshot1_, deletions_count, 0);
deletions_root()->Init(
snapshot1_, HeapEntry::kInternal, "", 0, 0, deletions_count, 0);
}
......
......@@ -67,6 +67,8 @@ class StringsStorage {
~StringsStorage();
const char* GetName(String* name);
inline const char* GetFunctionName(String* name);
inline const char* GetFunctionName(const char* name);
private:
INLINE(static bool StringsMatch(void* key1, void* key2)) {
......@@ -298,9 +300,13 @@ class CpuProfilesCollection {
void AddPathToCurrentProfiles(const Vector<CodeEntry*>& path);
private:
INLINE(const char* GetFunctionName(String* name));
INLINE(const char* GetFunctionName(const char* name));
const char* GetName(int args_count);
const char* GetFunctionName(String* name) {
return function_and_resource_names_.GetFunctionName(name);
}
const char* GetFunctionName(const char* name) {
return function_and_resource_names_.GetFunctionName(name);
}
List<CpuProfile*>* GetProfilesList(int security_token_id);
int TokenToIndex(int security_token_id);
......@@ -498,7 +504,6 @@ class HeapEntry BASE_EMBEDDED {
};
HeapEntry() { }
void Init(HeapSnapshot* snapshot, int children_count, int retainers_count);
void Init(HeapSnapshot* snapshot,
Type type,
const char* name,
......@@ -640,12 +645,19 @@ class HeapSnapshotsDiff;
// HeapSnapshotGenerator fills in a HeapSnapshot.
class HeapSnapshot {
public:
enum Type {
kFull = v8::HeapSnapshot::kFull,
kAggregated = v8::HeapSnapshot::kAggregated
};
HeapSnapshot(HeapSnapshotsCollection* collection,
Type type,
const char* title,
unsigned uid);
~HeapSnapshot();
HeapSnapshotsCollection* collection() { return collection_; }
Type type() { return type_; }
const char* title() { return title_; }
unsigned uid() { return uid_; }
HeapEntry* root() { return entries_[root_entry_index_]; }
......@@ -655,6 +667,12 @@ class HeapSnapshot {
HeapEntry* AddEntry(
HeapObject* object, int children_count, int retainers_count);
bool WillAddEntry(HeapObject* object);
HeapEntry* AddEntry(HeapEntry::Type type,
const char* name,
uint64_t id,
int size,
int children_count,
int retainers_count);
int AddCalculatedData();
HeapEntryCalculatedData& GetCalculatedData(int index) {
return calculated_data_[index];
......@@ -681,6 +699,7 @@ class HeapSnapshot {
static int CalculateNetworkSize(JSObject* obj);
HeapSnapshotsCollection* collection_;
Type type_;
const char* title_;
unsigned uid_;
int root_entry_index_;
......@@ -688,6 +707,9 @@ class HeapSnapshot {
List<HeapEntry*> entries_;
bool entries_sorted_;
List<HeapEntryCalculatedData> calculated_data_;
#ifdef DEBUG
int raw_entries_size_;
#endif
friend class HeapSnapshotTester;
......@@ -792,12 +814,16 @@ class HeapSnapshotsCollection {
bool is_tracking_objects() { return is_tracking_objects_; }
HeapSnapshot* NewSnapshot(const char* name, unsigned uid);
HeapSnapshot* NewSnapshot(
HeapSnapshot::Type type, const char* name, unsigned uid);
void SnapshotGenerationFinished() { ids_.SnapshotGenerationFinished(); }
List<HeapSnapshot*>* snapshots() { return &snapshots_; }
HeapSnapshot* GetSnapshot(unsigned uid);
const char* GetName(String* name) { return names_.GetName(name); }
const char* GetFunctionName(String* name) {
return names_.GetFunctionName(name);
}
TokenEnumerator* token_enumerator() { return token_enumerator_; }
......@@ -848,6 +874,8 @@ class HeapEntriesMap {
int total_children_count() { return total_children_count_; }
int total_retainers_count() { return total_retainers_count_; }
static HeapEntry *const kHeapEntryPlaceholder;
private:
struct EntryInfo {
explicit EntryInfo(HeapEntry* entry)
......@@ -903,8 +931,6 @@ class HeapSnapshotGenerator {
HeapEntry* child_entry) = 0;
virtual void SetRootReference(Object* child_obj,
HeapEntry* child_entry) = 0;
static HeapEntry *const kHeapEntryPlaceholder;
};
explicit HeapSnapshotGenerator(HeapSnapshot* snapshot);
......
......@@ -372,6 +372,7 @@ TEST(RetainerProfile) {
i::HeapIterator iterator;
for (i::HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next())
ret_profile.CollectStats(obj);
ret_profile.CoarseAndAggregate();
RetainerProfilePrinter printer;
ret_profile.DebugPrintStats(&printer);
const char* retainers_of_a = printer.GetRetainers("A");
......@@ -650,6 +651,8 @@ TEST(HeapSnapshotCodeObjects) {
CompileAndRunScript(
"function lazy(x) { return x - 1; }\n"
"function compiled(x) { return x + 1; }\n"
"var inferred = function(x) { return x; }\n"
"var anonymous = (function() { return function() { return 0; } })();\n"
"compiled(1)");
const v8::HeapSnapshot* snapshot =
v8::HeapProfiler::TakeSnapshot(v8::String::New("code"));
......@@ -663,6 +666,18 @@ TEST(HeapSnapshotCodeObjects) {
GetProperty(global, v8::HeapGraphEdge::kProperty, "lazy");
CHECK_NE(NULL, lazy);
CHECK_EQ(v8::HeapGraphNode::kClosure, lazy->GetType());
const v8::HeapGraphNode* inferred =
GetProperty(global, v8::HeapGraphEdge::kProperty, "inferred");
CHECK_NE(NULL, inferred);
CHECK_EQ(v8::HeapGraphNode::kClosure, inferred->GetType());
v8::String::AsciiValue inferred_name(inferred->GetName());
CHECK_EQ("inferred", *inferred_name);
const v8::HeapGraphNode* anonymous =
GetProperty(global, v8::HeapGraphEdge::kProperty, "anonymous");
CHECK_NE(NULL, anonymous);
CHECK_EQ(v8::HeapGraphNode::kClosure, anonymous->GetType());
v8::String::AsciiValue anonymous_name(anonymous->GetName());
CHECK_EQ("(anonymous function)", *anonymous_name);
// Find references to code.
const v8::HeapGraphNode* compiled_code =
......@@ -864,4 +879,114 @@ TEST(Issue822) {
i::HeapSnapshotTester::CalculateNetworkSize(*jsobj);
}
static const v8::HeapGraphNode* GetChild(
const v8::HeapGraphNode* node,
v8::HeapGraphNode::Type type,
const char* name,
const v8::HeapGraphNode* after = NULL) {
bool ignore_child = after == NULL ? false : true;
for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
const v8::HeapGraphEdge* prop = node->GetChild(i);
const v8::HeapGraphNode* child = prop->GetToNode();
v8::String::AsciiValue child_name(child->GetName());
if (!ignore_child
&& child->GetType() == type
&& strcmp(name, *child_name) == 0)
return child;
if (after != NULL && child == after) ignore_child = false;
}
return NULL;
}
static bool IsNodeRetainedAs(const v8::HeapGraphNode* node,
int element) {
for (int i = 0, count = node->GetRetainersCount(); i < count; ++i) {
const v8::HeapGraphEdge* prop = node->GetRetainer(i);
if (prop->GetType() == v8::HeapGraphEdge::kElement
&& element == prop->GetName()->Int32Value())
return true;
}
return false;
}
TEST(AggregatedHeapSnapshot) {
v8::HandleScope scope;
LocalContext env;
CompileAndRunScript(
"function A() {}\n"
"function B(x) { this.x = x; }\n"
"var a = new A();\n"
"var b = new B(a);");
const v8::HeapSnapshot* snapshot =
v8::HeapProfiler::TakeSnapshot(
v8::String::New("agg"), v8::HeapSnapshot::kAggregated);
const v8::HeapGraphNode* strings = GetChild(snapshot->GetRoot(),
v8::HeapGraphNode::kInternal,
"STRING_TYPE");
CHECK_NE(NULL, strings);
CHECK_NE(0, strings->GetSelfSize());
CHECK_NE(0, strings->GetInstancesCount());
const v8::HeapGraphNode* maps = GetChild(snapshot->GetRoot(),
v8::HeapGraphNode::kInternal,
"MAP_TYPE");
CHECK_NE(NULL, maps);
CHECK_NE(0, maps->GetSelfSize());
CHECK_NE(0, maps->GetInstancesCount());
const v8::HeapGraphNode* a = GetChild(snapshot->GetRoot(),
v8::HeapGraphNode::kObject,
"A");
CHECK_NE(NULL, a);
CHECK_NE(0, a->GetSelfSize());
CHECK_EQ(1, a->GetInstancesCount());
const v8::HeapGraphNode* b = GetChild(snapshot->GetRoot(),
v8::HeapGraphNode::kObject,
"B");
CHECK_NE(NULL, b);
CHECK_NE(0, b->GetSelfSize());
CHECK_EQ(1, b->GetInstancesCount());
const v8::HeapGraphNode* glob_prop = GetChild(snapshot->GetRoot(),
v8::HeapGraphNode::kObject,
"(global property)",
b);
CHECK_NE(NULL, glob_prop);
CHECK_EQ(0, glob_prop->GetSelfSize());
CHECK_EQ(0, glob_prop->GetInstancesCount());
CHECK_NE(0, glob_prop->GetChildrenCount());
const v8::HeapGraphNode* a_from_glob_prop = GetChild(
glob_prop,
v8::HeapGraphNode::kObject,
"A");
CHECK_NE(NULL, a_from_glob_prop);
CHECK_EQ(0, a_from_glob_prop->GetSelfSize());
CHECK_EQ(0, a_from_glob_prop->GetInstancesCount());
CHECK_EQ(0, a_from_glob_prop->GetChildrenCount()); // Retains nothing.
CHECK(IsNodeRetainedAs(a_from_glob_prop, 1)); // (global propery) has 1 ref.
const v8::HeapGraphNode* b_with_children = GetChild(
snapshot->GetRoot(),
v8::HeapGraphNode::kObject,
"B",
b);
CHECK_NE(NULL, b_with_children);
CHECK_EQ(0, b_with_children->GetSelfSize());
CHECK_EQ(0, b_with_children->GetInstancesCount());
CHECK_NE(0, b_with_children->GetChildrenCount());
const v8::HeapGraphNode* a_from_b = GetChild(
b_with_children,
v8::HeapGraphNode::kObject,
"A");
CHECK_NE(NULL, a_from_b);
CHECK_EQ(0, a_from_b->GetSelfSize());
CHECK_EQ(0, a_from_b->GetInstancesCount());
CHECK_EQ(0, a_from_b->GetChildrenCount()); // Retains nothing.
CHECK(IsNodeRetainedAs(a_from_b, 1)); // B has 1 ref to A.
}
#endif // ENABLE_LOGGING_AND_PROFILING
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment