| Index: src/profile-generator.cc
|
| diff --git a/src/profile-generator.cc b/src/profile-generator.cc
|
| index 7054b125950863776f41a5555d8aeaf807bb5a83..01c4e4fe8709a2eb4e1030bba3d828c7d9c4e7d6 100644
|
| --- a/src/profile-generator.cc
|
| +++ b/src/profile-generator.cc
|
| @@ -798,83 +798,102 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) {
|
| }
|
|
|
|
|
| -HeapGraphEdge::HeapGraphEdge(Type type,
|
| - const char* name,
|
| - HeapEntry* from,
|
| - HeapEntry* to)
|
| - : type_(type), name_(name), from_(from), to_(to) {
|
| - ASSERT(type_ == CONTEXT_VARIABLE || type_ == PROPERTY || type_ == INTERNAL);
|
| +void HeapGraphEdge::Init(
|
| + int child_index, Type type, const char* name, HeapEntry* to) {
|
| + ASSERT(type == kContextVariable || type == kProperty || type == kInternal);
|
| + child_index_ = child_index;
|
| + type_ = type;
|
| + name_ = name;
|
| + to_ = to;
|
| }
|
|
|
|
|
| -HeapGraphEdge::HeapGraphEdge(int index,
|
| - HeapEntry* from,
|
| - HeapEntry* to)
|
| - : type_(ELEMENT), index_(index), from_(from), to_(to) {
|
| +void HeapGraphEdge::Init(int child_index, int index, HeapEntry* to) {
|
| + child_index_ = child_index;
|
| + type_ = kElement;
|
| + index_ = index;
|
| + to_ = to;
|
| }
|
|
|
|
|
| -static void DeleteHeapGraphEdge(HeapGraphEdge** edge_ptr) {
|
| - delete *edge_ptr;
|
| +HeapEntry* HeapGraphEdge::From() {
|
| + return reinterpret_cast<HeapEntry*>(this - child_index_) - 1;
|
| }
|
|
|
|
|
| -static void DeleteHeapGraphPath(HeapGraphPath** path_ptr) {
|
| - delete *path_ptr;
|
| +void HeapEntry::Init(HeapSnapshot* snapshot,
|
| + int children_count,
|
| + int retainers_count) {
|
| + Init(snapshot, kInternal, "", 0, 0, children_count, retainers_count);
|
| }
|
|
|
|
|
| -HeapEntry::~HeapEntry() {
|
| - children_.Iterate(DeleteHeapGraphEdge);
|
| - retaining_paths_.Iterate(DeleteHeapGraphPath);
|
| +void HeapEntry::Init(HeapSnapshot* snapshot,
|
| + Type type,
|
| + const char* name,
|
| + uint64_t id,
|
| + int self_size,
|
| + int children_count,
|
| + int retainers_count) {
|
| + snapshot_ = snapshot;
|
| + type_ = type;
|
| + painted_ = kUnpainted;
|
| + calculated_data_index_ = kNoCalculatedData;
|
| + name_ = name;
|
| + id_ = id;
|
| + self_size_ = self_size;
|
| + children_count_ = children_count;
|
| + retainers_count_ = retainers_count;
|
| }
|
|
|
|
|
| -void HeapEntry::AddEdge(HeapGraphEdge* edge) {
|
| - children_.Add(edge);
|
| - edge->to()->retainers_.Add(edge);
|
| +void HeapEntry::SetNamedReference(HeapGraphEdge::Type type,
|
| + int child_index,
|
| + const char* name,
|
| + HeapEntry* entry,
|
| + int retainer_index) {
|
| + children_arr()[child_index].Init(child_index, type, name, entry);
|
| + entry->retainers_arr()[retainer_index] = children_arr() + child_index;
|
| }
|
|
|
|
|
| -void HeapEntry::SetClosureReference(const char* name, HeapEntry* entry) {
|
| - AddEdge(
|
| - new HeapGraphEdge(HeapGraphEdge::CONTEXT_VARIABLE, name, this, entry));
|
| +void HeapEntry::SetElementReference(
|
| + int child_index, int index, HeapEntry* entry, int retainer_index) {
|
| + children_arr()[child_index].Init(child_index, index, entry);
|
| + entry->retainers_arr()[retainer_index] = children_arr() + child_index;
|
| }
|
|
|
|
|
| -void HeapEntry::SetElementReference(int index, HeapEntry* entry) {
|
| - AddEdge(new HeapGraphEdge(index, this, entry));
|
| +void HeapEntry::SetUnidirElementReference(
|
| + int child_index, int index, HeapEntry* entry) {
|
| + children_arr()[child_index].Init(child_index, index, entry);
|
| }
|
|
|
|
|
| -void HeapEntry::SetInternalReference(const char* name, HeapEntry* entry) {
|
| - AddEdge(new HeapGraphEdge(HeapGraphEdge::INTERNAL, name, this, entry));
|
| -}
|
| -
|
| -
|
| -void HeapEntry::SetPropertyReference(const char* name, HeapEntry* entry) {
|
| - AddEdge(new HeapGraphEdge(HeapGraphEdge::PROPERTY, name, this, entry));
|
| -}
|
| -
|
| -
|
| -void HeapEntry::SetAutoIndexReference(HeapEntry* entry) {
|
| - SetElementReference(next_auto_index_++, entry);
|
| -}
|
| -
|
| -
|
| -void HeapEntry::SetUnidirAutoIndexReference(HeapEntry* entry) {
|
| - children_.Add(new HeapGraphEdge(next_auto_index_++, this, entry));
|
| +int HeapEntry::ReachableSize() {
|
| + if (calculated_data_index_ == kNoCalculatedData) {
|
| + calculated_data_index_ = snapshot_->AddCalculatedData();
|
| + }
|
| + return snapshot_->GetCalculatedData(
|
| + calculated_data_index_).ReachableSize(this);
|
| }
|
|
|
|
|
| -int HeapEntry::TotalSize() {
|
| - return total_size_ != kUnknownSize ? total_size_ : CalculateTotalSize();
|
| +int HeapEntry::RetainedSize() {
|
| + if (calculated_data_index_ == kNoCalculatedData) {
|
| + calculated_data_index_ = snapshot_->AddCalculatedData();
|
| + }
|
| + return snapshot_->GetCalculatedData(
|
| + calculated_data_index_).RetainedSize(this);
|
| }
|
|
|
|
|
| -int HeapEntry::NonSharedTotalSize() {
|
| - return non_shared_total_size_ != kUnknownSize ?
|
| - non_shared_total_size_ : CalculateNonSharedTotalSize();
|
| +List<HeapGraphPath*>* HeapEntry::GetRetainingPaths() {
|
| + if (calculated_data_index_ == kNoCalculatedData) {
|
| + calculated_data_index_ = snapshot_->AddCalculatedData();
|
| + }
|
| + return snapshot_->GetCalculatedData(
|
| + calculated_data_index_).GetRetainingPaths(this);
|
| }
|
|
|
|
|
| @@ -882,16 +901,16 @@ template<class Visitor>
|
| void HeapEntry::ApplyAndPaintAllReachable(Visitor* visitor) {
|
| List<HeapEntry*> list(10);
|
| list.Add(this);
|
| - this->PaintReachable();
|
| + this->paint_reachable();
|
| visitor->Apply(this);
|
| while (!list.is_empty()) {
|
| HeapEntry* entry = list.RemoveLast();
|
| - const int children_count = entry->children_.length();
|
| - for (int i = 0; i < children_count; ++i) {
|
| - HeapEntry* child = entry->children_[i]->to();
|
| + Vector<HeapGraphEdge> children = entry->children();
|
| + for (int i = 0; i < children.length(); ++i) {
|
| + HeapEntry* child = children[i].to();
|
| if (!child->painted_reachable()) {
|
| list.Add(child);
|
| - child->PaintReachable();
|
| + child->paint_reachable();
|
| visitor->Apply(child);
|
| }
|
| }
|
| @@ -910,78 +929,158 @@ void HeapEntry::PaintAllReachable() {
|
| }
|
|
|
|
|
| -class TotalSizeCalculator {
|
| - public:
|
| - TotalSizeCalculator()
|
| - : total_size_(0) {
|
| +void HeapEntry::Print(int max_depth, int indent) {
|
| + OS::Print("%6d %6d %6d [%ld] ",
|
| + self_size(), ReachableSize(), RetainedSize(), id_);
|
| + if (type() != kString) {
|
| + OS::Print("%s %.40s\n", TypeAsString(), name_);
|
| + } else {
|
| + OS::Print("\"");
|
| + const char* c = name_;
|
| + while (*c && (c - name_) <= 40) {
|
| + if (*c != '\n')
|
| + OS::Print("%c", *c);
|
| + else
|
| + OS::Print("\\n");
|
| + ++c;
|
| + }
|
| + OS::Print("\"\n");
|
| }
|
| + if (--max_depth == 0) return;
|
| + Vector<HeapGraphEdge> ch = children();
|
| + for (int i = 0; i < ch.length(); ++i) {
|
| + HeapGraphEdge& edge = ch[i];
|
| + switch (edge.type()) {
|
| + case HeapGraphEdge::kContextVariable:
|
| + OS::Print(" %*c #%s: ", indent, ' ', edge.name());
|
| + break;
|
| + case HeapGraphEdge::kElement:
|
| + OS::Print(" %*c %d: ", indent, ' ', edge.index());
|
| + break;
|
| + case HeapGraphEdge::kInternal:
|
| + OS::Print(" %*c $%s: ", indent, ' ', edge.name());
|
| + break;
|
| + case HeapGraphEdge::kProperty:
|
| + OS::Print(" %*c %s: ", indent, ' ', edge.name());
|
| + break;
|
| + default:
|
| + OS::Print("!!! unknown edge type: %d ", edge.type());
|
| + }
|
| + edge.to()->Print(max_depth, indent + 2);
|
| + }
|
| +}
|
|
|
| - int total_size() const { return total_size_; }
|
|
|
| - void Apply(HeapEntry* entry) {
|
| - total_size_ += entry->self_size();
|
| +const char* HeapEntry::TypeAsString() {
|
| + switch (type()) {
|
| + case kInternal: return "/internal/";
|
| + case kObject: return "/object/";
|
| + case kClosure: return "/closure/";
|
| + case kString: return "/string/";
|
| + case kCode: return "/code/";
|
| + case kArray: return "/array/";
|
| + default: return "???";
|
| }
|
| +}
|
|
|
| - private:
|
| - int total_size_;
|
| -};
|
|
|
| -int HeapEntry::CalculateTotalSize() {
|
| - snapshot_->ClearPaint();
|
| - TotalSizeCalculator calc;
|
| - ApplyAndPaintAllReachable(&calc);
|
| - total_size_ = calc.total_size();
|
| - return total_size_;
|
| +int HeapEntry::EntriesSize(int entries_count,
|
| + int children_count,
|
| + int retainers_count) {
|
| + return sizeof(HeapEntry) * entries_count // NOLINT
|
| + + sizeof(HeapGraphEdge) * children_count // NOLINT
|
| + + sizeof(HeapGraphEdge*) * retainers_count; // NOLINT
|
| }
|
|
|
|
|
| -class NonSharedSizeCalculator {
|
| +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:
|
| - NonSharedSizeCalculator()
|
| - : non_shared_total_size_(0) {
|
| + ReachableSizeCalculator()
|
| + : reachable_size_(0) {
|
| }
|
|
|
| - int non_shared_total_size() const { return non_shared_total_size_; }
|
| + int reachable_size() const { return reachable_size_; }
|
|
|
| void Apply(HeapEntry* entry) {
|
| - if (entry->painted_reachable()) {
|
| - non_shared_total_size_ += entry->self_size();
|
| + reachable_size_ += entry->self_size();
|
| + }
|
| +
|
| + private:
|
| + int reachable_size_;
|
| +};
|
| +
|
| +class RetainedSizeCalculator {
|
| + public:
|
| + RetainedSizeCalculator()
|
| + : retained_size_(0) {
|
| + }
|
| +
|
| + int reained_size() const { return retained_size_; }
|
| +
|
| + void Apply(HeapEntry** entry_ptr) {
|
| + if ((*entry_ptr)->painted_reachable()) {
|
| + retained_size_ += (*entry_ptr)->self_size();
|
| }
|
| }
|
|
|
| private:
|
| - int non_shared_total_size_;
|
| + int retained_size_;
|
| };
|
|
|
| -int HeapEntry::CalculateNonSharedTotalSize() {
|
| - // To calculate non-shared total size, first we paint all reachable
|
| - // nodes in one color, then we paint all nodes reachable from other
|
| - // nodes with a different color. Then we consider only nodes painted
|
| - // with the first color for calculating the total size.
|
| - snapshot_->ClearPaint();
|
| - PaintAllReachable();
|
| +void HeapEntryCalculatedData::CalculateSizes(HeapEntry* entry) {
|
| + // 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();
|
|
|
| List<HeapEntry*> list(10);
|
| - if (this != snapshot_->root()) {
|
| - list.Add(snapshot_->root());
|
| - snapshot_->root()->PaintReachableFromOthers();
|
| + HeapEntry* root = entry->snapshot()->root();
|
| + if (entry != root) {
|
| + list.Add(root);
|
| + root->paint_reachable_from_others();
|
| }
|
| while (!list.is_empty()) {
|
| - HeapEntry* entry = list.RemoveLast();
|
| - const int children_count = entry->children_.length();
|
| - for (int i = 0; i < children_count; ++i) {
|
| - HeapEntry* child = entry->children_[i]->to();
|
| - if (child != this && child->not_painted_reachable_from_others()) {
|
| + HeapEntry* curr = list.RemoveLast();
|
| + Vector<HeapGraphEdge> children = curr->children();
|
| + for (int i = 0; i < children.length(); ++i) {
|
| + HeapEntry* child = children[i].to();
|
| + if (child != entry && child->not_painted_reachable_from_others()) {
|
| list.Add(child);
|
| - child->PaintReachableFromOthers();
|
| + child->paint_reachable_from_others();
|
| }
|
| }
|
| }
|
|
|
| - NonSharedSizeCalculator calculator;
|
| - snapshot_->IterateEntries(&calculator);
|
| - non_shared_total_size_ = calculator.non_shared_total_size();
|
| - return non_shared_total_size_;
|
| + RetainedSizeCalculator ret_size_calc;
|
| + entry->snapshot()->IterateEntries(&ret_size_calc);
|
| + retained_size_ = ret_size_calc.reained_size();
|
| }
|
|
|
|
|
| @@ -1019,124 +1118,33 @@ class CachedHeapGraphPath {
|
| };
|
|
|
|
|
| -const List<HeapGraphPath*>* HeapEntry::GetRetainingPaths() {
|
| - if (retaining_paths_.length() == 0 && retainers_.length() != 0) {
|
| +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(this, &path);
|
| + FindRetainingPaths(entry, &path);
|
| }
|
| - return &retaining_paths_;
|
| + return retaining_paths_;
|
| }
|
|
|
|
|
| -void HeapEntry::FindRetainingPaths(HeapEntry* node,
|
| - CachedHeapGraphPath* prev_path) {
|
| - for (int i = 0; i < node->retainers_.length(); ++i) {
|
| - HeapGraphEdge* ret_edge = node->retainers_[i];
|
| - if (prev_path->ContainsNode(ret_edge->from())) continue;
|
| - if (ret_edge->from() != snapshot_->root()) {
|
| +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];
|
| + if (prev_path->ContainsNode(ret_edge->From())) continue;
|
| + if (ret_edge->From() != entry->snapshot()->root()) {
|
| CachedHeapGraphPath path(*prev_path);
|
| path.Add(ret_edge);
|
| - FindRetainingPaths(ret_edge->from(), &path);
|
| + FindRetainingPaths(ret_edge->From(), &path);
|
| } else {
|
| HeapGraphPath* ret_path = new HeapGraphPath(*prev_path->path());
|
| ret_path->Set(0, ret_edge);
|
| - retaining_paths_.Add(ret_path);
|
| - }
|
| - }
|
| -}
|
| -
|
| -
|
| -static void RemoveEdge(List<HeapGraphEdge*>* list, HeapGraphEdge* edge) {
|
| - for (int i = 0; i < list->length(); ) {
|
| - if (list->at(i) == edge) {
|
| - list->Remove(i);
|
| - return;
|
| - } else {
|
| - ++i;
|
| - }
|
| - }
|
| - UNREACHABLE();
|
| -}
|
| -
|
| -
|
| -void HeapEntry::RemoveChild(HeapGraphEdge* edge) {
|
| - RemoveEdge(&children_, edge);
|
| - delete edge;
|
| -}
|
| -
|
| -
|
| -void HeapEntry::RemoveRetainer(HeapGraphEdge* edge) {
|
| - RemoveEdge(&retainers_, edge);
|
| -}
|
| -
|
| -
|
| -void HeapEntry::CutEdges() {
|
| - for (int i = 0; i < children_.length(); ++i) {
|
| - HeapGraphEdge* edge = children_[i];
|
| - edge->to()->RemoveRetainer(edge);
|
| - }
|
| - children_.Iterate(DeleteHeapGraphEdge);
|
| - children_.Clear();
|
| -
|
| - for (int i = 0; i < retainers_.length(); ++i) {
|
| - HeapGraphEdge* edge = retainers_[i];
|
| - edge->from()->RemoveChild(edge);
|
| - }
|
| - retainers_.Clear();
|
| -}
|
| -
|
| -
|
| -void HeapEntry::Print(int max_depth, int indent) {
|
| - OS::Print("%6d %6d %6d [%ld] ",
|
| - self_size_, TotalSize(), NonSharedTotalSize(), id_);
|
| - if (type_ != STRING) {
|
| - OS::Print("%s %.40s\n", TypeAsString(), name_);
|
| - } else {
|
| - OS::Print("\"");
|
| - const char* c = name_;
|
| - while (*c && (c - name_) <= 40) {
|
| - if (*c != '\n')
|
| - OS::Print("%c", *c);
|
| - else
|
| - OS::Print("\\n");
|
| - ++c;
|
| + retaining_paths_->Add(ret_path);
|
| }
|
| - OS::Print("\"\n");
|
| - }
|
| - if (--max_depth == 0) return;
|
| - const int children_count = children_.length();
|
| - for (int i = 0; i < children_count; ++i) {
|
| - HeapGraphEdge* edge = children_[i];
|
| - switch (edge->type()) {
|
| - case HeapGraphEdge::CONTEXT_VARIABLE:
|
| - OS::Print(" %*c #%s: ", indent, ' ', edge->name());
|
| - break;
|
| - case HeapGraphEdge::ELEMENT:
|
| - OS::Print(" %*c %d: ", indent, ' ', edge->index());
|
| - break;
|
| - case HeapGraphEdge::INTERNAL:
|
| - OS::Print(" %*c $%s: ", indent, ' ', edge->name());
|
| - break;
|
| - case HeapGraphEdge::PROPERTY:
|
| - OS::Print(" %*c %s: ", indent, ' ', edge->name());
|
| - break;
|
| - default:
|
| - OS::Print("!!! unknown edge type: %d ", edge->type());
|
| - }
|
| - edge->to()->Print(max_depth, indent + 2);
|
| - }
|
| -}
|
| -
|
| -
|
| -const char* HeapEntry::TypeAsString() {
|
| - switch (type_) {
|
| - case INTERNAL: return "/internal/";
|
| - case OBJECT: return "/object/";
|
| - case CLOSURE: return "/closure/";
|
| - case STRING: return "/string/";
|
| - case CODE: return "/code/";
|
| - case ARRAY: return "/array/";
|
| - default: return "???";
|
| }
|
| }
|
|
|
| @@ -1151,21 +1159,21 @@ HeapGraphPath::HeapGraphPath(const List<HeapGraphEdge*>& path)
|
|
|
|
|
| void HeapGraphPath::Print() {
|
| - path_[0]->from()->Print(1, 0);
|
| + path_[0]->From()->Print(1, 0);
|
| for (int i = 0; i < path_.length(); ++i) {
|
| OS::Print(" -> ");
|
| HeapGraphEdge* edge = path_[i];
|
| switch (edge->type()) {
|
| - case HeapGraphEdge::CONTEXT_VARIABLE:
|
| + case HeapGraphEdge::kContextVariable:
|
| OS::Print("[#%s] ", edge->name());
|
| break;
|
| - case HeapGraphEdge::ELEMENT:
|
| + case HeapGraphEdge::kElement:
|
| OS::Print("[%d] ", edge->index());
|
| break;
|
| - case HeapGraphEdge::INTERNAL:
|
| + case HeapGraphEdge::kInternal:
|
| OS::Print("[$%s] ", edge->name());
|
| break;
|
| - case HeapGraphEdge::PROPERTY:
|
| + case HeapGraphEdge::kProperty:
|
| OS::Print("[%s] ", edge->name());
|
| break;
|
| default:
|
| @@ -1177,76 +1185,8 @@ void HeapGraphPath::Print() {
|
| }
|
|
|
|
|
| -class IndexedReferencesExtractor : public ObjectVisitor {
|
| - public:
|
| - IndexedReferencesExtractor(HeapSnapshot* snapshot, HeapEntry* parent)
|
| - : snapshot_(snapshot),
|
| - parent_(parent) {
|
| - }
|
| -
|
| - void VisitPointer(Object** o) {
|
| - if (!(*o)->IsHeapObject()) return;
|
| - HeapEntry* entry = snapshot_->GetEntry(HeapObject::cast(*o));
|
| - if (entry != NULL) {
|
| - parent_->SetAutoIndexReference(entry);
|
| - }
|
| - }
|
| -
|
| - void VisitPointers(Object** start, Object** end) {
|
| - for (Object** p = start; p < end; p++) VisitPointer(p);
|
| - }
|
| -
|
| - private:
|
| - HeapSnapshot* snapshot_;
|
| - HeapEntry* parent_;
|
| -};
|
| -
|
| -
|
| -HeapEntriesMap::HeapEntriesMap()
|
| - : entries_(HeapObjectsMatch) {
|
| -}
|
| -
|
| -
|
| -HeapEntriesMap::~HeapEntriesMap() {
|
| - for (HashMap::Entry* p = entries_.Start();
|
| - p != NULL;
|
| - p = entries_.Next(p)) {
|
| - if (!IsAlias(p->value)) delete reinterpret_cast<HeapEntry*>(p->value);
|
| - }
|
| -}
|
| -
|
| -
|
| -void HeapEntriesMap::Alias(HeapObject* object, HeapEntry* entry) {
|
| - HashMap::Entry* cache_entry = entries_.Lookup(object, Hash(object), true);
|
| - if (cache_entry->value == NULL)
|
| - cache_entry->value = reinterpret_cast<void*>(
|
| - reinterpret_cast<intptr_t>(entry) | kAliasTag);
|
| -}
|
| -
|
| -
|
| -void HeapEntriesMap::Apply(void (HeapEntry::*Func)(void)) {
|
| - for (HashMap::Entry* p = entries_.Start();
|
| - p != NULL;
|
| - p = entries_.Next(p)) {
|
| - if (!IsAlias(p->value)) (reinterpret_cast<HeapEntry*>(p->value)->*Func)();
|
| - }
|
| -}
|
| -
|
| -
|
| -HeapEntry* HeapEntriesMap::Map(HeapObject* object) {
|
| - HashMap::Entry* cache_entry = entries_.Lookup(object, Hash(object), false);
|
| - return cache_entry != NULL ?
|
| - reinterpret_cast<HeapEntry*>(
|
| - reinterpret_cast<intptr_t>(cache_entry->value) & (~kAliasTag)) : NULL;
|
| -}
|
| -
|
| -
|
| -void HeapEntriesMap::Pair(HeapObject* object, HeapEntry* entry) {
|
| - HashMap::Entry* cache_entry = entries_.Lookup(object, Hash(object), true);
|
| - ASSERT(cache_entry->value == NULL);
|
| - cache_entry->value = entry;
|
| -}
|
| -
|
| +HeapObject *const HeapSnapshot::kInternalRootObject =
|
| + reinterpret_cast<HeapObject*>(1);
|
|
|
| HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection,
|
| const char* title,
|
| @@ -1254,176 +1194,151 @@ HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection,
|
| : collection_(collection),
|
| title_(title),
|
| uid_(uid),
|
| - root_(this),
|
| - sorted_entries_(NULL) {
|
| + root_entry_index_(-1),
|
| + raw_entries_(NULL),
|
| + entries_sorted_(false) {
|
| }
|
|
|
|
|
| -HeapSnapshot::~HeapSnapshot() {
|
| - delete sorted_entries_;
|
| +static void DisposeCalculatedData(HeapEntryCalculatedData* cdata) {
|
| + cdata->Dispose();
|
| }
|
|
|
| -
|
| -void HeapSnapshot::ClearPaint() {
|
| - root_.ClearPaint();
|
| - entries_.Apply(&HeapEntry::ClearPaint);
|
| +HeapSnapshot::~HeapSnapshot() {
|
| + DeleteArray(raw_entries_);
|
| + calculated_data_.Iterate(DisposeCalculatedData);
|
| }
|
|
|
|
|
| -HeapEntry* HeapSnapshot::GetEntry(Object* obj) {
|
| - if (!obj->IsHeapObject()) return NULL;
|
| - HeapObject* object = HeapObject::cast(obj);
|
| +void HeapSnapshot::AllocateEntries(int entries_count,
|
| + int children_count,
|
| + int retainers_count) {
|
| + ASSERT(raw_entries_ == NULL);
|
| + raw_entries_ = NewArray<char>(
|
| + HeapEntry::EntriesSize(entries_count, children_count, retainers_count));
|
| +}
|
|
|
| - {
|
| - HeapEntry* existing = FindEntry(object);
|
| - if (existing != NULL) return existing;
|
| - }
|
|
|
| - // Add new entry.
|
| - if (object->IsJSFunction()) {
|
| +HeapEntry* HeapSnapshot::AddEntry(HeapObject* object,
|
| + int children_count,
|
| + int retainers_count) {
|
| + 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;
|
| + } else if (object->IsJSFunction()) {
|
| JSFunction* func = JSFunction::cast(object);
|
| SharedFunctionInfo* shared = func->shared();
|
| String* name = String::cast(shared->name())->length() > 0 ?
|
| String::cast(shared->name()) : shared->inferred_name();
|
| - return AddEntry(object, HeapEntry::CLOSURE, collection_->GetName(name));
|
| + return AddEntry(object,
|
| + HeapEntry::kClosure,
|
| + collection_->GetName(name),
|
| + children_count,
|
| + retainers_count);
|
| } else if (object->IsJSObject()) {
|
| return AddEntry(object,
|
| - HeapEntry::OBJECT,
|
| + HeapEntry::kObject,
|
| collection_->GetName(
|
| - JSObject::cast(object)->constructor_name()));
|
| - } else if (object->IsJSGlobalPropertyCell()) {
|
| - HeapEntry* value = GetEntry(JSGlobalPropertyCell::cast(object)->value());
|
| - // If GPC references an object that we have interest in, add the object.
|
| - // We don't store HeapEntries for GPCs. Instead, we make our hash map
|
| - // to point to object's HeapEntry by GPCs address.
|
| - if (value != NULL) AddEntryAlias(object, value);
|
| - return value;
|
| + JSObject::cast(object)->constructor_name()),
|
| + children_count,
|
| + retainers_count);
|
| } else if (object->IsString()) {
|
| return AddEntry(object,
|
| - HeapEntry::STRING,
|
| - collection_->GetName(String::cast(object)));
|
| + HeapEntry::kString,
|
| + collection_->GetName(String::cast(object)),
|
| + children_count,
|
| + retainers_count);
|
| } else if (object->IsCode()) {
|
| - return AddEntry(object, HeapEntry::CODE);
|
| + return AddEntry(object,
|
| + HeapEntry::kCode,
|
| + "",
|
| + children_count,
|
| + retainers_count);
|
| } else if (object->IsSharedFunctionInfo()) {
|
| SharedFunctionInfo* shared = SharedFunctionInfo::cast(object);
|
| String* name = String::cast(shared->name())->length() > 0 ?
|
| String::cast(shared->name()) : shared->inferred_name();
|
| - return AddEntry(object, HeapEntry::CODE, collection_->GetName(name));
|
| + return AddEntry(object,
|
| + HeapEntry::kCode,
|
| + collection_->GetName(name),
|
| + children_count,
|
| + retainers_count);
|
| } else if (object->IsScript()) {
|
| Script* script = Script::cast(object);
|
| return AddEntry(object,
|
| - HeapEntry::CODE,
|
| + HeapEntry::kCode,
|
| script->name()->IsString() ?
|
| - collection_->GetName(String::cast(script->name())) : "");
|
| + collection_->GetName(String::cast(script->name())) : "",
|
| + children_count,
|
| + retainers_count);
|
| } else if (object->IsFixedArray()) {
|
| - return AddEntry(object, HeapEntry::ARRAY);
|
| + return AddEntry(object,
|
| + HeapEntry::kArray,
|
| + "",
|
| + children_count,
|
| + retainers_count);
|
| }
|
| // No interest in this object.
|
| return NULL;
|
| }
|
|
|
|
|
| -void HeapSnapshot::SetClosureReference(HeapEntry* parent,
|
| - String* reference_name,
|
| - Object* child) {
|
| - HeapEntry* child_entry = GetEntry(child);
|
| - if (child_entry != NULL) {
|
| - parent->SetClosureReference(
|
| - collection_->GetName(reference_name), child_entry);
|
| - }
|
| +bool HeapSnapshot::WillAddEntry(HeapObject* object) {
|
| + return object == kInternalRootObject
|
| + || object->IsJSFunction()
|
| + || object->IsJSObject()
|
| + || object->IsString()
|
| + || object->IsCode()
|
| + || object->IsSharedFunctionInfo()
|
| + || object->IsScript()
|
| + || object->IsFixedArray();
|
| }
|
|
|
|
|
| -void HeapSnapshot::SetElementReference(HeapEntry* parent,
|
| - int index,
|
| - Object* child) {
|
| - HeapEntry* child_entry = GetEntry(child);
|
| - if (child_entry != NULL) {
|
| - parent->SetElementReference(index, child_entry);
|
| - }
|
| +static void HeapEntryClearPaint(HeapEntry** entry_ptr) {
|
| + (*entry_ptr)->clear_paint();
|
| }
|
|
|
| -
|
| -void HeapSnapshot::SetInternalReference(HeapEntry* parent,
|
| - const char* reference_name,
|
| - Object* child) {
|
| - HeapEntry* child_entry = GetEntry(child);
|
| - if (child_entry != NULL) {
|
| - parent->SetInternalReference(reference_name, child_entry);
|
| - }
|
| +void HeapSnapshot::ClearPaint() {
|
| + entries_.Iterate(HeapEntryClearPaint);
|
| }
|
|
|
|
|
| -void HeapSnapshot::SetPropertyReference(HeapEntry* parent,
|
| - String* reference_name,
|
| - Object* child) {
|
| - HeapEntry* child_entry = GetEntry(child);
|
| - if (child_entry != NULL) {
|
| - parent->SetPropertyReference(
|
| - collection_->GetName(reference_name), child_entry);
|
| - }
|
| +int HeapSnapshot::AddCalculatedData() {
|
| + calculated_data_.Add(HeapEntryCalculatedData());
|
| + return calculated_data_.length() - 1;
|
| }
|
|
|
|
|
| HeapEntry* HeapSnapshot::AddEntry(HeapObject* object,
|
| HeapEntry::Type type,
|
| - const char* name) {
|
| - HeapEntry* entry = new HeapEntry(this,
|
| - type,
|
| - name,
|
| - collection_->GetObjectId(object->address()),
|
| - GetObjectSize(object),
|
| - GetObjectSecurityToken(object));
|
| - entries_.Pair(object, entry);
|
| -
|
| - // Detect, if this is a JS global object of the current context, and
|
| - // add it to snapshot's roots. There can be several JS global objects
|
| - // in a context.
|
| - if (object->IsJSGlobalProxy()) {
|
| - int global_security_token = GetGlobalSecurityToken();
|
| - int object_security_token =
|
| - collection_->token_enumerator()->GetTokenId(
|
| - Context::cast(
|
| - JSGlobalProxy::cast(object)->context())->security_token());
|
| - if (object_security_token == TokenEnumerator::kNoSecurityToken
|
| - || object_security_token == global_security_token) {
|
| - HeapEntry* global_object_entry =
|
| - GetEntry(HeapObject::cast(object->map()->prototype()));
|
| - ASSERT(global_object_entry != NULL);
|
| - root_.SetAutoIndexReference(global_object_entry);
|
| - }
|
| - }
|
| -
|
| + const char* name,
|
| + int children_count,
|
| + int retainers_count) {
|
| + HeapEntry* entry = GetNextEntryToInit();
|
| + entry->Init(this,
|
| + type,
|
| + name,
|
| + collection_->GetObjectId(object->address()),
|
| + GetObjectSize(object),
|
| + children_count,
|
| + retainers_count);
|
| return entry;
|
| }
|
|
|
|
|
| -class EdgesCutter {
|
| - public:
|
| - explicit EdgesCutter(int global_security_token)
|
| - : global_security_token_(global_security_token) {
|
| - }
|
| -
|
| - void Apply(HeapEntry* entry) {
|
| - if (entry->security_token_id() != TokenEnumerator::kNoSecurityToken
|
| - && entry->security_token_id() != global_security_token_) {
|
| - entry->CutEdges();
|
| - }
|
| +HeapEntry* HeapSnapshot::GetNextEntryToInit() {
|
| + if (entries_.length() > 0) {
|
| + HeapEntry* last_entry = entries_.last();
|
| + entries_.Add(reinterpret_cast<HeapEntry*>(
|
| + reinterpret_cast<char*>(last_entry) + last_entry->EntrySize()));
|
| + } else {
|
| + entries_.Add(reinterpret_cast<HeapEntry*>(raw_entries_));
|
| }
|
| -
|
| - private:
|
| - const int global_security_token_;
|
| -};
|
| -
|
| -void HeapSnapshot::CutObjectsFromForeignSecurityContexts() {
|
| - EdgesCutter cutter(GetGlobalSecurityToken());
|
| - entries_.Apply(&cutter);
|
| -}
|
| -
|
| -
|
| -int HeapSnapshot::GetGlobalSecurityToken() {
|
| - return collection_->token_enumerator()->GetTokenId(
|
| - Top::context()->global()->global_context()->security_token());
|
| + return entries_.last();
|
| }
|
|
|
|
|
| @@ -1433,16 +1348,6 @@ int HeapSnapshot::GetObjectSize(HeapObject* obj) {
|
| }
|
|
|
|
|
| -int HeapSnapshot::GetObjectSecurityToken(HeapObject* obj) {
|
| - if (obj->IsGlobalContext()) {
|
| - return collection_->token_enumerator()->GetTokenId(
|
| - Context::cast(obj)->security_token());
|
| - } else {
|
| - return TokenEnumerator::kNoSecurityToken;
|
| - }
|
| -}
|
| -
|
| -
|
| int HeapSnapshot::CalculateNetworkSize(JSObject* obj) {
|
| int size = obj->Size();
|
| // If 'properties' and 'elements' are non-empty (thus, non-shared),
|
| @@ -1467,15 +1372,10 @@ int HeapSnapshot::CalculateNetworkSize(JSObject* obj) {
|
| }
|
|
|
|
|
| -class EntriesCollector {
|
| - public:
|
| - explicit EntriesCollector(List<HeapEntry*>* list) : list_(list) { }
|
| - void Apply(HeapEntry* entry) {
|
| - list_->Add(entry);
|
| - }
|
| - private:
|
| - List<HeapEntry*>* list_;
|
| -};
|
| +HeapSnapshotsDiff* HeapSnapshot::CompareWith(HeapSnapshot* snapshot) {
|
| + return collection_->CompareSnapshots(this, snapshot);
|
| +}
|
| +
|
|
|
| template<class T>
|
| static int SortByIds(const T* entry1_ptr,
|
| @@ -1485,22 +1385,16 @@ static int SortByIds(const T* entry1_ptr,
|
| }
|
|
|
| List<HeapEntry*>* HeapSnapshot::GetSortedEntriesList() {
|
| - if (sorted_entries_ != NULL) return sorted_entries_;
|
| - sorted_entries_ = new List<HeapEntry*>(entries_.capacity());
|
| - EntriesCollector collector(sorted_entries_);
|
| - entries_.Apply(&collector);
|
| - sorted_entries_->Sort(SortByIds);
|
| - return sorted_entries_;
|
| -}
|
| -
|
| -
|
| -HeapSnapshotsDiff* HeapSnapshot::CompareWith(HeapSnapshot* snapshot) {
|
| - return collection_->CompareSnapshots(this, snapshot);
|
| + if (!entries_sorted_) {
|
| + entries_.Sort(SortByIds);
|
| + entries_sorted_ = true;
|
| + }
|
| + return &entries_;
|
| }
|
|
|
|
|
| void HeapSnapshot::Print(int max_depth) {
|
| - root_.Print(max_depth, 0);
|
| + root()->Print(max_depth, 0);
|
| }
|
|
|
|
|
| @@ -1635,53 +1529,343 @@ HeapSnapshotsDiff* HeapSnapshotsCollection::CompareSnapshots(
|
| }
|
|
|
|
|
| +HeapEntriesMap::HeapEntriesMap()
|
| + : entries_(HeapObjectsMatch),
|
| + entries_count_(0),
|
| + total_children_count_(0),
|
| + total_retainers_count_(0) {
|
| +}
|
| +
|
| +
|
| +HeapEntriesMap::~HeapEntriesMap() {
|
| + for (HashMap::Entry* p = entries_.Start(); p != NULL; p = entries_.Next(p)) {
|
| + if (!IsAlias(p->value)) delete reinterpret_cast<EntryInfo*>(p->value);
|
| + }
|
| +}
|
| +
|
| +
|
| +void HeapEntriesMap::Alias(HeapObject* from, HeapObject* to) {
|
| + HashMap::Entry* from_cache_entry = entries_.Lookup(from, Hash(from), true);
|
| + HashMap::Entry* to_cache_entry = entries_.Lookup(to, Hash(to), false);
|
| + if (from_cache_entry->value == NULL) {
|
| + ASSERT(to_cache_entry != NULL);
|
| + from_cache_entry->value = MakeAlias(to_cache_entry->value);
|
| + }
|
| +}
|
| +
|
| +
|
| +HeapEntry* HeapEntriesMap::Map(HeapObject* object) {
|
| + HashMap::Entry* cache_entry = entries_.Lookup(object, Hash(object), false);
|
| + if (cache_entry != NULL) {
|
| + EntryInfo* entry_info =
|
| + reinterpret_cast<EntryInfo*>(Unalias(cache_entry->value));
|
| + return entry_info->entry;
|
| + } else {
|
| + return NULL;
|
| + }
|
| +}
|
| +
|
| +
|
| +void HeapEntriesMap::Pair(HeapObject* object, HeapEntry* entry) {
|
| + HashMap::Entry* cache_entry = entries_.Lookup(object, Hash(object), true);
|
| + ASSERT(cache_entry->value == NULL);
|
| + cache_entry->value = new EntryInfo(entry);
|
| + ++entries_count_;
|
| +}
|
| +
|
| +
|
| +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* to_cache_entry = entries_.Lookup(to, Hash(to), false);
|
| + ASSERT(from_cache_entry != NULL);
|
| + ASSERT(to_cache_entry != NULL);
|
| + EntryInfo* from_entry_info =
|
| + reinterpret_cast<EntryInfo*>(Unalias(from_cache_entry->value));
|
| + EntryInfo* to_entry_info =
|
| + reinterpret_cast<EntryInfo*>(Unalias(to_cache_entry->value));
|
| + if (prev_children_count)
|
| + *prev_children_count = from_entry_info->children_count;
|
| + if (prev_retainers_count)
|
| + *prev_retainers_count = to_entry_info->retainers_count;
|
| + ++from_entry_info->children_count;
|
| + ++to_entry_info->retainers_count;
|
| + ++total_children_count_;
|
| + ++total_retainers_count_;
|
| +}
|
| +
|
| +
|
| +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) {
|
| + : 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;
|
| + }
|
| + void SetElementReference(HeapObject* parent_obj,
|
| + HeapEntry*,
|
| + int,
|
| + Object* child_obj,
|
| + HeapEntry*) {
|
| + entries_->CountReference(parent_obj, HeapObject::cast(child_obj));
|
| + }
|
| + void SetNamedReference(HeapGraphEdge::Type,
|
| + HeapObject* parent_obj,
|
| + HeapEntry*,
|
| + const char*,
|
| + Object* child_obj,
|
| + HeapEntry*) {
|
| + entries_->CountReference(parent_obj, HeapObject::cast(child_obj));
|
| + }
|
| + void SetRootReference(Object* child_obj, HeapEntry*) {
|
| + entries_->CountReference(
|
| + HeapSnapshot::kInternalRootObject, HeapObject::cast(child_obj));
|
| + }
|
| + private:
|
| + HeapEntriesMap* entries_;
|
| +};
|
| +
|
| +
|
| +class SnapshotFiller : public HeapSnapshotGenerator::SnapshotFillerInterface {
|
| + public:
|
| + explicit SnapshotFiller(HeapSnapshot* snapshot, HeapEntriesMap* entries)
|
| + : snapshot_(snapshot),
|
| + collection_(snapshot->collection()),
|
| + entries_(entries) { }
|
| + HeapEntry* AddEntry(HeapObject* obj) {
|
| + UNREACHABLE();
|
| + return NULL;
|
| + }
|
| + void SetElementReference(HeapObject* parent_obj,
|
| + HeapEntry* parent_entry,
|
| + int index,
|
| + Object* child_obj,
|
| + HeapEntry* child_entry) {
|
| + int child_index, retainer_index;
|
| + entries_->CountReference(parent_obj, HeapObject::cast(child_obj),
|
| + &child_index, &retainer_index);
|
| + parent_entry->SetElementReference(
|
| + child_index, index, child_entry, retainer_index);
|
| + }
|
| + void SetNamedReference(HeapGraphEdge::Type type,
|
| + HeapObject* parent_obj,
|
| + HeapEntry* parent_entry,
|
| + const char* reference_name,
|
| + Object* child_obj,
|
| + HeapEntry* child_entry) {
|
| + int child_index, retainer_index;
|
| + entries_->CountReference(parent_obj, HeapObject::cast(child_obj),
|
| + &child_index, &retainer_index);
|
| + parent_entry->SetNamedReference(type,
|
| + child_index,
|
| + reference_name,
|
| + child_entry,
|
| + retainer_index);
|
| + }
|
| + void SetRootReference(Object* child_obj, HeapEntry* child_entry) {
|
| + int child_index, retainer_index;
|
| + entries_->CountReference(
|
| + HeapSnapshot::kInternalRootObject, HeapObject::cast(child_obj),
|
| + &child_index, &retainer_index);
|
| + snapshot_->root()->SetElementReference(
|
| + child_index, child_index + 1, child_entry, retainer_index);
|
| + }
|
| + private:
|
| + HeapSnapshot* snapshot_;
|
| + HeapSnapshotsCollection* collection_;
|
| + HeapEntriesMap* entries_;
|
| +};
|
| +
|
| +class SnapshotAllocator {
|
| + public:
|
| + explicit SnapshotAllocator(HeapSnapshot* snapshot)
|
| + : snapshot_(snapshot) { }
|
| + HeapEntry* GetEntry(
|
| + HeapObject* obj, int children_count, int retainers_count) {
|
| + HeapEntry* entry =
|
| + snapshot_->AddEntry(obj, children_count, retainers_count);
|
| + ASSERT(entry != NULL);
|
| + return entry;
|
| + }
|
| + private:
|
| + HeapSnapshot* snapshot_;
|
| +};
|
| +
|
| void HeapSnapshotGenerator::GenerateSnapshot() {
|
| AssertNoAllocation no_alloc;
|
|
|
| - // Iterate heap contents.
|
| - HeapIterator iterator;
|
| - for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
|
| + // Pass 1. Iterate heap contents to count entries and references.
|
| + SnapshotCounter counter(&entries_);
|
| + filler_ = &counter;
|
| + filler_->AddEntry(HeapSnapshot::kInternalRootObject);
|
| + HeapIterator iterator1;
|
| + for (HeapObject* obj = iterator1.next();
|
| + obj != NULL;
|
| + obj = iterator1.next()) {
|
| ExtractReferences(obj);
|
| }
|
|
|
| - snapshot_->CutObjectsFromForeignSecurityContexts();
|
| + // Allocate and fill entries in the snapshot, allocate references.
|
| + snapshot_->AllocateEntries(entries_.entries_count(),
|
| + entries_.total_children_count(),
|
| + entries_.total_retainers_count());
|
| + SnapshotAllocator allocator(snapshot_);
|
| + entries_.UpdateEntries(&allocator);
|
| +
|
| + // Pass 2. Fill references.
|
| + SnapshotFiller filler(snapshot_, &entries_);
|
| + filler_ = &filler;
|
| + HeapIterator iterator2;
|
| + for (HeapObject* obj = iterator2.next();
|
| + obj != NULL;
|
| + obj = iterator2.next()) {
|
| + ExtractReferences(obj);
|
| + }
|
| +}
|
| +
|
| +
|
| +HeapEntry* HeapSnapshotGenerator::GetEntry(Object* obj) {
|
| + if (!obj->IsHeapObject()) return NULL;
|
| + HeapObject* object = HeapObject::cast(obj);
|
| + HeapEntry* entry = entries_.Map(object);
|
| +
|
| + // A new entry.
|
| + if (entry == NULL) {
|
| + if (obj->IsJSGlobalPropertyCell()) {
|
| + Object* cell_target = JSGlobalPropertyCell::cast(obj)->value();
|
| + entry = GetEntry(cell_target);
|
| + // If GPC references an object that we have interest in (see
|
| + // HeapSnapshot::AddEntry, WillAddEntry), add the object. We
|
| + // don't store HeapEntries for GPCs. Instead, we make our hash
|
| + // map to point to object's HeapEntry by GPCs address.
|
| + if (entry != NULL) {
|
| + entries_.Alias(object, HeapObject::cast(cell_target));
|
| + }
|
| + return entry;
|
| + }
|
| +
|
| + if (snapshot_->WillAddEntry(object)) entry = filler_->AddEntry(object);
|
| + }
|
| +
|
| + return entry;
|
| +}
|
| +
|
| +
|
| +int HeapSnapshotGenerator::GetGlobalSecurityToken() {
|
| + return collection_->token_enumerator()->GetTokenId(
|
| + Top::context()->global()->global_context()->security_token());
|
| +}
|
| +
|
| +
|
| +int HeapSnapshotGenerator::GetObjectSecurityToken(HeapObject* obj) {
|
| + if (obj->IsGlobalContext()) {
|
| + return collection_->token_enumerator()->GetTokenId(
|
| + Context::cast(obj)->security_token());
|
| + } else {
|
| + return TokenEnumerator::kNoSecurityToken;
|
| + }
|
| }
|
|
|
|
|
| +class IndexedReferencesExtractor : public ObjectVisitor {
|
| + public:
|
| + IndexedReferencesExtractor(HeapSnapshotGenerator* generator,
|
| + HeapObject* parent_obj,
|
| + HeapEntry* parent_entry)
|
| + : generator_(generator),
|
| + parent_obj_(parent_obj),
|
| + parent_(parent_entry),
|
| + next_index_(1) {
|
| + }
|
| +
|
| + void VisitPointer(Object** o) {
|
| + generator_->SetElementReference(parent_obj_, parent_, next_index_++, *o);
|
| + }
|
| +
|
| + void VisitPointers(Object** start, Object** end) {
|
| + for (Object** p = start; p < end; p++) VisitPointer(p);
|
| + }
|
| +
|
| + private:
|
| + HeapSnapshotGenerator* generator_;
|
| + HeapObject* parent_obj_;
|
| + HeapEntry* parent_;
|
| + int next_index_;
|
| +};
|
| +
|
| +
|
| void HeapSnapshotGenerator::ExtractReferences(HeapObject* obj) {
|
| - HeapEntry* entry = snapshot_->GetEntry(obj);
|
| - if (entry == NULL) return;
|
| - if (entry->visited()) return;
|
| + // We need to reference JS global objects from snapshot's root.
|
| + // We also need to only include global objects from the current
|
| + // security context. And we don't want to add the global proxy,
|
| + // as we don't have a special type for it.
|
| + if (obj->IsJSGlobalProxy()) {
|
| + int global_security_token = GetGlobalSecurityToken();
|
| + JSGlobalProxy* proxy = JSGlobalProxy::cast(obj);
|
| + int object_security_token =
|
| + collection_->token_enumerator()->GetTokenId(
|
| + Context::cast(proxy->context())->security_token());
|
| + if (object_security_token == TokenEnumerator::kNoSecurityToken
|
| + || object_security_token == global_security_token) {
|
| + SetRootReference(proxy->map()->prototype());
|
| + }
|
| + return;
|
| + }
|
| +
|
| + HeapEntry* entry = GetEntry(obj);
|
| + if (entry == NULL) return; // No interest in this object.
|
|
|
| if (obj->IsJSObject()) {
|
| JSObject* js_obj = JSObject::cast(obj);
|
| ExtractClosureReferences(js_obj, entry);
|
| ExtractPropertyReferences(js_obj, entry);
|
| ExtractElementReferences(js_obj, entry);
|
| - snapshot_->SetPropertyReference(
|
| - entry, Heap::prototype_symbol(), js_obj->map()->prototype());
|
| - } else if (obj->IsJSGlobalPropertyCell()) {
|
| - JSGlobalPropertyCell* cell = JSGlobalPropertyCell::cast(obj);
|
| - snapshot_->SetElementReference(entry, 0, cell->value());
|
| + SetPropertyReference(
|
| + obj, entry, Heap::prototype_symbol(), js_obj->map()->prototype());
|
| } else if (obj->IsString()) {
|
| if (obj->IsConsString()) {
|
| ConsString* cs = ConsString::cast(obj);
|
| - snapshot_->SetElementReference(entry, 0, cs->first());
|
| - snapshot_->SetElementReference(entry, 1, cs->second());
|
| + SetElementReference(obj, entry, 0, cs->first());
|
| + SetElementReference(obj, entry, 1, cs->second());
|
| }
|
| } else if (obj->IsCode() || obj->IsSharedFunctionInfo() || obj->IsScript()) {
|
| - IndexedReferencesExtractor refs_extractor(snapshot_, entry);
|
| + IndexedReferencesExtractor refs_extractor(this, obj, entry);
|
| obj->Iterate(&refs_extractor);
|
| } else if (obj->IsFixedArray()) {
|
| - IndexedReferencesExtractor refs_extractor(snapshot_, entry);
|
| + IndexedReferencesExtractor refs_extractor(this, obj, entry);
|
| obj->Iterate(&refs_extractor);
|
| }
|
| - entry->MarkAsVisited();
|
| }
|
|
|
|
|
| @@ -1700,10 +1884,10 @@ void HeapSnapshotGenerator::ExtractClosureReferences(JSObject* js_obj,
|
| String* local_name = *zone_scope_info.LocalName(i);
|
| int idx = serialized_scope_info->ContextSlotIndex(local_name, NULL);
|
| if (idx >= 0 && idx < context->length()) {
|
| - snapshot_->SetClosureReference(entry, local_name, context->get(idx));
|
| + SetClosureReference(js_obj, entry, local_name, context->get(idx));
|
| }
|
| }
|
| - snapshot_->SetInternalReference(entry, "code", func->shared());
|
| + SetInternalReference(js_obj, entry, "code", func->shared());
|
| }
|
| }
|
|
|
| @@ -1716,13 +1900,13 @@ void HeapSnapshotGenerator::ExtractPropertyReferences(JSObject* js_obj,
|
| switch (descs->GetType(i)) {
|
| case FIELD: {
|
| int index = descs->GetFieldIndex(i);
|
| - snapshot_->SetPropertyReference(
|
| - entry, descs->GetKey(i), js_obj->FastPropertyAt(index));
|
| + SetPropertyReference(
|
| + js_obj, entry, descs->GetKey(i), js_obj->FastPropertyAt(index));
|
| break;
|
| }
|
| case CONSTANT_FUNCTION:
|
| - snapshot_->SetPropertyReference(
|
| - entry, descs->GetKey(i), descs->GetConstantFunction(i));
|
| + SetPropertyReference(
|
| + js_obj, entry, descs->GetKey(i), descs->GetConstantFunction(i));
|
| break;
|
| default: ;
|
| }
|
| @@ -1733,8 +1917,8 @@ void HeapSnapshotGenerator::ExtractPropertyReferences(JSObject* js_obj,
|
| for (int i = 0; i < length; ++i) {
|
| Object* k = dictionary->KeyAt(i);
|
| if (dictionary->IsKey(k)) {
|
| - snapshot_->SetPropertyReference(
|
| - entry, String::cast(k), dictionary->ValueAt(i));
|
| + SetPropertyReference(
|
| + js_obj, entry, String::cast(k), dictionary->ValueAt(i));
|
| }
|
| }
|
| }
|
| @@ -1750,7 +1934,7 @@ void HeapSnapshotGenerator::ExtractElementReferences(JSObject* js_obj,
|
| elements->length();
|
| for (int i = 0; i < length; ++i) {
|
| if (!elements->get(i)->IsTheHole()) {
|
| - snapshot_->SetElementReference(entry, i, elements->get(i));
|
| + SetElementReference(js_obj, entry, i, elements->get(i));
|
| }
|
| }
|
| } else if (js_obj->HasDictionaryElements()) {
|
| @@ -1761,13 +1945,90 @@ void HeapSnapshotGenerator::ExtractElementReferences(JSObject* js_obj,
|
| if (dictionary->IsKey(k)) {
|
| ASSERT(k->IsNumber());
|
| uint32_t index = static_cast<uint32_t>(k->Number());
|
| - snapshot_->SetElementReference(entry, index, dictionary->ValueAt(i));
|
| + SetElementReference(js_obj, entry, index, dictionary->ValueAt(i));
|
| }
|
| }
|
| }
|
| }
|
|
|
|
|
| +void HeapSnapshotGenerator::SetClosureReference(HeapObject* parent_obj,
|
| + HeapEntry* parent_entry,
|
| + String* reference_name,
|
| + Object* child_obj) {
|
| + HeapEntry* child_entry = GetEntry(child_obj);
|
| + if (child_entry != NULL) {
|
| + filler_->SetNamedReference(HeapGraphEdge::kContextVariable,
|
| + parent_obj,
|
| + parent_entry,
|
| + collection_->GetName(reference_name),
|
| + child_obj,
|
| + child_entry);
|
| + }
|
| +}
|
| +
|
| +
|
| +void HeapSnapshotGenerator::SetElementReference(HeapObject* parent_obj,
|
| + HeapEntry* parent_entry,
|
| + int index,
|
| + Object* child_obj) {
|
| + HeapEntry* child_entry = GetEntry(child_obj);
|
| + if (child_entry != NULL) {
|
| + filler_->SetElementReference(
|
| + parent_obj, parent_entry, index, child_obj, child_entry);
|
| + }
|
| +}
|
| +
|
| +
|
| +void HeapSnapshotGenerator::SetInternalReference(HeapObject* parent_obj,
|
| + HeapEntry* parent_entry,
|
| + const char* reference_name,
|
| + Object* child_obj) {
|
| + HeapEntry* child_entry = GetEntry(child_obj);
|
| + if (child_entry != NULL) {
|
| + filler_->SetNamedReference(HeapGraphEdge::kInternal,
|
| + parent_obj,
|
| + parent_entry,
|
| + reference_name,
|
| + child_obj,
|
| + child_entry);
|
| + }
|
| +}
|
| +
|
| +
|
| +void HeapSnapshotGenerator::SetPropertyReference(HeapObject* parent_obj,
|
| + HeapEntry* parent_entry,
|
| + String* reference_name,
|
| + Object* child_obj) {
|
| + HeapEntry* child_entry = GetEntry(child_obj);
|
| + if (child_entry != NULL) {
|
| + filler_->SetNamedReference(HeapGraphEdge::kProperty,
|
| + parent_obj,
|
| + parent_entry,
|
| + collection_->GetName(reference_name),
|
| + child_obj,
|
| + child_entry);
|
| + }
|
| +}
|
| +
|
| +
|
| +void HeapSnapshotGenerator::SetRootReference(Object* child_obj) {
|
| + HeapEntry* child_entry = GetEntry(child_obj);
|
| + ASSERT(child_entry != NULL);
|
| + filler_->SetRootReference(child_obj, child_entry);
|
| +}
|
| +
|
| +
|
| +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);
|
| + raw_deletions_root_ =
|
| + NewArray<char>(HeapEntry::EntriesSize(1, deletions_count, 0));
|
| + deletions_root()->Init(snapshot1_, deletions_count, 0);
|
| +}
|
| +
|
| +
|
| static void DeleteHeapSnapshotsDiff(HeapSnapshotsDiff** diff_ptr) {
|
| delete *diff_ptr;
|
| }
|
| @@ -1779,8 +2040,6 @@ HeapSnapshotsComparator::~HeapSnapshotsComparator() {
|
|
|
| HeapSnapshotsDiff* HeapSnapshotsComparator::Compare(HeapSnapshot* snapshot1,
|
| HeapSnapshot* snapshot2) {
|
| - HeapSnapshotsDiff* diff = new HeapSnapshotsDiff(snapshot1, snapshot2);
|
| - diffs_.Add(diff);
|
| List<HeapEntry*>* entries1 = snapshot1->GetSortedEntriesList();
|
| List<HeapEntry*>* entries2 = snapshot2->GetSortedEntriesList();
|
| int i = 0, j = 0;
|
| @@ -1810,17 +2069,33 @@ HeapSnapshotsDiff* HeapSnapshotsComparator::Compare(HeapSnapshot* snapshot1,
|
|
|
| snapshot1->ClearPaint();
|
| snapshot1->root()->PaintAllReachable();
|
| + snapshot2->ClearPaint();
|
| + snapshot2->root()->PaintAllReachable();
|
| + int reachable_deleted_entries = 0, reachable_added_entries = 0;
|
| + for (int i = 0; i < deleted_entries.length(); ++i) {
|
| + HeapEntry* entry = deleted_entries[i];
|
| + if (entry->painted_reachable()) ++reachable_deleted_entries;
|
| + }
|
| + for (int i = 0; i < added_entries.length(); ++i) {
|
| + HeapEntry* entry = added_entries[i];
|
| + if (entry->painted_reachable()) ++reachable_added_entries;
|
| + }
|
| +
|
| + HeapSnapshotsDiff* diff = new HeapSnapshotsDiff(snapshot1, snapshot2);
|
| + diffs_.Add(diff);
|
| + diff->CreateRoots(reachable_added_entries, reachable_deleted_entries);
|
| +
|
| + int del_child_index = 0, deleted_entry_index = 1;
|
| for (int i = 0; i < deleted_entries.length(); ++i) {
|
| HeapEntry* entry = deleted_entries[i];
|
| if (entry->painted_reachable())
|
| - diff->AddDeletedEntry(entry);
|
| + diff->AddDeletedEntry(del_child_index++, deleted_entry_index++, entry);
|
| }
|
| - snapshot2->ClearPaint();
|
| - snapshot2->root()->PaintAllReachable();
|
| + int add_child_index = 0, added_entry_index = 1;
|
| for (int i = 0; i < added_entries.length(); ++i) {
|
| HeapEntry* entry = added_entries[i];
|
| if (entry->painted_reachable())
|
| - diff->AddAddedEntry(entry);
|
| + diff->AddAddedEntry(add_child_index++, added_entry_index++, entry);
|
| }
|
| return diff;
|
| }
|
|
|