| Index: src/profile-generator.cc
|
| diff --git a/src/profile-generator.cc b/src/profile-generator.cc
|
| index 9ee4977d5045293a3c225c16377db63316646f00..9f475a49135a5550b24061c553af50d0d7be6740 100644
|
| --- a/src/profile-generator.cc
|
| +++ b/src/profile-generator.cc
|
| @@ -869,12 +869,13 @@ void HeapEntry::Init(HeapSnapshot* snapshot,
|
| snapshot_ = snapshot;
|
| type_ = type;
|
| painted_ = kUnpainted;
|
| - calculated_data_index_ = kNoCalculatedData;
|
| name_ = name;
|
| id_ = id;
|
| self_size_ = self_size;
|
| + retained_size_ = 0;
|
| children_count_ = children_count;
|
| retainers_count_ = retainers_count;
|
| + dominator_ = NULL;
|
| }
|
|
|
|
|
| @@ -904,30 +905,16 @@ void HeapEntry::SetUnidirElementReference(
|
| }
|
|
|
|
|
| -int HeapEntry::ReachableSize() {
|
| - if (calculated_data_index_ == kNoCalculatedData) {
|
| - calculated_data_index_ = snapshot_->AddCalculatedData();
|
| +int HeapEntry::RetainedSize(bool exact) {
|
| + if (exact && (retained_size_ & kExactRetainedSizeTag) == 0) {
|
| + CalculateExactRetainedSize();
|
| }
|
| - return snapshot_->GetCalculatedData(
|
| - 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);
|
| + return retained_size_ & (~kExactRetainedSizeTag);
|
| }
|
|
|
|
|
| List<HeapGraphPath*>* HeapEntry::GetRetainingPaths() {
|
| - if (calculated_data_index_ == kNoCalculatedData) {
|
| - calculated_data_index_ = snapshot_->AddCalculatedData();
|
| - }
|
| - return snapshot_->GetCalculatedData(
|
| - calculated_data_index_).GetRetainingPaths(this);
|
| + return snapshot_->GetRetainingPaths(this);
|
| }
|
|
|
|
|
| @@ -965,8 +952,7 @@ void HeapEntry::PaintAllReachable() {
|
|
|
|
|
| void HeapEntry::Print(int max_depth, int indent) {
|
| - OS::Print("%6d %6d %6d [%llu] ",
|
| - self_size(), ReachableSize(), RetainedSize(), id_);
|
| + OS::Print("%6d %6d [%llu] ", self_size(), RetainedSize(false), id_);
|
| if (type() != kString) {
|
| OS::Print("%s %.40s\n", TypeAsString(), name_);
|
| } else {
|
| @@ -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 {
|
| public:
|
| RetainedSizeCalculator()
|
| @@ -1092,20 +1040,17 @@ class RetainedSizeCalculator {
|
| int retained_size_;
|
| };
|
|
|
| -void HeapEntryCalculatedData::CalculateSizes(HeapEntry* entry) {
|
| +void HeapEntry::CalculateExactRetainedSize() {
|
| // To calculate retained size, first we paint all reachable nodes in
|
| - // one color (and calculate reachable size as a byproduct), then we
|
| - // paint (or re-paint) all nodes reachable from other nodes with a
|
| - // different color. Then we consider only nodes painted with the
|
| - // first color for calculating the retained size.
|
| - entry->snapshot()->ClearPaint();
|
| - ReachableSizeCalculator rch_size_calc;
|
| - entry->ApplyAndPaintAllReachable(&rch_size_calc);
|
| - reachable_size_ = rch_size_calc.reachable_size();
|
| + // one color, then we paint (or re-paint) all nodes reachable from
|
| + // other nodes with a different color. Then we sum up self sizes of
|
| + // nodes painted with the first color.
|
| + snapshot()->ClearPaint();
|
| + PaintAllReachable();
|
|
|
| List<HeapEntry*> list(10);
|
| - HeapEntry* root = entry->snapshot()->root();
|
| - if (entry != root) {
|
| + HeapEntry* root = snapshot()->root();
|
| + if (this != root) {
|
| list.Add(root);
|
| root->paint_reachable_from_others();
|
| }
|
| @@ -1115,7 +1060,7 @@ void HeapEntryCalculatedData::CalculateSizes(HeapEntry* entry) {
|
| for (int i = 0; i < children.length(); ++i) {
|
| if (children[i].type() == HeapGraphEdge::kShortcut) continue;
|
| 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);
|
| child->paint_reachable_from_others();
|
| }
|
| @@ -1123,8 +1068,10 @@ void HeapEntryCalculatedData::CalculateSizes(HeapEntry* entry) {
|
| }
|
|
|
| RetainedSizeCalculator ret_size_calc;
|
| - entry->snapshot()->IterateEntries(&ret_size_calc);
|
| + snapshot()->IterateEntries(&ret_size_calc);
|
| retained_size_ = ret_size_calc.reained_size();
|
| + ASSERT((retained_size_ & kExactRetainedSizeTag) == 0);
|
| + retained_size_ |= kExactRetainedSizeTag;
|
| }
|
|
|
|
|
| @@ -1162,32 +1109,28 @@ class CachedHeapGraphPath {
|
| };
|
|
|
|
|
| -List<HeapGraphPath*>* HeapEntryCalculatedData::GetRetainingPaths(
|
| - HeapEntry* entry) {
|
| - if (retaining_paths_ == NULL) retaining_paths_ = new List<HeapGraphPath*>(4);
|
| - if (retaining_paths_->length() == 0 && entry->retainers().length() != 0) {
|
| - CachedHeapGraphPath path;
|
| - FindRetainingPaths(entry, &path);
|
| - }
|
| - return retaining_paths_;
|
| +List<HeapGraphPath*>* HeapEntry::CalculateRetainingPaths() {
|
| + List<HeapGraphPath*>* retaining_paths = new List<HeapGraphPath*>(4);
|
| + CachedHeapGraphPath path;
|
| + FindRetainingPaths(&path, retaining_paths);
|
| + return retaining_paths;
|
| }
|
|
|
|
|
| -void HeapEntryCalculatedData::FindRetainingPaths(
|
| - HeapEntry* entry,
|
| - CachedHeapGraphPath* prev_path) {
|
| - Vector<HeapGraphEdge*> retainers = entry->retainers();
|
| - for (int i = 0; i < retainers.length(); ++i) {
|
| - HeapGraphEdge* ret_edge = retainers[i];
|
| +void HeapEntry::FindRetainingPaths(CachedHeapGraphPath* prev_path,
|
| + List<HeapGraphPath*>* retaining_paths) {
|
| + Vector<HeapGraphEdge*> rets = retainers();
|
| + for (int i = 0; i < rets.length(); ++i) {
|
| + HeapGraphEdge* ret_edge = rets[i];
|
| if (prev_path->ContainsNode(ret_edge->From())) continue;
|
| - if (ret_edge->From() != entry->snapshot()->root()) {
|
| + if (ret_edge->From() != snapshot()->root()) {
|
| CachedHeapGraphPath path(*prev_path);
|
| path.Add(ret_edge);
|
| - FindRetainingPaths(ret_edge->From(), &path);
|
| + ret_edge->From()->FindRetainingPaths(&path, retaining_paths);
|
| } else {
|
| HeapGraphPath* ret_path = new HeapGraphPath(*prev_path->path());
|
| ret_path->Set(0, ret_edge);
|
| - retaining_paths_->Add(ret_path);
|
| + retaining_paths->Add(ret_path);
|
| }
|
| }
|
| }
|
| @@ -1247,12 +1190,12 @@ template <size_t ptr_size> struct SnapshotSizeConstants;
|
|
|
| template <> struct SnapshotSizeConstants<4> {
|
| static const int kExpectedHeapGraphEdgeSize = 12;
|
| - static const int kExpectedHeapEntrySize = 32;
|
| + static const int kExpectedHeapEntrySize = 36;
|
| };
|
|
|
| template <> struct SnapshotSizeConstants<8> {
|
| static const int kExpectedHeapGraphEdgeSize = 24;
|
| - static const int kExpectedHeapEntrySize = 40;
|
| + static const int kExpectedHeapEntrySize = 48;
|
| };
|
|
|
| } // namespace
|
| @@ -1268,7 +1211,8 @@ HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection,
|
| root_entry_(NULL),
|
| gc_roots_entry_(NULL),
|
| raw_entries_(NULL),
|
| - entries_sorted_(false) {
|
| + entries_sorted_(false),
|
| + retaining_paths_(HeapEntry::Match) {
|
| STATIC_ASSERT(
|
| sizeof(HeapGraphEdge) ==
|
| SnapshotSizeConstants<sizeof(void*)>::kExpectedHeapGraphEdgeSize); // NOLINT
|
| @@ -1278,13 +1222,20 @@ HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection,
|
| }
|
|
|
|
|
| -static void DisposeCalculatedData(HeapEntryCalculatedData* cdata) {
|
| - cdata->Dispose();
|
| +static void DeleteHeapGraphPath(HeapGraphPath** path_ptr) {
|
| + delete *path_ptr;
|
| }
|
|
|
| HeapSnapshot::~HeapSnapshot() {
|
| 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() {
|
| }
|
|
|
|
|
| -int HeapSnapshot::AddCalculatedData() {
|
| - calculated_data_.Add(HeapEntryCalculatedData());
|
| - return calculated_data_.length() - 1;
|
| -}
|
| -
|
| -
|
| HeapEntry* HeapSnapshot::AddEntry(HeapObject* object,
|
| HeapEntry::Type type,
|
| const char* name,
|
| @@ -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() {
|
| if (entries_.length() > 0) {
|
| HeapEntry* last_entry = entries_.last();
|
| @@ -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>
|
| static int SortByIds(const T* entry1_ptr,
|
| const T* entry2_ptr) {
|
| @@ -1896,6 +1989,8 @@ void HeapSnapshotGenerator::GenerateSnapshot() {
|
| }
|
| SetRootGcRootsReference();
|
| Heap::IterateRoots(&extractor, VISIT_ONLY_STRONG);
|
| +
|
| + snapshot_->ApproximateRetainedSizes();
|
| }
|
|
|
|
|
| @@ -2471,6 +2566,10 @@ void HeapSnapshotJSONSerializer::SerializeNode(HeapEntry* entry) {
|
| writer_->AddNumber(entry->id());
|
| writer_->AddCharacter(',');
|
| writer_->AddNumber(entry->self_size());
|
| + writer_->AddCharacter(',');
|
| + writer_->AddNumber(entry->RetainedSize(false));
|
| + writer_->AddCharacter(',');
|
| + writer_->AddNumber(GetNodeId(entry->dominator()));
|
| Vector<HeapGraphEdge> children = entry->children();
|
| writer_->AddCharacter(',');
|
| writer_->AddNumber(children.length());
|
| @@ -2494,6 +2593,8 @@ void HeapSnapshotJSONSerializer::SerializeNodes() {
|
| "," JSON_S("name")
|
| "," JSON_S("id")
|
| "," JSON_S("self_size")
|
| + "," JSON_S("retained_size")
|
| + "," JSON_S("dominator")
|
| "," JSON_S("children_count")
|
| "," JSON_S("children"))
|
| "," JSON_S("types") ":" JSON_A(
|
| @@ -2510,6 +2611,8 @@ void HeapSnapshotJSONSerializer::SerializeNodes() {
|
| "," JSON_S("number")
|
| "," JSON_S("number")
|
| "," JSON_S("number")
|
| + "," JSON_S("number")
|
| + "," JSON_S("number")
|
| "," JSON_O(
|
| JSON_S("fields") ":" JSON_A(
|
| JSON_S("type")
|
| @@ -2529,7 +2632,8 @@ void HeapSnapshotJSONSerializer::SerializeNodes() {
|
| #undef JSON_O
|
| #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.
|
| List<HashMap::Entry*> sorted_nodes;
|
| SortHashMap(&nodes_, &sorted_nodes);
|
|
|