New heap profiler: implement fast retaining sizes approximation.

Approximation is done by building a dominators tree for the heap graph.
Dominator nodes and retained sizes are serialized into JSON.

Removed:
 - reachable size (it is useless, after all);
 - HeapEntryCalculatedData (size is now stored in the node, retaining
   paths in a hash map);

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5867 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 1e8413e1
...@@ -282,16 +282,19 @@ class V8EXPORT HeapGraphNode { ...@@ -282,16 +282,19 @@ class V8EXPORT HeapGraphNode {
/** Returns node's own size, in bytes. */ /** Returns node's own size, in bytes. */
int GetSelfSize() const; int GetSelfSize() const;
/** Returns node's network (self + reachable nodes) size, in bytes. */
int GetReachableSize() const;
/** /**
* Returns node's retained size, in bytes. That is, self + sizes of * Returns node's retained size, in bytes. That is, self + sizes of
* the objects that are reachable only from this object. In other * the objects that are reachable only from this object. In other
* words, the size of memory that will be reclaimed having this node * words, the size of memory that will be reclaimed having this node
* collected. * collected.
*
* Exact retained size calculation has O(N) (number of nodes)
* computational complexity, while approximate has O(1). It is
* assumed that initially heap profiling tools provide approximate
* sizes for all nodes, and then exact sizes are calculated for the
* most 'interesting' nodes.
*/ */
int GetRetainedSize() const; int GetRetainedSize(bool exact) const;
/** Returns child nodes count of the node. */ /** Returns child nodes count of the node. */
int GetChildrenCount() const; int GetChildrenCount() const;
...@@ -310,6 +313,12 @@ class V8EXPORT HeapGraphNode { ...@@ -310,6 +313,12 @@ class V8EXPORT HeapGraphNode {
/** Returns a retaining path by index. */ /** Returns a retaining path by index. */
const HeapGraphPath* GetRetainingPath(int index) const; const HeapGraphPath* GetRetainingPath(int index) const;
/**
* Returns a dominator node. This is the node that participates in every
* path from the snapshot root to the current node.
*/
const HeapGraphNode* GetDominatorNode() const;
}; };
......
...@@ -4763,15 +4763,9 @@ int HeapGraphNode::GetSelfSize() const { ...@@ -4763,15 +4763,9 @@ int HeapGraphNode::GetSelfSize() const {
} }
int HeapGraphNode::GetReachableSize() const { int HeapGraphNode::GetRetainedSize(bool exact) const {
IsDeadCheck("v8::HeapSnapshot::GetReachableSize");
return ToInternal(this)->ReachableSize();
}
int HeapGraphNode::GetRetainedSize() const {
IsDeadCheck("v8::HeapSnapshot::GetRetainedSize"); IsDeadCheck("v8::HeapSnapshot::GetRetainedSize");
return ToInternal(this)->RetainedSize(); return ToInternal(this)->RetainedSize(exact);
} }
...@@ -4814,6 +4808,12 @@ const HeapGraphPath* HeapGraphNode::GetRetainingPath(int index) const { ...@@ -4814,6 +4808,12 @@ const HeapGraphPath* HeapGraphNode::GetRetainingPath(int index) const {
} }
const HeapGraphNode* HeapGraphNode::GetDominatorNode() const {
IsDeadCheck("v8::HeapSnapshot::GetDominatorNode");
return reinterpret_cast<const HeapGraphNode*>(ToInternal(this)->dominator());
}
const HeapGraphNode* HeapSnapshotsDiff::GetAdditionsRoot() const { const HeapGraphNode* HeapSnapshotsDiff::GetAdditionsRoot() const {
IsDeadCheck("v8::HeapSnapshotsDiff::GetAdditionsRoot"); IsDeadCheck("v8::HeapSnapshotsDiff::GetAdditionsRoot");
i::HeapSnapshotsDiff* diff = i::HeapSnapshotsDiff* diff =
......
...@@ -105,22 +105,6 @@ void CodeMap::DeleteCode(Address addr) { ...@@ -105,22 +105,6 @@ void CodeMap::DeleteCode(Address addr) {
} }
template<class Visitor>
void HeapEntriesMap::UpdateEntries(Visitor* visitor) {
for (HashMap::Entry* p = entries_.Start();
p != NULL;
p = entries_.Next(p)) {
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;
}
}
CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) { CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) {
switch (tag) { switch (tag) {
case GC: case GC:
...@@ -137,6 +121,22 @@ CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) { ...@@ -137,6 +121,22 @@ CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) {
} }
} }
template<class Visitor>
void HeapEntriesMap::UpdateEntries(Visitor* visitor) {
for (HashMap::Entry* p = entries_.Start();
p != NULL;
p = entries_.Next(p)) {
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;
}
}
} } // namespace v8::internal } } // namespace v8::internal
#endif // ENABLE_LOGGING_AND_PROFILING #endif // ENABLE_LOGGING_AND_PROFILING
......
...@@ -869,12 +869,13 @@ void HeapEntry::Init(HeapSnapshot* snapshot, ...@@ -869,12 +869,13 @@ void HeapEntry::Init(HeapSnapshot* snapshot,
snapshot_ = snapshot; snapshot_ = snapshot;
type_ = type; type_ = type;
painted_ = kUnpainted; painted_ = kUnpainted;
calculated_data_index_ = kNoCalculatedData;
name_ = name; name_ = name;
id_ = id; id_ = id;
self_size_ = self_size; self_size_ = self_size;
retained_size_ = 0;
children_count_ = children_count; children_count_ = children_count;
retainers_count_ = retainers_count; retainers_count_ = retainers_count;
dominator_ = NULL;
} }
...@@ -904,30 +905,16 @@ void HeapEntry::SetUnidirElementReference( ...@@ -904,30 +905,16 @@ void HeapEntry::SetUnidirElementReference(
} }
int HeapEntry::ReachableSize() { int HeapEntry::RetainedSize(bool exact) {
if (calculated_data_index_ == kNoCalculatedData) { if (exact && (retained_size_ & kExactRetainedSizeTag) == 0) {
calculated_data_index_ = snapshot_->AddCalculatedData(); CalculateExactRetainedSize();
} }
return snapshot_->GetCalculatedData( return retained_size_ & (~kExactRetainedSizeTag);
calculated_data_index_).ReachableSize(this);
}
int HeapEntry::RetainedSize() {
if (calculated_data_index_ == kNoCalculatedData) {
calculated_data_index_ = snapshot_->AddCalculatedData();
}
return snapshot_->GetCalculatedData(
calculated_data_index_).RetainedSize(this);
} }
List<HeapGraphPath*>* HeapEntry::GetRetainingPaths() { List<HeapGraphPath*>* HeapEntry::GetRetainingPaths() {
if (calculated_data_index_ == kNoCalculatedData) { return snapshot_->GetRetainingPaths(this);
calculated_data_index_ = snapshot_->AddCalculatedData();
}
return snapshot_->GetCalculatedData(
calculated_data_index_).GetRetainingPaths(this);
} }
...@@ -965,8 +952,7 @@ void HeapEntry::PaintAllReachable() { ...@@ -965,8 +952,7 @@ void HeapEntry::PaintAllReachable() {
void HeapEntry::Print(int max_depth, int indent) { void HeapEntry::Print(int max_depth, int indent) {
OS::Print("%6d %6d %6d [%llu] ", OS::Print("%6d %6d [%llu] ", self_size(), RetainedSize(false), id_);
self_size(), ReachableSize(), RetainedSize(), id_);
if (type() != kString) { if (type() != kString) {
OS::Print("%s %.40s\n", TypeAsString(), name_); OS::Print("%s %.40s\n", TypeAsString(), name_);
} else { } else {
...@@ -1036,44 +1022,6 @@ int HeapEntry::EntriesSize(int entries_count, ...@@ -1036,44 +1022,6 @@ int HeapEntry::EntriesSize(int entries_count,
} }
static void DeleteHeapGraphPath(HeapGraphPath** path_ptr) {
delete *path_ptr;
}
void HeapEntryCalculatedData::Dispose() {
if (retaining_paths_ != NULL) retaining_paths_->Iterate(DeleteHeapGraphPath);
delete retaining_paths_;
}
int HeapEntryCalculatedData::ReachableSize(HeapEntry* entry) {
if (reachable_size_ == kUnknownSize) CalculateSizes(entry);
return reachable_size_;
}
int HeapEntryCalculatedData::RetainedSize(HeapEntry* entry) {
if (retained_size_ == kUnknownSize) CalculateSizes(entry);
return retained_size_;
}
class ReachableSizeCalculator {
public:
ReachableSizeCalculator()
: reachable_size_(0) {
}
int reachable_size() const { return reachable_size_; }
void Apply(HeapEntry* entry) {
reachable_size_ += entry->self_size();
}
private:
int reachable_size_;
};
class RetainedSizeCalculator { class RetainedSizeCalculator {
public: public:
RetainedSizeCalculator() RetainedSizeCalculator()
...@@ -1092,20 +1040,17 @@ class RetainedSizeCalculator { ...@@ -1092,20 +1040,17 @@ class RetainedSizeCalculator {
int retained_size_; int retained_size_;
}; };
void HeapEntryCalculatedData::CalculateSizes(HeapEntry* entry) { void HeapEntry::CalculateExactRetainedSize() {
// To calculate retained size, first we paint all reachable nodes in // To calculate retained size, first we paint all reachable nodes in
// one color (and calculate reachable size as a byproduct), then we // one color, then we paint (or re-paint) all nodes reachable from
// paint (or re-paint) all nodes reachable from other nodes with a // other nodes with a different color. Then we sum up self sizes of
// different color. Then we consider only nodes painted with the // nodes painted with the first color.
// first color for calculating the retained size. snapshot()->ClearPaint();
entry->snapshot()->ClearPaint(); PaintAllReachable();
ReachableSizeCalculator rch_size_calc;
entry->ApplyAndPaintAllReachable(&rch_size_calc);
reachable_size_ = rch_size_calc.reachable_size();
List<HeapEntry*> list(10); List<HeapEntry*> list(10);
HeapEntry* root = entry->snapshot()->root(); HeapEntry* root = snapshot()->root();
if (entry != root) { if (this != root) {
list.Add(root); list.Add(root);
root->paint_reachable_from_others(); root->paint_reachable_from_others();
} }
...@@ -1115,7 +1060,7 @@ void HeapEntryCalculatedData::CalculateSizes(HeapEntry* entry) { ...@@ -1115,7 +1060,7 @@ void HeapEntryCalculatedData::CalculateSizes(HeapEntry* entry) {
for (int i = 0; i < children.length(); ++i) { for (int i = 0; i < children.length(); ++i) {
if (children[i].type() == HeapGraphEdge::kShortcut) continue; if (children[i].type() == HeapGraphEdge::kShortcut) continue;
HeapEntry* child = children[i].to(); HeapEntry* child = children[i].to();
if (child != entry && child->not_painted_reachable_from_others()) { if (child != this && child->not_painted_reachable_from_others()) {
list.Add(child); list.Add(child);
child->paint_reachable_from_others(); child->paint_reachable_from_others();
} }
...@@ -1123,8 +1068,10 @@ void HeapEntryCalculatedData::CalculateSizes(HeapEntry* entry) { ...@@ -1123,8 +1068,10 @@ void HeapEntryCalculatedData::CalculateSizes(HeapEntry* entry) {
} }
RetainedSizeCalculator ret_size_calc; RetainedSizeCalculator ret_size_calc;
entry->snapshot()->IterateEntries(&ret_size_calc); snapshot()->IterateEntries(&ret_size_calc);
retained_size_ = ret_size_calc.reained_size(); retained_size_ = ret_size_calc.reained_size();
ASSERT((retained_size_ & kExactRetainedSizeTag) == 0);
retained_size_ |= kExactRetainedSizeTag;
} }
...@@ -1162,32 +1109,28 @@ class CachedHeapGraphPath { ...@@ -1162,32 +1109,28 @@ class CachedHeapGraphPath {
}; };
List<HeapGraphPath*>* HeapEntryCalculatedData::GetRetainingPaths( List<HeapGraphPath*>* HeapEntry::CalculateRetainingPaths() {
HeapEntry* entry) { List<HeapGraphPath*>* retaining_paths = new List<HeapGraphPath*>(4);
if (retaining_paths_ == NULL) retaining_paths_ = new List<HeapGraphPath*>(4); CachedHeapGraphPath path;
if (retaining_paths_->length() == 0 && entry->retainers().length() != 0) { FindRetainingPaths(&path, retaining_paths);
CachedHeapGraphPath path; return retaining_paths;
FindRetainingPaths(entry, &path);
}
return retaining_paths_;
} }
void HeapEntryCalculatedData::FindRetainingPaths( void HeapEntry::FindRetainingPaths(CachedHeapGraphPath* prev_path,
HeapEntry* entry, List<HeapGraphPath*>* retaining_paths) {
CachedHeapGraphPath* prev_path) { Vector<HeapGraphEdge*> rets = retainers();
Vector<HeapGraphEdge*> retainers = entry->retainers(); for (int i = 0; i < rets.length(); ++i) {
for (int i = 0; i < retainers.length(); ++i) { HeapGraphEdge* ret_edge = rets[i];
HeapGraphEdge* ret_edge = retainers[i];
if (prev_path->ContainsNode(ret_edge->From())) continue; if (prev_path->ContainsNode(ret_edge->From())) continue;
if (ret_edge->From() != entry->snapshot()->root()) { if (ret_edge->From() != snapshot()->root()) {
CachedHeapGraphPath path(*prev_path); CachedHeapGraphPath path(*prev_path);
path.Add(ret_edge); path.Add(ret_edge);
FindRetainingPaths(ret_edge->From(), &path); ret_edge->From()->FindRetainingPaths(&path, retaining_paths);
} else { } else {
HeapGraphPath* ret_path = new HeapGraphPath(*prev_path->path()); HeapGraphPath* ret_path = new HeapGraphPath(*prev_path->path());
ret_path->Set(0, ret_edge); ret_path->Set(0, ret_edge);
retaining_paths_->Add(ret_path); retaining_paths->Add(ret_path);
} }
} }
} }
...@@ -1247,12 +1190,12 @@ template <size_t ptr_size> struct SnapshotSizeConstants; ...@@ -1247,12 +1190,12 @@ template <size_t ptr_size> struct SnapshotSizeConstants;
template <> struct SnapshotSizeConstants<4> { template <> struct SnapshotSizeConstants<4> {
static const int kExpectedHeapGraphEdgeSize = 12; static const int kExpectedHeapGraphEdgeSize = 12;
static const int kExpectedHeapEntrySize = 32; static const int kExpectedHeapEntrySize = 36;
}; };
template <> struct SnapshotSizeConstants<8> { template <> struct SnapshotSizeConstants<8> {
static const int kExpectedHeapGraphEdgeSize = 24; static const int kExpectedHeapGraphEdgeSize = 24;
static const int kExpectedHeapEntrySize = 40; static const int kExpectedHeapEntrySize = 48;
}; };
} // namespace } // namespace
...@@ -1268,7 +1211,8 @@ HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection, ...@@ -1268,7 +1211,8 @@ HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection,
root_entry_(NULL), root_entry_(NULL),
gc_roots_entry_(NULL), gc_roots_entry_(NULL),
raw_entries_(NULL), raw_entries_(NULL),
entries_sorted_(false) { entries_sorted_(false),
retaining_paths_(HeapEntry::Match) {
STATIC_ASSERT( STATIC_ASSERT(
sizeof(HeapGraphEdge) == sizeof(HeapGraphEdge) ==
SnapshotSizeConstants<sizeof(void*)>::kExpectedHeapGraphEdgeSize); // NOLINT SnapshotSizeConstants<sizeof(void*)>::kExpectedHeapGraphEdgeSize); // NOLINT
...@@ -1278,13 +1222,20 @@ HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection, ...@@ -1278,13 +1222,20 @@ HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection,
} }
static void DisposeCalculatedData(HeapEntryCalculatedData* cdata) { static void DeleteHeapGraphPath(HeapGraphPath** path_ptr) {
cdata->Dispose(); delete *path_ptr;
} }
HeapSnapshot::~HeapSnapshot() { HeapSnapshot::~HeapSnapshot() {
DeleteArray(raw_entries_); DeleteArray(raw_entries_);
calculated_data_.Iterate(DisposeCalculatedData); 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;
}
} }
...@@ -1400,12 +1351,6 @@ void HeapSnapshot::ClearPaint() { ...@@ -1400,12 +1351,6 @@ void HeapSnapshot::ClearPaint() {
} }
int HeapSnapshot::AddCalculatedData() {
calculated_data_.Add(HeapEntryCalculatedData());
return calculated_data_.length() - 1;
}
HeapEntry* HeapSnapshot::AddEntry(HeapObject* object, HeapEntry* HeapSnapshot::AddEntry(HeapObject* object,
HeapEntry::Type type, HeapEntry::Type type,
const char* name, const char* name,
...@@ -1432,6 +1377,144 @@ HeapEntry* HeapSnapshot::AddEntry(HeapEntry::Type type, ...@@ -1432,6 +1377,144 @@ HeapEntry* HeapSnapshot::AddEntry(HeapEntry::Type type,
} }
void HeapSnapshot::FillReversePostorderIndexes(Vector<HeapEntry*>* entries) {
ClearPaint();
int current_entry = 0;
List<HeapEntry*> nodes_to_visit;
nodes_to_visit.Add(root());
root()->paint_reachable();
while (!nodes_to_visit.is_empty()) {
HeapEntry* entry = nodes_to_visit.last();
Vector<HeapGraphEdge> children = entry->children();
bool has_new_edges = false;
for (int i = 0; i < children.length(); ++i) {
if (children[i].type() == HeapGraphEdge::kShortcut) continue;
HeapEntry* child = children[i].to();
if (!child->painted_reachable()) {
nodes_to_visit.Add(child);
child->paint_reachable();
has_new_edges = true;
}
}
if (!has_new_edges) {
entry->set_ordered_index(current_entry);
entries->at(current_entry++) = entry;
nodes_to_visit.RemoveLast();
}
}
entries->Truncate(current_entry);
}
static int Intersect(int i1, int i2, const Vector<HeapEntry*>& dominators) {
int finger1 = i1, finger2 = i2;
while (finger1 != finger2) {
while (finger1 < finger2) finger1 = dominators[finger1]->ordered_index();
while (finger2 < finger1) finger2 = dominators[finger2]->ordered_index();
}
return finger1;
}
// The algorithm is based on the article:
// K. Cooper, T. Harvey and K. Kennedy "A Simple, Fast Dominance Algorithm"
// Softw. Pract. Exper. 4 (2001), pp. 1–10.
void HeapSnapshot::BuildDominatorTree(const Vector<HeapEntry*>& entries,
Vector<HeapEntry*>* dominators) {
if (entries.length() == 0) return;
const int root_index = entries.length() - 1;
for (int i = 0; i < root_index; ++i) dominators->at(i) = NULL;
dominators->at(root_index) = entries[root_index];
bool changed = true;
while (changed) {
changed = false;
for (int i = root_index - 1; i >= 0; --i) {
HeapEntry* new_idom = NULL;
Vector<HeapGraphEdge*> rets = entries[i]->retainers();
int j = 0;
for (; j < rets.length(); ++j) {
if (rets[j]->type() == HeapGraphEdge::kShortcut) continue;
HeapEntry* ret = rets[j]->From();
if (dominators->at(ret->ordered_index()) != NULL) {
new_idom = ret;
break;
}
}
for (++j; j < rets.length(); ++j) {
if (rets[j]->type() == HeapGraphEdge::kShortcut) continue;
HeapEntry* ret = rets[j]->From();
if (dominators->at(ret->ordered_index()) != NULL) {
new_idom = entries[Intersect(ret->ordered_index(),
new_idom->ordered_index(),
*dominators)];
}
}
if (new_idom != NULL && dominators->at(i) != new_idom) {
dominators->at(i) = new_idom;
changed = true;
}
}
}
}
void HeapSnapshot::SetEntriesDominators() {
// This array is used for maintaining reverse postorder of nodes.
ScopedVector<HeapEntry*> ordered_entries(entries_.length());
FillReversePostorderIndexes(&ordered_entries);
ScopedVector<HeapEntry*> dominators(ordered_entries.length());
BuildDominatorTree(ordered_entries, &dominators);
for (int i = 0; i < ordered_entries.length(); ++i) {
ASSERT(dominators[i] != NULL);
ordered_entries[i]->set_dominator(dominators[i]);
}
// For nodes unreachable from root, set dominator to itself.
for (int i = 0; i < entries_.length(); ++i) {
HeapEntry* entry = entries_[i];
if (entry->dominator() == NULL) entry->set_dominator(entry);
}
}
void HeapSnapshot::ApproximateRetainedSizes() {
SetEntriesDominators();
// As for the dominators tree we only know parent nodes, not
// children, to sum up total sizes we traverse the tree level by
// level upwards, starting from leaves.
for (int i = 0; i < entries_.length(); ++i) {
HeapEntry* entry = entries_[i];
entry->set_retained_size(entry->self_size());
entry->set_leaf();
}
while (true) {
bool onlyLeaves = true;
for (int i = 0; i < entries_.length(); ++i) {
HeapEntry *entry = entries_[i], *dominator = entry->dominator();
if (!entry->is_processed() && dominator != entry) {
dominator->set_non_leaf();
onlyLeaves = false;
}
}
if (onlyLeaves) break;
for (int i = 0; i < entries_.length(); ++i) {
HeapEntry *entry = entries_[i], *dominator = entry->dominator();
if (entry->is_leaf() && dominator != entry) {
dominator->add_retained_size(entry->retained_size());
}
}
// Mark all current leaves as processed, reset non-leaves back to leaves.
for (int i = 0; i < entries_.length(); ++i) {
HeapEntry* entry = entries_[i];
if (entry->is_leaf())
entry->set_processed();
else if (entry->is_non_leaf())
entry->set_leaf();
}
}
}
HeapEntry* HeapSnapshot::GetNextEntryToInit() { HeapEntry* HeapSnapshot::GetNextEntryToInit() {
if (entries_.length() > 0) { if (entries_.length() > 0) {
HeapEntry* last_entry = entries_.last(); HeapEntry* last_entry = entries_.last();
...@@ -1451,6 +1534,16 @@ HeapSnapshotsDiff* HeapSnapshot::CompareWith(HeapSnapshot* snapshot) { ...@@ -1451,6 +1534,16 @@ HeapSnapshotsDiff* HeapSnapshot::CompareWith(HeapSnapshot* snapshot) {
} }
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> template<class T>
static int SortByIds(const T* entry1_ptr, static int SortByIds(const T* entry1_ptr,
const T* entry2_ptr) { const T* entry2_ptr) {
...@@ -1896,6 +1989,8 @@ void HeapSnapshotGenerator::GenerateSnapshot() { ...@@ -1896,6 +1989,8 @@ void HeapSnapshotGenerator::GenerateSnapshot() {
} }
SetRootGcRootsReference(); SetRootGcRootsReference();
Heap::IterateRoots(&extractor, VISIT_ONLY_STRONG); Heap::IterateRoots(&extractor, VISIT_ONLY_STRONG);
snapshot_->ApproximateRetainedSizes();
} }
...@@ -2471,6 +2566,10 @@ void HeapSnapshotJSONSerializer::SerializeNode(HeapEntry* entry) { ...@@ -2471,6 +2566,10 @@ void HeapSnapshotJSONSerializer::SerializeNode(HeapEntry* entry) {
writer_->AddNumber(entry->id()); writer_->AddNumber(entry->id());
writer_->AddCharacter(','); writer_->AddCharacter(',');
writer_->AddNumber(entry->self_size()); writer_->AddNumber(entry->self_size());
writer_->AddCharacter(',');
writer_->AddNumber(entry->RetainedSize(false));
writer_->AddCharacter(',');
writer_->AddNumber(GetNodeId(entry->dominator()));
Vector<HeapGraphEdge> children = entry->children(); Vector<HeapGraphEdge> children = entry->children();
writer_->AddCharacter(','); writer_->AddCharacter(',');
writer_->AddNumber(children.length()); writer_->AddNumber(children.length());
...@@ -2494,6 +2593,8 @@ void HeapSnapshotJSONSerializer::SerializeNodes() { ...@@ -2494,6 +2593,8 @@ void HeapSnapshotJSONSerializer::SerializeNodes() {
"," JSON_S("name") "," JSON_S("name")
"," JSON_S("id") "," JSON_S("id")
"," JSON_S("self_size") "," JSON_S("self_size")
"," JSON_S("retained_size")
"," JSON_S("dominator")
"," JSON_S("children_count") "," JSON_S("children_count")
"," JSON_S("children")) "," JSON_S("children"))
"," JSON_S("types") ":" JSON_A( "," JSON_S("types") ":" JSON_A(
...@@ -2510,6 +2611,8 @@ void HeapSnapshotJSONSerializer::SerializeNodes() { ...@@ -2510,6 +2611,8 @@ void HeapSnapshotJSONSerializer::SerializeNodes() {
"," JSON_S("number") "," JSON_S("number")
"," JSON_S("number") "," JSON_S("number")
"," JSON_S("number") "," JSON_S("number")
"," JSON_S("number")
"," JSON_S("number")
"," JSON_O( "," JSON_O(
JSON_S("fields") ":" JSON_A( JSON_S("fields") ":" JSON_A(
JSON_S("type") JSON_S("type")
...@@ -2529,7 +2632,8 @@ void HeapSnapshotJSONSerializer::SerializeNodes() { ...@@ -2529,7 +2632,8 @@ void HeapSnapshotJSONSerializer::SerializeNodes() {
#undef JSON_O #undef JSON_O
#undef JSON_A #undef JSON_A
const int node_fields_count = 5; // type,name,id,self_size,children_count. const int node_fields_count = 7;
// type,name,id,self_size,retained_size,dominator,children_count.
const int edge_fields_count = 3; // type,name|index,to_node. const int edge_fields_count = 3; // type,name|index,to_node.
List<HashMap::Entry*> sorted_nodes; List<HashMap::Entry*> sorted_nodes;
SortHashMap(&nodes_, &sorted_nodes); SortHashMap(&nodes_, &sorted_nodes);
......
...@@ -528,12 +528,19 @@ class HeapEntry BASE_EMBEDDED { ...@@ -528,12 +528,19 @@ class HeapEntry BASE_EMBEDDED {
const char* name() { return name_; } const char* name() { return name_; }
uint64_t id() { return id_; } uint64_t id() { return id_; }
int self_size() { return self_size_; } int self_size() { return self_size_; }
int retained_size() { return retained_size_; }
void add_retained_size(int size) { retained_size_ += size; }
void set_retained_size(int value) { retained_size_ = value; }
int ordered_index() { return ordered_index_; }
void set_ordered_index(int value) { ordered_index_ = value; }
Vector<HeapGraphEdge> children() { Vector<HeapGraphEdge> children() {
return Vector<HeapGraphEdge>(children_arr(), children_count_); } return Vector<HeapGraphEdge>(children_arr(), children_count_); }
Vector<HeapGraphEdge*> retainers() { Vector<HeapGraphEdge*> retainers() {
return Vector<HeapGraphEdge*>(retainers_arr(), retainers_count_); } return Vector<HeapGraphEdge*>(retainers_arr(), retainers_count_); }
List<HeapGraphPath*>* GetRetainingPaths(); List<HeapGraphPath*>* GetRetainingPaths();
HeapEntry* dominator() { return dominator_; }
void set_dominator(HeapEntry* entry) { dominator_ = entry; }
void clear_paint() { painted_ = kUnpainted; } void clear_paint() { painted_ = kUnpainted; }
bool painted_reachable() { return painted_ == kPainted; } bool painted_reachable() { return painted_ == kPainted; }
...@@ -551,6 +558,13 @@ class HeapEntry BASE_EMBEDDED { ...@@ -551,6 +558,13 @@ class HeapEntry BASE_EMBEDDED {
void ApplyAndPaintAllReachable(Visitor* visitor); void ApplyAndPaintAllReachable(Visitor* visitor);
void PaintAllReachable(); void PaintAllReachable();
bool is_leaf() { return painted_ == kLeaf; }
void set_leaf() { painted_ = kLeaf; }
bool is_non_leaf() { return painted_ == kNonLeaf; }
void set_non_leaf() { painted_ = kNonLeaf; }
bool is_processed() { return painted_ == kProcessed; }
void set_processed() { painted_ = kProcessed; }
void SetIndexedReference(HeapGraphEdge::Type type, void SetIndexedReference(HeapGraphEdge::Type type,
int child_index, int child_index,
int index, int index,
...@@ -564,14 +578,19 @@ class HeapEntry BASE_EMBEDDED { ...@@ -564,14 +578,19 @@ class HeapEntry BASE_EMBEDDED {
void SetUnidirElementReference(int child_index, int index, HeapEntry* entry); void SetUnidirElementReference(int child_index, int index, HeapEntry* entry);
int EntrySize() { return EntriesSize(1, children_count_, retainers_count_); } int EntrySize() { return EntriesSize(1, children_count_, retainers_count_); }
int ReachableSize(); int RetainedSize(bool exact);
int RetainedSize(); List<HeapGraphPath*>* CalculateRetainingPaths();
void Print(int max_depth, int indent); void Print(int max_depth, int indent);
static int EntriesSize(int entries_count, static int EntriesSize(int entries_count,
int children_count, int children_count,
int retainers_count); int retainers_count);
static uint32_t Hash(HeapEntry* entry) {
return ComputeIntegerHash(
static_cast<uint32_t>(reinterpret_cast<uintptr_t>(entry)));
}
static bool Match(void* entry1, void* entry2) { return entry1 == entry2; }
private: private:
HeapGraphEdge* children_arr() { HeapGraphEdge* children_arr() {
...@@ -580,53 +599,37 @@ class HeapEntry BASE_EMBEDDED { ...@@ -580,53 +599,37 @@ class HeapEntry BASE_EMBEDDED {
HeapGraphEdge** retainers_arr() { HeapGraphEdge** retainers_arr() {
return reinterpret_cast<HeapGraphEdge**>(children_arr() + children_count_); return reinterpret_cast<HeapGraphEdge**>(children_arr() + children_count_);
} }
void CalculateExactRetainedSize();
void FindRetainingPaths(CachedHeapGraphPath* prev_path,
List<HeapGraphPath*>* retaining_paths);
const char* TypeAsString(); const char* TypeAsString();
unsigned painted_: 2; unsigned painted_: 2;
unsigned type_: 3; unsigned type_: 3;
// The calculated data is stored in HeapSnapshot in HeapEntryCalculatedData int children_count_: 27;
// entries. See AddCalculatedData and GetCalculatedData.
int calculated_data_index_: 27;
int self_size_;
int children_count_;
int retainers_count_; int retainers_count_;
int self_size_;
union {
int ordered_index_; // Used during dominator tree building.
int retained_size_; // At that moment, there is no retained size yet.
};
HeapEntry* dominator_;
HeapSnapshot* snapshot_; HeapSnapshot* snapshot_;
const char* name_; const char* name_;
uint64_t id_; uint64_t id_;
// Paints used for exact retained sizes calculation.
static const unsigned kUnpainted = 0; static const unsigned kUnpainted = 0;
static const unsigned kPainted = 1; static const unsigned kPainted = 1;
static const unsigned kPaintedReachableFromOthers = 2; static const unsigned kPaintedReachableFromOthers = 2;
static const int kNoCalculatedData = -1; // Paints used for approximate retained sizes calculation.
static const unsigned kLeaf = 0;
DISALLOW_COPY_AND_ASSIGN(HeapEntry); static const unsigned kNonLeaf = 1;
}; static const unsigned kProcessed = 2;
class HeapEntryCalculatedData { static const int kExactRetainedSizeTag = 1;
public:
HeapEntryCalculatedData()
: retaining_paths_(NULL),
reachable_size_(kUnknownSize),
retained_size_(kUnknownSize) {
}
void Dispose();
List<HeapGraphPath*>* GetRetainingPaths(HeapEntry* entry);
int ReachableSize(HeapEntry* entry);
int RetainedSize(HeapEntry* entry);
private:
void CalculateSizes(HeapEntry* entry);
void FindRetainingPaths(HeapEntry* entry, CachedHeapGraphPath* prev_path);
List<HeapGraphPath*>* retaining_paths_; DISALLOW_COPY_AND_ASSIGN(HeapEntry);
int reachable_size_;
int retained_size_;
static const int kUnknownSize = -1;
// Allow generated copy constructor and assignment operator.
}; };
...@@ -687,12 +690,10 @@ class HeapSnapshot { ...@@ -687,12 +690,10 @@ class HeapSnapshot {
int size, int size,
int children_count, int children_count,
int retainers_count); int retainers_count);
int AddCalculatedData(); void ApproximateRetainedSizes();
HeapEntryCalculatedData& GetCalculatedData(int index) {
return calculated_data_[index];
}
void ClearPaint(); void ClearPaint();
HeapSnapshotsDiff* CompareWith(HeapSnapshot* snapshot); HeapSnapshotsDiff* CompareWith(HeapSnapshot* snapshot);
List<HeapGraphPath*>* GetRetainingPaths(HeapEntry* entry);
List<HeapEntry*>* GetSortedEntriesList(); List<HeapEntry*>* GetSortedEntriesList();
template<class Visitor> template<class Visitor>
void IterateEntries(Visitor* visitor) { entries_.Iterate(visitor); } void IterateEntries(Visitor* visitor) { entries_.Iterate(visitor); }
...@@ -710,6 +711,10 @@ class HeapSnapshot { ...@@ -710,6 +711,10 @@ class HeapSnapshot {
int children_count, int children_count,
int retainers_count); int retainers_count);
HeapEntry* GetNextEntryToInit(); HeapEntry* GetNextEntryToInit();
void BuildDominatorTree(const Vector<HeapEntry*>& entries,
Vector<HeapEntry*>* dominators);
void FillReversePostorderIndexes(Vector<HeapEntry*>* entries);
void SetEntriesDominators();
HeapSnapshotsCollection* collection_; HeapSnapshotsCollection* collection_;
Type type_; Type type_;
...@@ -720,7 +725,7 @@ class HeapSnapshot { ...@@ -720,7 +725,7 @@ class HeapSnapshot {
char* raw_entries_; char* raw_entries_;
List<HeapEntry*> entries_; List<HeapEntry*> entries_;
bool entries_sorted_; bool entries_sorted_;
List<HeapEntryCalculatedData> calculated_data_; HashMap retaining_paths_;
#ifdef DEBUG #ifdef DEBUG
int raw_entries_size_; int raw_entries_size_;
#endif #endif
......
...@@ -326,6 +326,8 @@ class Vector { ...@@ -326,6 +326,8 @@ class Vector {
return start_[index]; return start_[index];
} }
T& at(int i) const { return operator[](i); }
T& first() { return start_[0]; } T& first() { return start_[0]; }
T& last() { return start_[length_ - 1]; } T& last() { return start_[length_ - 1]; }
......
...@@ -565,18 +565,21 @@ TEST(HeapSnapshotObjectSizes) { ...@@ -565,18 +565,21 @@ TEST(HeapSnapshotObjectSizes) {
const v8::HeapGraphNode* x = const v8::HeapGraphNode* x =
GetProperty(global, v8::HeapGraphEdge::kShortcut, "x"); GetProperty(global, v8::HeapGraphEdge::kShortcut, "x");
CHECK_NE(NULL, x); CHECK_NE(NULL, x);
const v8::HeapGraphNode* x_prototype =
GetProperty(x, v8::HeapGraphEdge::kProperty, "__proto__");
CHECK_NE(NULL, x_prototype);
const v8::HeapGraphNode* x1 = const v8::HeapGraphNode* x1 =
GetProperty(x, v8::HeapGraphEdge::kProperty, "a"); GetProperty(x, v8::HeapGraphEdge::kProperty, "a");
CHECK_NE(NULL, x1); CHECK_NE(NULL, x1);
const v8::HeapGraphNode* x2 = const v8::HeapGraphNode* x2 =
GetProperty(x, v8::HeapGraphEdge::kProperty, "b"); GetProperty(x, v8::HeapGraphEdge::kProperty, "b");
CHECK_NE(NULL, x2); CHECK_NE(NULL, x2);
CHECK_EQ(x->GetSelfSize() * 3, x->GetRetainedSize());
CHECK_EQ(x1->GetSelfSize(), x1->GetRetainedSize()); // Test approximate sizes.
CHECK_EQ(x2->GetSelfSize(), x2->GetRetainedSize()); CHECK_EQ(x->GetSelfSize() * 3, x->GetRetainedSize(false));
CHECK_EQ(x1->GetSelfSize(), x1->GetRetainedSize(false));
CHECK_EQ(x2->GetSelfSize(), x2->GetRetainedSize(false));
// Test exact sizes.
CHECK_EQ(x->GetSelfSize() * 3, x->GetRetainedSize(true));
CHECK_EQ(x1->GetSelfSize(), x1->GetRetainedSize(true));
CHECK_EQ(x2->GetSelfSize(), x2->GetRetainedSize(true));
} }
...@@ -963,6 +966,67 @@ TEST(AggregatedHeapSnapshot) { ...@@ -963,6 +966,67 @@ TEST(AggregatedHeapSnapshot) {
CHECK(IsNodeRetainedAs(a_from_b, 1)); // B has 1 ref to A. CHECK(IsNodeRetainedAs(a_from_b, 1)); // B has 1 ref to A.
} }
TEST(HeapEntryDominator) {
// The graph looks like this:
//
// -> node1
// a |^
// -> node5 ba
// a v|
// node6 -> node2
// b a |^
// -> node4 ba
// b v|
// -> node3
//
// The dominator for all nodes is node6.
v8::HandleScope scope;
LocalContext env;
CompileRun(
"function X(a, b) { this.a = a; this.b = b; }\n"
"node6 = new X(new X(new X()), new X(new X(),new X()));\n"
"(function(){\n"
"node6.a.a.b = node6.b.a; // node1 -> node2\n"
"node6.b.a.a = node6.a.a; // node2 -> node1\n"
"node6.b.a.b = node6.b.b; // node2 -> node3\n"
"node6.b.b.a = node6.b.a; // node3 -> node2\n"
"})();");
const v8::HeapSnapshot* snapshot =
v8::HeapProfiler::TakeSnapshot(v8::String::New("dominators"));
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
CHECK_NE(NULL, global);
const v8::HeapGraphNode* node6 =
GetProperty(global, v8::HeapGraphEdge::kShortcut, "node6");
CHECK_NE(NULL, node6);
const v8::HeapGraphNode* node5 =
GetProperty(node6, v8::HeapGraphEdge::kProperty, "a");
CHECK_NE(NULL, node5);
const v8::HeapGraphNode* node4 =
GetProperty(node6, v8::HeapGraphEdge::kProperty, "b");
CHECK_NE(NULL, node4);
const v8::HeapGraphNode* node3 =
GetProperty(node4, v8::HeapGraphEdge::kProperty, "b");
CHECK_NE(NULL, node3);
const v8::HeapGraphNode* node2 =
GetProperty(node4, v8::HeapGraphEdge::kProperty, "a");
CHECK_NE(NULL, node2);
const v8::HeapGraphNode* node1 =
GetProperty(node5, v8::HeapGraphEdge::kProperty, "a");
CHECK_NE(NULL, node1);
CHECK_EQ(node6, node1->GetDominatorNode());
CHECK_EQ(node6, node2->GetDominatorNode());
CHECK_EQ(node6, node3->GetDominatorNode());
CHECK_EQ(node6, node4->GetDominatorNode());
CHECK_EQ(node6, node5->GetDominatorNode());
}
namespace { namespace {
class TestJSONStream : public v8::OutputStream { class TestJSONStream : public v8::OutputStream {
......
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