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 { ...@@ -260,10 +260,17 @@ class V8EXPORT HeapGraphNode {
/** /**
* Returns node id. For the same heap object, the id remains the same * 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; 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. */ /** Returns node's own size, in bytes. */
int GetSelfSize() const; int GetSelfSize() const;
...@@ -313,6 +320,15 @@ class V8EXPORT HeapSnapshotsDiff { ...@@ -313,6 +320,15 @@ class V8EXPORT HeapSnapshotsDiff {
*/ */
class V8EXPORT HeapSnapshot { class V8EXPORT HeapSnapshot {
public: 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.) */ /** Returns heap snapshot UID (assigned by the profiler.) */
unsigned GetUid() const; unsigned GetUid() const;
...@@ -322,7 +338,10 @@ class V8EXPORT HeapSnapshot { ...@@ -322,7 +338,10 @@ class V8EXPORT HeapSnapshot {
/** Returns the root node of the heap graph. */ /** Returns the root node of the heap graph. */
const HeapGraphNode* GetRoot() const; 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; const HeapSnapshotsDiff* CompareWith(const HeapSnapshot* snapshot) const;
}; };
...@@ -341,8 +360,13 @@ class V8EXPORT HeapProfiler { ...@@ -341,8 +360,13 @@ class V8EXPORT HeapProfiler {
/** Returns a profile by uid. */ /** Returns a profile by uid. */
static const HeapSnapshot* FindSnapshot(unsigned 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 { ...@@ -4592,10 +4592,18 @@ Handle<String> HeapGraphNode::GetName() const {
uint64_t HeapGraphNode::GetId() const { uint64_t HeapGraphNode::GetId() const {
IsDeadCheck("v8::HeapGraphNode::GetId"); IsDeadCheck("v8::HeapGraphNode::GetId");
ASSERT(ToInternal(this)->snapshot()->type() != i::HeapSnapshot::kAggregated);
return ToInternal(this)->id(); 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 { int HeapGraphNode::GetSelfSize() const {
IsDeadCheck("v8::HeapGraphNode::GetSelfSize"); IsDeadCheck("v8::HeapGraphNode::GetSelfSize");
return ToInternal(this)->self_size(); return ToInternal(this)->self_size();
...@@ -4677,6 +4685,12 @@ static i::HeapSnapshot* ToInternal(const HeapSnapshot* snapshot) { ...@@ -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 { unsigned HeapSnapshot::GetUid() const {
IsDeadCheck("v8::HeapSnapshot::GetUid"); IsDeadCheck("v8::HeapSnapshot::GetUid");
return ToInternal(this)->uid(); return ToInternal(this)->uid();
...@@ -4724,10 +4738,22 @@ const HeapSnapshot* HeapProfiler::FindSnapshot(unsigned 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"); 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*>( return reinterpret_cast<const HeapSnapshot*>(
i::HeapProfiler::TakeSnapshot(*Utils::OpenHandle(*title))); i::HeapProfiler::TakeSnapshot(*Utils::OpenHandle(*title), internal_type));
} }
#endif // ENABLE_LOGGING_AND_PROFILING #endif // ENABLE_LOGGING_AND_PROFILING
......
This diff is collapsed.
...@@ -56,8 +56,8 @@ class HeapProfiler { ...@@ -56,8 +56,8 @@ class HeapProfiler {
static void TearDown(); static void TearDown();
#ifdef ENABLE_LOGGING_AND_PROFILING #ifdef ENABLE_LOGGING_AND_PROFILING
static HeapSnapshot* TakeSnapshot(const char* name); static HeapSnapshot* TakeSnapshot(const char* name, int type);
static HeapSnapshot* TakeSnapshot(String* name); static HeapSnapshot* TakeSnapshot(String* name, int type);
static int GetSnapshotsCount(); static int GetSnapshotsCount();
static HeapSnapshot* GetSnapshot(int index); static HeapSnapshot* GetSnapshot(int index);
static HeapSnapshot* FindSnapshot(unsigned uid); static HeapSnapshot* FindSnapshot(unsigned uid);
...@@ -75,12 +75,8 @@ class HeapProfiler { ...@@ -75,12 +75,8 @@ class HeapProfiler {
private: private:
HeapProfiler(); HeapProfiler();
~HeapProfiler(); ~HeapProfiler();
HeapSnapshot* TakeSnapshotImpl(const char* name); HeapSnapshot* TakeSnapshotImpl(const char* name, int type);
HeapSnapshot* TakeSnapshotImpl(String* name); HeapSnapshot* TakeSnapshotImpl(String* name, int type);
// Obsolete interface.
// Update the array info with stats from obj.
static void CollectStats(HeapObject* obj, HistogramInfo* info);
HeapSnapshotsCollection* snapshots_; HeapSnapshotsCollection* snapshots_;
unsigned next_snapshot_uid_; unsigned next_snapshot_uid_;
...@@ -132,7 +128,9 @@ class JSObjectsCluster BASE_EMBEDDED { ...@@ -132,7 +128,9 @@ class JSObjectsCluster BASE_EMBEDDED {
bool is_null() const { return constructor_ == NULL; } bool is_null() const { return constructor_ == NULL; }
bool can_be_coarsed() const { return instance_ != NULL; } bool can_be_coarsed() const { return instance_ != NULL; }
String* constructor() const { return constructor_; } String* constructor() const { return constructor_; }
Object* instance() const { return instance_; }
const char* GetSpecialCaseName() const;
void Print(StringStream* accumulator) const; void Print(StringStream* accumulator) const;
// Allows null clusters to be printed. // Allows null clusters to be printed.
void DebugPrint(StringStream* accumulator) const; void DebugPrint(StringStream* accumulator) const;
...@@ -179,6 +177,9 @@ class ConstructorHeapProfile BASE_EMBEDDED { ...@@ -179,6 +177,9 @@ class ConstructorHeapProfile BASE_EMBEDDED {
virtual ~ConstructorHeapProfile() {} virtual ~ConstructorHeapProfile() {}
void CollectStats(HeapObject* obj); void CollectStats(HeapObject* obj);
void PrintStats(); 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. // Used by ZoneSplayTree::ForEach. Made virtual to allow overriding in tests.
virtual void Call(const JSObjectsCluster& cluster, virtual void Call(const JSObjectsCluster& cluster,
const NumberAndSizeInfo& number_and_size); const NumberAndSizeInfo& number_and_size);
...@@ -282,6 +283,8 @@ class ClustersCoarser BASE_EMBEDDED { ...@@ -282,6 +283,8 @@ class ClustersCoarser BASE_EMBEDDED {
// "retainer profile" of JS objects allocated on heap. // "retainer profile" of JS objects allocated on heap.
// It is run during garbage collection cycle, thus it doesn't need // It is run during garbage collection cycle, thus it doesn't need
// to use handles. // to use handles.
class RetainerTreeAggregator;
class RetainerHeapProfile BASE_EMBEDDED { class RetainerHeapProfile BASE_EMBEDDED {
public: public:
class Printer { class Printer {
...@@ -292,7 +295,14 @@ class RetainerHeapProfile BASE_EMBEDDED { ...@@ -292,7 +295,14 @@ class RetainerHeapProfile BASE_EMBEDDED {
}; };
RetainerHeapProfile(); RetainerHeapProfile();
~RetainerHeapProfile();
RetainerTreeAggregator* aggregator() { return aggregator_; }
ClustersCoarser* coarser() { return &coarser_; }
JSObjectsRetainerTree* retainers_tree() { return &retainers_tree_; }
void CollectStats(HeapObject* obj); void CollectStats(HeapObject* obj);
void CoarseAndAggregate();
void PrintStats(); void PrintStats();
void DebugPrintStats(Printer* printer); void DebugPrintStats(Printer* printer);
void StoreReference(const JSObjectsCluster& cluster, HeapObject* ref); void StoreReference(const JSObjectsCluster& cluster, HeapObject* ref);
...@@ -301,6 +311,44 @@ class RetainerHeapProfile BASE_EMBEDDED { ...@@ -301,6 +311,44 @@ class RetainerHeapProfile BASE_EMBEDDED {
ZoneScope zscope_; ZoneScope zscope_;
JSObjectsRetainerTree retainers_tree_; JSObjectsRetainerTree retainers_tree_;
ClustersCoarser coarser_; 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 @@ ...@@ -35,6 +35,16 @@
namespace v8 { namespace v8 {
namespace internal { 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) CodeEntry::CodeEntry(int security_token_id)
: call_uid_(0), : call_uid_(0),
tag_(Logger::FUNCTION_TAG), tag_(Logger::FUNCTION_TAG),
...@@ -97,13 +107,21 @@ void CodeMap::DeleteCode(Address addr) { ...@@ -97,13 +107,21 @@ void CodeMap::DeleteCode(Address addr) {
} }
const char* CpuProfilesCollection::GetFunctionName(String* name) { template<class Visitor>
return GetFunctionName(GetName(name)); void HeapEntriesMap::UpdateEntries(Visitor* visitor) {
} for (HashMap::Entry* p = entries_.Start();
p != NULL;
p = entries_.Next(p)) {
const char* CpuProfilesCollection::GetFunctionName(const char* name) { if (!IsAlias(p->value)) {
return strlen(name) > 0 ? name : ProfileGenerator::kAnonymousFunctionName; 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() { ...@@ -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, void HeapEntry::Init(HeapSnapshot* snapshot,
Type type, Type type,
const char* name, const char* name,
...@@ -1210,9 +1203,11 @@ template <> struct SnapshotSizeConstants<8> { ...@@ -1210,9 +1203,11 @@ template <> struct SnapshotSizeConstants<8> {
} // namespace } // namespace
HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection, HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection,
HeapSnapshot::Type type,
const char* title, const char* title,
unsigned uid) unsigned uid)
: collection_(collection), : collection_(collection),
type_(type),
title_(title), title_(title),
uid_(uid), uid_(uid),
root_entry_index_(-1), root_entry_index_(-1),
...@@ -1243,6 +1238,10 @@ void HeapSnapshot::AllocateEntries(int entries_count, ...@@ -1243,6 +1238,10 @@ void HeapSnapshot::AllocateEntries(int entries_count,
ASSERT(raw_entries_ == NULL); ASSERT(raw_entries_ == NULL);
raw_entries_ = NewArray<char>( raw_entries_ = NewArray<char>(
HeapEntry::EntriesSize(entries_count, children_count, retainers_count)); 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, ...@@ -1252,9 +1251,9 @@ HeapEntry* HeapSnapshot::AddEntry(HeapObject* object,
if (object == kInternalRootObject) { if (object == kInternalRootObject) {
ASSERT(root_entry_index_ == -1); ASSERT(root_entry_index_ == -1);
root_entry_index_ = entries_.length(); root_entry_index_ = entries_.length();
HeapEntry* entry = GetNextEntryToInit(); ASSERT(retainers_count == 0);
entry->Init(this, children_count, retainers_count); return AddEntry(
return entry; HeapEntry::kInternal, "", 0, 0, children_count, retainers_count);
} else if (object->IsJSFunction()) { } else if (object->IsJSFunction()) {
JSFunction* func = JSFunction::cast(object); JSFunction* func = JSFunction::cast(object);
SharedFunctionInfo* shared = func->shared(); SharedFunctionInfo* shared = func->shared();
...@@ -1262,7 +1261,7 @@ HeapEntry* HeapSnapshot::AddEntry(HeapObject* object, ...@@ -1262,7 +1261,7 @@ HeapEntry* HeapSnapshot::AddEntry(HeapObject* object,
String::cast(shared->name()) : shared->inferred_name(); String::cast(shared->name()) : shared->inferred_name();
return AddEntry(object, return AddEntry(object,
HeapEntry::kClosure, HeapEntry::kClosure,
collection_->GetName(name), collection_->GetFunctionName(name),
children_count, children_count,
retainers_count); retainers_count);
} else if (object->IsJSObject()) { } else if (object->IsJSObject()) {
...@@ -1290,7 +1289,7 @@ HeapEntry* HeapSnapshot::AddEntry(HeapObject* object, ...@@ -1290,7 +1289,7 @@ HeapEntry* HeapSnapshot::AddEntry(HeapObject* object,
String::cast(shared->name()) : shared->inferred_name(); String::cast(shared->name()) : shared->inferred_name();
return AddEntry(object, return AddEntry(object,
HeapEntry::kCode, HeapEntry::kCode,
collection_->GetName(name), collection_->GetFunctionName(name),
children_count, children_count,
retainers_count); retainers_count);
} else if (object->IsScript()) { } else if (object->IsScript()) {
...@@ -1345,14 +1344,23 @@ HeapEntry* HeapSnapshot::AddEntry(HeapObject* object, ...@@ -1345,14 +1344,23 @@ HeapEntry* HeapSnapshot::AddEntry(HeapObject* object,
const char* name, const char* name,
int children_count, int children_count,
int retainers_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(); HeapEntry* entry = GetNextEntryToInit();
entry->Init(this, entry->Init(this, type, name, id, size, children_count, retainers_count);
type,
name,
collection_->GetObjectId(object->address()),
GetObjectSize(object),
children_count,
retainers_count);
return entry; return entry;
} }
...@@ -1365,6 +1373,8 @@ HeapEntry* HeapSnapshot::GetNextEntryToInit() { ...@@ -1365,6 +1373,8 @@ HeapEntry* HeapSnapshot::GetNextEntryToInit() {
} else { } else {
entries_.Add(reinterpret_cast<HeapEntry*>(raw_entries_)); entries_.Add(reinterpret_cast<HeapEntry*>(raw_entries_));
} }
ASSERT(reinterpret_cast<char*>(entries_.last()) <
(raw_entries_ + raw_entries_size_));
return entries_.last(); return entries_.last();
} }
...@@ -1534,10 +1544,11 @@ HeapSnapshotsCollection::~HeapSnapshotsCollection() { ...@@ -1534,10 +1544,11 @@ HeapSnapshotsCollection::~HeapSnapshotsCollection() {
} }
HeapSnapshot* HeapSnapshotsCollection::NewSnapshot(const char* name, HeapSnapshot* HeapSnapshotsCollection::NewSnapshot(HeapSnapshot::Type type,
const char* name,
unsigned uid) { unsigned uid) {
is_tracking_objects_ = true; // Start watching for heap objects moves. 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); snapshots_.Add(snapshot);
HashMap::Entry* entry = HashMap::Entry* entry =
snapshots_uids_.Lookup(reinterpret_cast<void*>(snapshot->uid()), snapshots_uids_.Lookup(reinterpret_cast<void*>(snapshot->uid()),
...@@ -1564,6 +1575,9 @@ HeapSnapshotsDiff* HeapSnapshotsCollection::CompareSnapshots( ...@@ -1564,6 +1575,9 @@ HeapSnapshotsDiff* HeapSnapshotsCollection::CompareSnapshots(
} }
HeapEntry *const HeapEntriesMap::kHeapEntryPlaceholder =
reinterpret_cast<HeapEntry*>(1);
HeapEntriesMap::HeapEntriesMap() HeapEntriesMap::HeapEntriesMap()
: entries_(HeapObjectsMatch), : entries_(HeapObjectsMatch),
entries_count_(0), entries_count_(0),
...@@ -1612,7 +1626,7 @@ void HeapEntriesMap::Pair(HeapObject* object, HeapEntry* entry) { ...@@ -1612,7 +1626,7 @@ void HeapEntriesMap::Pair(HeapObject* object, HeapEntry* entry) {
void HeapEntriesMap::CountReference(HeapObject* from, HeapObject* to, void HeapEntriesMap::CountReference(HeapObject* from, HeapObject* to,
int* prev_children_count, int* prev_children_count,
int* prev_retainers_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); HashMap::Entry* to_cache_entry = entries_.Lookup(to, Hash(to), false);
ASSERT(from_cache_entry != NULL); ASSERT(from_cache_entry != NULL);
ASSERT(to_cache_entry != NULL); ASSERT(to_cache_entry != NULL);
...@@ -1631,42 +1645,19 @@ void HeapEntriesMap::CountReference(HeapObject* from, HeapObject* to, ...@@ -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) HeapSnapshotGenerator::HeapSnapshotGenerator(HeapSnapshot* snapshot)
: snapshot_(snapshot), : snapshot_(snapshot),
collection_(snapshot->collection()), collection_(snapshot->collection()),
filler_(NULL) { filler_(NULL) {
} }
HeapEntry *const
HeapSnapshotGenerator::SnapshotFillerInterface::kHeapEntryPlaceholder =
reinterpret_cast<HeapEntry*>(1);
class SnapshotCounter : public HeapSnapshotGenerator::SnapshotFillerInterface { class SnapshotCounter : public HeapSnapshotGenerator::SnapshotFillerInterface {
public: public:
explicit SnapshotCounter(HeapEntriesMap* entries) explicit SnapshotCounter(HeapEntriesMap* entries)
: entries_(entries) { } : entries_(entries) { }
HeapEntry* AddEntry(HeapObject* obj) { HeapEntry* AddEntry(HeapObject* obj) {
entries_->Pair(obj, kHeapEntryPlaceholder); entries_->Pair(obj, HeapEntriesMap::kHeapEntryPlaceholder);
return kHeapEntryPlaceholder; return HeapEntriesMap::kHeapEntryPlaceholder;
} }
void SetElementReference(HeapObject* parent_obj, void SetElementReference(HeapObject* parent_obj,
HeapEntry*, HeapEntry*,
...@@ -2057,10 +2048,12 @@ void HeapSnapshotGenerator::SetRootReference(Object* child_obj) { ...@@ -2057,10 +2048,12 @@ void HeapSnapshotGenerator::SetRootReference(Object* child_obj) {
void HeapSnapshotsDiff::CreateRoots(int additions_count, int deletions_count) { void HeapSnapshotsDiff::CreateRoots(int additions_count, int deletions_count) {
raw_additions_root_ = raw_additions_root_ =
NewArray<char>(HeapEntry::EntriesSize(1, additions_count, 0)); 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_ = raw_deletions_root_ =
NewArray<char>(HeapEntry::EntriesSize(1, deletions_count, 0)); 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 { ...@@ -67,6 +67,8 @@ class StringsStorage {
~StringsStorage(); ~StringsStorage();
const char* GetName(String* name); const char* GetName(String* name);
inline const char* GetFunctionName(String* name);
inline const char* GetFunctionName(const char* name);
private: private:
INLINE(static bool StringsMatch(void* key1, void* key2)) { INLINE(static bool StringsMatch(void* key1, void* key2)) {
...@@ -298,9 +300,13 @@ class CpuProfilesCollection { ...@@ -298,9 +300,13 @@ class CpuProfilesCollection {
void AddPathToCurrentProfiles(const Vector<CodeEntry*>& path); void AddPathToCurrentProfiles(const Vector<CodeEntry*>& path);
private: private:
INLINE(const char* GetFunctionName(String* name));
INLINE(const char* GetFunctionName(const char* name));
const char* GetName(int args_count); 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); List<CpuProfile*>* GetProfilesList(int security_token_id);
int TokenToIndex(int security_token_id); int TokenToIndex(int security_token_id);
...@@ -498,7 +504,6 @@ class HeapEntry BASE_EMBEDDED { ...@@ -498,7 +504,6 @@ class HeapEntry BASE_EMBEDDED {
}; };
HeapEntry() { } HeapEntry() { }
void Init(HeapSnapshot* snapshot, int children_count, int retainers_count);
void Init(HeapSnapshot* snapshot, void Init(HeapSnapshot* snapshot,
Type type, Type type,
const char* name, const char* name,
...@@ -640,12 +645,19 @@ class HeapSnapshotsDiff; ...@@ -640,12 +645,19 @@ class HeapSnapshotsDiff;
// HeapSnapshotGenerator fills in a HeapSnapshot. // HeapSnapshotGenerator fills in a HeapSnapshot.
class HeapSnapshot { class HeapSnapshot {
public: public:
enum Type {
kFull = v8::HeapSnapshot::kFull,
kAggregated = v8::HeapSnapshot::kAggregated
};
HeapSnapshot(HeapSnapshotsCollection* collection, HeapSnapshot(HeapSnapshotsCollection* collection,
Type type,
const char* title, const char* title,
unsigned uid); unsigned uid);
~HeapSnapshot(); ~HeapSnapshot();
HeapSnapshotsCollection* collection() { return collection_; } HeapSnapshotsCollection* collection() { return collection_; }
Type type() { return type_; }
const char* title() { return title_; } const char* title() { return title_; }
unsigned uid() { return uid_; } unsigned uid() { return uid_; }
HeapEntry* root() { return entries_[root_entry_index_]; } HeapEntry* root() { return entries_[root_entry_index_]; }
...@@ -655,6 +667,12 @@ class HeapSnapshot { ...@@ -655,6 +667,12 @@ class HeapSnapshot {
HeapEntry* AddEntry( HeapEntry* AddEntry(
HeapObject* object, int children_count, int retainers_count); HeapObject* object, int children_count, int retainers_count);
bool WillAddEntry(HeapObject* object); 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(); int AddCalculatedData();
HeapEntryCalculatedData& GetCalculatedData(int index) { HeapEntryCalculatedData& GetCalculatedData(int index) {
return calculated_data_[index]; return calculated_data_[index];
...@@ -681,6 +699,7 @@ class HeapSnapshot { ...@@ -681,6 +699,7 @@ class HeapSnapshot {
static int CalculateNetworkSize(JSObject* obj); static int CalculateNetworkSize(JSObject* obj);
HeapSnapshotsCollection* collection_; HeapSnapshotsCollection* collection_;
Type type_;
const char* title_; const char* title_;
unsigned uid_; unsigned uid_;
int root_entry_index_; int root_entry_index_;
...@@ -688,6 +707,9 @@ class HeapSnapshot { ...@@ -688,6 +707,9 @@ class HeapSnapshot {
List<HeapEntry*> entries_; List<HeapEntry*> entries_;
bool entries_sorted_; bool entries_sorted_;
List<HeapEntryCalculatedData> calculated_data_; List<HeapEntryCalculatedData> calculated_data_;
#ifdef DEBUG
int raw_entries_size_;
#endif
friend class HeapSnapshotTester; friend class HeapSnapshotTester;
...@@ -792,12 +814,16 @@ class HeapSnapshotsCollection { ...@@ -792,12 +814,16 @@ class HeapSnapshotsCollection {
bool is_tracking_objects() { return is_tracking_objects_; } 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(); } void SnapshotGenerationFinished() { ids_.SnapshotGenerationFinished(); }
List<HeapSnapshot*>* snapshots() { return &snapshots_; } List<HeapSnapshot*>* snapshots() { return &snapshots_; }
HeapSnapshot* GetSnapshot(unsigned uid); HeapSnapshot* GetSnapshot(unsigned uid);
const char* GetName(String* name) { return names_.GetName(name); } const char* GetName(String* name) { return names_.GetName(name); }
const char* GetFunctionName(String* name) {
return names_.GetFunctionName(name);
}
TokenEnumerator* token_enumerator() { return token_enumerator_; } TokenEnumerator* token_enumerator() { return token_enumerator_; }
...@@ -848,6 +874,8 @@ class HeapEntriesMap { ...@@ -848,6 +874,8 @@ class HeapEntriesMap {
int total_children_count() { return total_children_count_; } int total_children_count() { return total_children_count_; }
int total_retainers_count() { return total_retainers_count_; } int total_retainers_count() { return total_retainers_count_; }
static HeapEntry *const kHeapEntryPlaceholder;
private: private:
struct EntryInfo { struct EntryInfo {
explicit EntryInfo(HeapEntry* entry) explicit EntryInfo(HeapEntry* entry)
...@@ -903,8 +931,6 @@ class HeapSnapshotGenerator { ...@@ -903,8 +931,6 @@ class HeapSnapshotGenerator {
HeapEntry* child_entry) = 0; HeapEntry* child_entry) = 0;
virtual void SetRootReference(Object* child_obj, virtual void SetRootReference(Object* child_obj,
HeapEntry* child_entry) = 0; HeapEntry* child_entry) = 0;
static HeapEntry *const kHeapEntryPlaceholder;
}; };
explicit HeapSnapshotGenerator(HeapSnapshot* snapshot); explicit HeapSnapshotGenerator(HeapSnapshot* snapshot);
......
...@@ -372,6 +372,7 @@ TEST(RetainerProfile) { ...@@ -372,6 +372,7 @@ TEST(RetainerProfile) {
i::HeapIterator iterator; i::HeapIterator iterator;
for (i::HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) for (i::HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next())
ret_profile.CollectStats(obj); ret_profile.CollectStats(obj);
ret_profile.CoarseAndAggregate();
RetainerProfilePrinter printer; RetainerProfilePrinter printer;
ret_profile.DebugPrintStats(&printer); ret_profile.DebugPrintStats(&printer);
const char* retainers_of_a = printer.GetRetainers("A"); const char* retainers_of_a = printer.GetRetainers("A");
...@@ -650,6 +651,8 @@ TEST(HeapSnapshotCodeObjects) { ...@@ -650,6 +651,8 @@ TEST(HeapSnapshotCodeObjects) {
CompileAndRunScript( CompileAndRunScript(
"function lazy(x) { return x - 1; }\n" "function lazy(x) { return x - 1; }\n"
"function compiled(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)"); "compiled(1)");
const v8::HeapSnapshot* snapshot = const v8::HeapSnapshot* snapshot =
v8::HeapProfiler::TakeSnapshot(v8::String::New("code")); v8::HeapProfiler::TakeSnapshot(v8::String::New("code"));
...@@ -663,6 +666,18 @@ TEST(HeapSnapshotCodeObjects) { ...@@ -663,6 +666,18 @@ TEST(HeapSnapshotCodeObjects) {
GetProperty(global, v8::HeapGraphEdge::kProperty, "lazy"); GetProperty(global, v8::HeapGraphEdge::kProperty, "lazy");
CHECK_NE(NULL, lazy); CHECK_NE(NULL, lazy);
CHECK_EQ(v8::HeapGraphNode::kClosure, lazy->GetType()); 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. // Find references to code.
const v8::HeapGraphNode* compiled_code = const v8::HeapGraphNode* compiled_code =
...@@ -864,4 +879,114 @@ TEST(Issue822) { ...@@ -864,4 +879,114 @@ TEST(Issue822) {
i::HeapSnapshotTester::CalculateNetworkSize(*jsobj); 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 #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