Heap profiler: reduce heap snapshots size.

The size of a snapshot is now 65-80% of the JS heap size (tested on
GMail and Wave), previously it was >200%.

BUG=783

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5211 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent c3833657
...@@ -194,10 +194,10 @@ class HeapGraphNode; ...@@ -194,10 +194,10 @@ class HeapGraphNode;
class V8EXPORT HeapGraphEdge { class V8EXPORT HeapGraphEdge {
public: public:
enum Type { enum Type {
CONTEXT_VARIABLE = 0, // A variable from a function context. kContextVariable = 0, // A variable from a function context.
ELEMENT = 1, // An element of an array. kElement = 1, // An element of an array.
PROPERTY = 2, // A named object property. kProperty = 2, // A named object property.
INTERNAL = 3 // A link that can't be accessed from JS, kInternal = 3 // A link that can't be accessed from JS,
// thus, its name isn't a real property name. // thus, its name isn't a real property name.
}; };
...@@ -240,12 +240,12 @@ class V8EXPORT HeapGraphPath { ...@@ -240,12 +240,12 @@ class V8EXPORT HeapGraphPath {
class V8EXPORT HeapGraphNode { class V8EXPORT HeapGraphNode {
public: public:
enum Type { enum Type {
INTERNAL = 0, // Internal node, a virtual one, for housekeeping. kInternal = 0, // Internal node, a virtual one, for housekeeping.
ARRAY = 1, // An array of elements. kArray = 1, // An array of elements.
STRING = 2, // A string. kString = 2, // A string.
OBJECT = 3, // A JS object (except for arrays and strings). kObject = 3, // A JS object (except for arrays and strings).
CODE = 4, // Compiled code. kCode = 4, // Compiled code.
CLOSURE = 5 // Function closure. kClosure = 5 // Function closure.
}; };
/** Returns node type (see HeapGraphNode::Type). */ /** Returns node type (see HeapGraphNode::Type). */
...@@ -268,13 +268,15 @@ class V8EXPORT HeapGraphNode { ...@@ -268,13 +268,15 @@ class V8EXPORT HeapGraphNode {
int GetSelfSize() const; int GetSelfSize() const;
/** Returns node's network (self + reachable nodes) size, in bytes. */ /** Returns node's network (self + reachable nodes) size, in bytes. */
int GetTotalSize() const; int GetReachableSize() const;
/** /**
* Returns node's private size, in bytes. That is, the size of memory * Returns node's retained size, in bytes. That is, self + sizes of
* that will be reclaimed having this node collected. * the objects that are reachable only from this object. In other
* words, the size of memory that will be reclaimed having this node
* collected.
*/ */
int GetPrivateSize() const; int GetRetainedSize() const;
/** Returns child nodes count of the node. */ /** Returns child nodes count of the node. */
int GetChildrenCount() const; int GetChildrenCount() const;
......
...@@ -4491,24 +4491,27 @@ const CpuProfile* CpuProfiler::StopProfiling(Handle<String> title, ...@@ -4491,24 +4491,27 @@ const CpuProfile* CpuProfiler::StopProfiling(Handle<String> title,
} }
static i::HeapGraphEdge* ToInternal(const HeapGraphEdge* edge) {
return const_cast<i::HeapGraphEdge*>(
reinterpret_cast<const i::HeapGraphEdge*>(edge));
}
HeapGraphEdge::Type HeapGraphEdge::GetType() const { HeapGraphEdge::Type HeapGraphEdge::GetType() const {
IsDeadCheck("v8::HeapGraphEdge::GetType"); IsDeadCheck("v8::HeapGraphEdge::GetType");
return static_cast<HeapGraphEdge::Type>( return static_cast<HeapGraphEdge::Type>(ToInternal(this)->type());
reinterpret_cast<const i::HeapGraphEdge*>(this)->type());
} }
Handle<Value> HeapGraphEdge::GetName() const { Handle<Value> HeapGraphEdge::GetName() const {
IsDeadCheck("v8::HeapGraphEdge::GetName"); IsDeadCheck("v8::HeapGraphEdge::GetName");
const i::HeapGraphEdge* edge = i::HeapGraphEdge* edge = ToInternal(this);
reinterpret_cast<const i::HeapGraphEdge*>(this);
switch (edge->type()) { switch (edge->type()) {
case i::HeapGraphEdge::CONTEXT_VARIABLE: case i::HeapGraphEdge::kContextVariable:
case i::HeapGraphEdge::INTERNAL: case i::HeapGraphEdge::kInternal:
case i::HeapGraphEdge::PROPERTY: case i::HeapGraphEdge::kProperty:
return Handle<String>(ToApi<String>(i::Factory::LookupAsciiSymbol( return Handle<String>(ToApi<String>(i::Factory::LookupAsciiSymbol(
edge->name()))); edge->name())));
case i::HeapGraphEdge::ELEMENT: case i::HeapGraphEdge::kElement:
return Handle<Number>(ToApi<Number>(i::Factory::NewNumberFromInt( return Handle<Number>(ToApi<Number>(i::Factory::NewNumberFromInt(
edge->index()))); edge->index())));
default: UNREACHABLE(); default: UNREACHABLE();
...@@ -4519,28 +4522,32 @@ Handle<Value> HeapGraphEdge::GetName() const { ...@@ -4519,28 +4522,32 @@ Handle<Value> HeapGraphEdge::GetName() const {
const HeapGraphNode* HeapGraphEdge::GetFromNode() const { const HeapGraphNode* HeapGraphEdge::GetFromNode() const {
IsDeadCheck("v8::HeapGraphEdge::GetFromNode"); IsDeadCheck("v8::HeapGraphEdge::GetFromNode");
const i::HeapEntry* from = const i::HeapEntry* from = ToInternal(this)->From();
reinterpret_cast<const i::HeapGraphEdge*>(this)->from();
return reinterpret_cast<const HeapGraphNode*>(from); return reinterpret_cast<const HeapGraphNode*>(from);
} }
const HeapGraphNode* HeapGraphEdge::GetToNode() const { const HeapGraphNode* HeapGraphEdge::GetToNode() const {
IsDeadCheck("v8::HeapGraphEdge::GetToNode"); IsDeadCheck("v8::HeapGraphEdge::GetToNode");
const i::HeapEntry* to = const i::HeapEntry* to = ToInternal(this)->to();
reinterpret_cast<const i::HeapGraphEdge*>(this)->to();
return reinterpret_cast<const HeapGraphNode*>(to); return reinterpret_cast<const HeapGraphNode*>(to);
} }
static i::HeapGraphPath* ToInternal(const HeapGraphPath* path) {
return const_cast<i::HeapGraphPath*>(
reinterpret_cast<const i::HeapGraphPath*>(path));
}
int HeapGraphPath::GetEdgesCount() const { int HeapGraphPath::GetEdgesCount() const {
return reinterpret_cast<const i::HeapGraphPath*>(this)->path()->length(); return ToInternal(this)->path()->length();
} }
const HeapGraphEdge* HeapGraphPath::GetEdge(int index) const { const HeapGraphEdge* HeapGraphPath::GetEdge(int index) const {
return reinterpret_cast<const HeapGraphEdge*>( return reinterpret_cast<const HeapGraphEdge*>(
reinterpret_cast<const i::HeapGraphPath*>(this)->path()->at(index)); ToInternal(this)->path()->at(index));
} }
...@@ -4555,137 +4562,136 @@ const HeapGraphNode* HeapGraphPath::GetToNode() const { ...@@ -4555,137 +4562,136 @@ const HeapGraphNode* HeapGraphPath::GetToNode() const {
} }
static i::HeapEntry* ToInternal(const HeapGraphNode* entry) {
return const_cast<i::HeapEntry*>(
reinterpret_cast<const i::HeapEntry*>(entry));
}
HeapGraphNode::Type HeapGraphNode::GetType() const { HeapGraphNode::Type HeapGraphNode::GetType() const {
IsDeadCheck("v8::HeapGraphNode::GetType"); IsDeadCheck("v8::HeapGraphNode::GetType");
return static_cast<HeapGraphNode::Type>( return static_cast<HeapGraphNode::Type>(ToInternal(this)->type());
reinterpret_cast<const i::HeapEntry*>(this)->type());
} }
Handle<String> HeapGraphNode::GetName() const { Handle<String> HeapGraphNode::GetName() const {
IsDeadCheck("v8::HeapGraphNode::GetName"); IsDeadCheck("v8::HeapGraphNode::GetName");
return Handle<String>(ToApi<String>(i::Factory::LookupAsciiSymbol( return Handle<String>(ToApi<String>(i::Factory::LookupAsciiSymbol(
reinterpret_cast<const i::HeapEntry*>(this)->name()))); ToInternal(this)->name())));
} }
uint64_t HeapGraphNode::GetId() const { uint64_t HeapGraphNode::GetId() const {
IsDeadCheck("v8::HeapGraphNode::GetId"); IsDeadCheck("v8::HeapGraphNode::GetId");
return reinterpret_cast<const i::HeapEntry*>(this)->id(); return ToInternal(this)->id();
} }
int HeapGraphNode::GetSelfSize() const { int HeapGraphNode::GetSelfSize() const {
IsDeadCheck("v8::HeapGraphNode::GetSelfSize"); IsDeadCheck("v8::HeapGraphNode::GetSelfSize");
return reinterpret_cast<const i::HeapEntry*>(this)->self_size(); return ToInternal(this)->self_size();
} }
int HeapGraphNode::GetTotalSize() const { int HeapGraphNode::GetReachableSize() const {
IsDeadCheck("v8::HeapSnapshot::GetHead"); IsDeadCheck("v8::HeapSnapshot::GetReachableSize");
return const_cast<i::HeapEntry*>( return ToInternal(this)->ReachableSize();
reinterpret_cast<const i::HeapEntry*>(this))->TotalSize();
} }
int HeapGraphNode::GetPrivateSize() const { int HeapGraphNode::GetRetainedSize() const {
IsDeadCheck("v8::HeapSnapshot::GetPrivateSize"); IsDeadCheck("v8::HeapSnapshot::GetRetainedSize");
return const_cast<i::HeapEntry*>( return ToInternal(this)->RetainedSize();
reinterpret_cast<const i::HeapEntry*>(this))->NonSharedTotalSize();
} }
int HeapGraphNode::GetChildrenCount() const { int HeapGraphNode::GetChildrenCount() const {
IsDeadCheck("v8::HeapSnapshot::GetChildrenCount"); IsDeadCheck("v8::HeapSnapshot::GetChildrenCount");
return reinterpret_cast<const i::HeapEntry*>(this)->children()->length(); return ToInternal(this)->children().length();
} }
const HeapGraphEdge* HeapGraphNode::GetChild(int index) const { const HeapGraphEdge* HeapGraphNode::GetChild(int index) const {
IsDeadCheck("v8::HeapSnapshot::GetChild"); IsDeadCheck("v8::HeapSnapshot::GetChild");
return reinterpret_cast<const HeapGraphEdge*>( return reinterpret_cast<const HeapGraphEdge*>(
reinterpret_cast<const i::HeapEntry*>(this)->children()->at(index)); &ToInternal(this)->children()[index]);
} }
int HeapGraphNode::GetRetainersCount() const { int HeapGraphNode::GetRetainersCount() const {
IsDeadCheck("v8::HeapSnapshot::GetRetainersCount"); IsDeadCheck("v8::HeapSnapshot::GetRetainersCount");
return reinterpret_cast<const i::HeapEntry*>(this)->retainers()->length(); return ToInternal(this)->retainers().length();
} }
const HeapGraphEdge* HeapGraphNode::GetRetainer(int index) const { const HeapGraphEdge* HeapGraphNode::GetRetainer(int index) const {
IsDeadCheck("v8::HeapSnapshot::GetRetainer"); IsDeadCheck("v8::HeapSnapshot::GetRetainer");
return reinterpret_cast<const HeapGraphEdge*>( return reinterpret_cast<const HeapGraphEdge*>(
reinterpret_cast<const i::HeapEntry*>(this)->retainers()->at(index)); ToInternal(this)->retainers()[index]);
} }
int HeapGraphNode::GetRetainingPathsCount() const { int HeapGraphNode::GetRetainingPathsCount() const {
IsDeadCheck("v8::HeapSnapshot::GetRetainingPathsCount"); IsDeadCheck("v8::HeapSnapshot::GetRetainingPathsCount");
return const_cast<i::HeapEntry*>( return ToInternal(this)->GetRetainingPaths()->length();
reinterpret_cast<const i::HeapEntry*>(
this))->GetRetainingPaths()->length();
} }
const HeapGraphPath* HeapGraphNode::GetRetainingPath(int index) const { const HeapGraphPath* HeapGraphNode::GetRetainingPath(int index) const {
IsDeadCheck("v8::HeapSnapshot::GetRetainingPath"); IsDeadCheck("v8::HeapSnapshot::GetRetainingPath");
return reinterpret_cast<const HeapGraphPath*>( return reinterpret_cast<const HeapGraphPath*>(
const_cast<i::HeapEntry*>( ToInternal(this)->GetRetainingPaths()->at(index));
reinterpret_cast<const i::HeapEntry*>(
this))->GetRetainingPaths()->at(index));
} }
const HeapGraphNode* HeapSnapshotsDiff::GetAdditionsRoot() const { const HeapGraphNode* HeapSnapshotsDiff::GetAdditionsRoot() const {
IsDeadCheck("v8::HeapSnapshotsDiff::GetAdditionsRoot"); IsDeadCheck("v8::HeapSnapshotsDiff::GetAdditionsRoot");
const i::HeapSnapshotsDiff* diff = i::HeapSnapshotsDiff* diff =
reinterpret_cast<const i::HeapSnapshotsDiff*>(this); const_cast<i::HeapSnapshotsDiff*>(
reinterpret_cast<const i::HeapSnapshotsDiff*>(this));
return reinterpret_cast<const HeapGraphNode*>(diff->additions_root()); return reinterpret_cast<const HeapGraphNode*>(diff->additions_root());
} }
const HeapGraphNode* HeapSnapshotsDiff::GetDeletionsRoot() const { const HeapGraphNode* HeapSnapshotsDiff::GetDeletionsRoot() const {
IsDeadCheck("v8::HeapSnapshotsDiff::GetDeletionsRoot"); IsDeadCheck("v8::HeapSnapshotsDiff::GetDeletionsRoot");
const i::HeapSnapshotsDiff* diff = i::HeapSnapshotsDiff* diff =
reinterpret_cast<const i::HeapSnapshotsDiff*>(this); const_cast<i::HeapSnapshotsDiff*>(
reinterpret_cast<const i::HeapSnapshotsDiff*>(this));
return reinterpret_cast<const HeapGraphNode*>(diff->deletions_root()); return reinterpret_cast<const HeapGraphNode*>(diff->deletions_root());
} }
static i::HeapSnapshot* ToInternal(const HeapSnapshot* snapshot) {
return const_cast<i::HeapSnapshot*>(
reinterpret_cast<const i::HeapSnapshot*>(snapshot));
}
unsigned HeapSnapshot::GetUid() const { unsigned HeapSnapshot::GetUid() const {
IsDeadCheck("v8::HeapSnapshot::GetUid"); IsDeadCheck("v8::HeapSnapshot::GetUid");
return reinterpret_cast<const i::HeapSnapshot*>(this)->uid(); return ToInternal(this)->uid();
} }
Handle<String> HeapSnapshot::GetTitle() const { Handle<String> HeapSnapshot::GetTitle() const {
IsDeadCheck("v8::HeapSnapshot::GetTitle"); IsDeadCheck("v8::HeapSnapshot::GetTitle");
const i::HeapSnapshot* snapshot =
reinterpret_cast<const i::HeapSnapshot*>(this);
return Handle<String>(ToApi<String>(i::Factory::LookupAsciiSymbol( return Handle<String>(ToApi<String>(i::Factory::LookupAsciiSymbol(
snapshot->title()))); ToInternal(this)->title())));
} }
const HeapGraphNode* HeapSnapshot::GetRoot() const { const HeapGraphNode* HeapSnapshot::GetRoot() const {
IsDeadCheck("v8::HeapSnapshot::GetHead"); IsDeadCheck("v8::HeapSnapshot::GetHead");
const i::HeapSnapshot* snapshot = return reinterpret_cast<const HeapGraphNode*>(ToInternal(this)->root());
reinterpret_cast<const i::HeapSnapshot*>(this);
return reinterpret_cast<const HeapGraphNode*>(snapshot->const_root());
} }
const HeapSnapshotsDiff* HeapSnapshot::CompareWith( const HeapSnapshotsDiff* HeapSnapshot::CompareWith(
const HeapSnapshot* snapshot) const { const HeapSnapshot* snapshot) const {
IsDeadCheck("v8::HeapSnapshot::CompareWith"); IsDeadCheck("v8::HeapSnapshot::CompareWith");
i::HeapSnapshot* snapshot1 = const_cast<i::HeapSnapshot*>(
reinterpret_cast<const i::HeapSnapshot*>(this));
i::HeapSnapshot* snapshot2 = const_cast<i::HeapSnapshot*>(
reinterpret_cast<const i::HeapSnapshot*>(snapshot));
return reinterpret_cast<const HeapSnapshotsDiff*>( return reinterpret_cast<const HeapSnapshotsDiff*>(
snapshot1->CompareWith(snapshot2)); ToInternal(this)->CompareWith(ToInternal(snapshot)));
} }
......
...@@ -126,6 +126,13 @@ void List<T, P>::Iterate(void (*callback)(T* x)) { ...@@ -126,6 +126,13 @@ void List<T, P>::Iterate(void (*callback)(T* x)) {
} }
template<typename T, class P>
template<class Visitor>
void List<T, P>::Iterate(Visitor* visitor) {
for (int i = 0; i < length_; i++) visitor->Apply(&data_[i]);
}
template<typename T, class P> template<typename T, class P>
bool List<T, P>::Contains(const T& elm) { bool List<T, P>::Contains(const T& elm) {
for (int i = 0; i < length_; i++) { for (int i = 0; i < length_; i++) {
......
...@@ -117,6 +117,8 @@ class List { ...@@ -117,6 +117,8 @@ class List {
// Iterate through all list entries, starting at index 0. // Iterate through all list entries, starting at index 0.
void Iterate(void (*callback)(T* x)); void Iterate(void (*callback)(T* x));
template<class Visitor>
void Iterate(Visitor* visitor);
// Sort all list entries (using QuickSort) // Sort all list entries (using QuickSort)
void Sort(int (*cmp)(const T* x, const T* y)); void Sort(int (*cmp)(const T* x, const T* y));
......
...@@ -130,17 +130,6 @@ CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) { ...@@ -130,17 +130,6 @@ CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) {
} }
} }
template<class Visitor>
void HeapEntriesMap::Apply(Visitor* visitor) {
for (HashMap::Entry* p = entries_.Start();
p != NULL;
p = entries_.Next(p)) {
if (!IsAlias(p->value))
visitor->Apply(reinterpret_cast<HeapEntry*>(p->value));
}
}
} } // namespace v8::internal } } // namespace v8::internal
#endif // ENABLE_LOGGING_AND_PROFILING #endif // ENABLE_LOGGING_AND_PROFILING
......
...@@ -798,83 +798,102 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) { ...@@ -798,83 +798,102 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) {
} }
HeapGraphEdge::HeapGraphEdge(Type type, void HeapGraphEdge::Init(
const char* name, int child_index, Type type, const char* name, HeapEntry* to) {
HeapEntry* from, ASSERT(type == kContextVariable || type == kProperty || type == kInternal);
HeapEntry* to) child_index_ = child_index;
: type_(type), name_(name), from_(from), to_(to) { type_ = type;
ASSERT(type_ == CONTEXT_VARIABLE || type_ == PROPERTY || type_ == INTERNAL); name_ = name;
to_ = to;
} }
HeapGraphEdge::HeapGraphEdge(int index, void HeapGraphEdge::Init(int child_index, int index, HeapEntry* to) {
HeapEntry* from, child_index_ = child_index;
HeapEntry* to) type_ = kElement;
: type_(ELEMENT), index_(index), from_(from), to_(to) { index_ = index;
to_ = to;
} }
static void DeleteHeapGraphEdge(HeapGraphEdge** edge_ptr) { HeapEntry* HeapGraphEdge::From() {
delete *edge_ptr; return reinterpret_cast<HeapEntry*>(this - child_index_) - 1;
} }
static void DeleteHeapGraphPath(HeapGraphPath** path_ptr) { void HeapEntry::Init(HeapSnapshot* snapshot,
delete *path_ptr; int children_count,
int retainers_count) {
Init(snapshot, kInternal, "", 0, 0, children_count, retainers_count);
} }
HeapEntry::~HeapEntry() { void HeapEntry::Init(HeapSnapshot* snapshot,
children_.Iterate(DeleteHeapGraphEdge); Type type,
retaining_paths_.Iterate(DeleteHeapGraphPath); 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) { void HeapEntry::SetNamedReference(HeapGraphEdge::Type type,
children_.Add(edge); int child_index,
edge->to()->retainers_.Add(edge); 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) { void HeapEntry::SetElementReference(
AddEdge( int child_index, int index, HeapEntry* entry, int retainer_index) {
new HeapGraphEdge(HeapGraphEdge::CONTEXT_VARIABLE, name, this, entry)); 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) { void HeapEntry::SetUnidirElementReference(
AddEdge(new HeapGraphEdge(index, this, entry)); int child_index, int index, HeapEntry* entry) {
children_arr()[child_index].Init(child_index, index, entry);
} }
void HeapEntry::SetInternalReference(const char* name, HeapEntry* entry) { int HeapEntry::ReachableSize() {
AddEdge(new HeapGraphEdge(HeapGraphEdge::INTERNAL, name, this, entry)); if (calculated_data_index_ == kNoCalculatedData) {
} calculated_data_index_ = snapshot_->AddCalculatedData();
}
return snapshot_->GetCalculatedData(
void HeapEntry::SetPropertyReference(const char* name, HeapEntry* entry) { calculated_data_index_).ReachableSize(this);
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::TotalSize() { int HeapEntry::RetainedSize() {
return total_size_ != kUnknownSize ? total_size_ : CalculateTotalSize(); if (calculated_data_index_ == kNoCalculatedData) {
calculated_data_index_ = snapshot_->AddCalculatedData();
}
return snapshot_->GetCalculatedData(
calculated_data_index_).RetainedSize(this);
} }
int HeapEntry::NonSharedTotalSize() { List<HeapGraphPath*>* HeapEntry::GetRetainingPaths() {
return non_shared_total_size_ != kUnknownSize ? if (calculated_data_index_ == kNoCalculatedData) {
non_shared_total_size_ : CalculateNonSharedTotalSize(); calculated_data_index_ = snapshot_->AddCalculatedData();
}
return snapshot_->GetCalculatedData(
calculated_data_index_).GetRetainingPaths(this);
} }
...@@ -882,16 +901,16 @@ template<class Visitor> ...@@ -882,16 +901,16 @@ template<class Visitor>
void HeapEntry::ApplyAndPaintAllReachable(Visitor* visitor) { void HeapEntry::ApplyAndPaintAllReachable(Visitor* visitor) {
List<HeapEntry*> list(10); List<HeapEntry*> list(10);
list.Add(this); list.Add(this);
this->PaintReachable(); this->paint_reachable();
visitor->Apply(this); visitor->Apply(this);
while (!list.is_empty()) { while (!list.is_empty()) {
HeapEntry* entry = list.RemoveLast(); HeapEntry* entry = list.RemoveLast();
const int children_count = entry->children_.length(); Vector<HeapGraphEdge> children = entry->children();
for (int i = 0; i < children_count; ++i) { for (int i = 0; i < children.length(); ++i) {
HeapEntry* child = entry->children_[i]->to(); HeapEntry* child = children[i].to();
if (!child->painted_reachable()) { if (!child->painted_reachable()) {
list.Add(child); list.Add(child);
child->PaintReachable(); child->paint_reachable();
visitor->Apply(child); visitor->Apply(child);
} }
} }
...@@ -910,78 +929,158 @@ void HeapEntry::PaintAllReachable() { ...@@ -910,78 +929,158 @@ void HeapEntry::PaintAllReachable() {
} }
class TotalSizeCalculator { void HeapEntry::Print(int max_depth, int indent) {
public: OS::Print("%6d %6d %6d [%ld] ",
TotalSizeCalculator() self_size(), ReachableSize(), RetainedSize(), id_);
: total_size_(0) { 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) { const char* HeapEntry::TypeAsString() {
total_size_ += entry->self_size(); 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() { int HeapEntry::EntriesSize(int entries_count,
snapshot_->ClearPaint(); int children_count,
TotalSizeCalculator calc; int retainers_count) {
ApplyAndPaintAllReachable(&calc); return sizeof(HeapEntry) * entries_count // NOLINT
total_size_ = calc.total_size(); + sizeof(HeapGraphEdge) * children_count // NOLINT
return total_size_; + 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: public:
NonSharedSizeCalculator() ReachableSizeCalculator()
: non_shared_total_size_(0) { : 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) { void Apply(HeapEntry* entry) {
if (entry->painted_reachable()) { reachable_size_ += entry->self_size();
non_shared_total_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: private:
int non_shared_total_size_; int retained_size_;
}; };
int HeapEntry::CalculateNonSharedTotalSize() { void HeapEntryCalculatedData::CalculateSizes(HeapEntry* entry) {
// To calculate non-shared total size, first we paint all reachable // To calculate retained size, first we paint all reachable nodes in
// nodes in one color, then we paint all nodes reachable from other // one color (and calculate reachable size as a byproduct), then we
// nodes with a different color. Then we consider only nodes painted // paint (or re-paint) all nodes reachable from other nodes with a
// with the first color for calculating the total size. // different color. Then we consider only nodes painted with the
snapshot_->ClearPaint(); // first color for calculating the retained size.
PaintAllReachable(); entry->snapshot()->ClearPaint();
ReachableSizeCalculator rch_size_calc;
entry->ApplyAndPaintAllReachable(&rch_size_calc);
reachable_size_ = rch_size_calc.reachable_size();
List<HeapEntry*> list(10); List<HeapEntry*> list(10);
if (this != snapshot_->root()) { HeapEntry* root = entry->snapshot()->root();
list.Add(snapshot_->root()); if (entry != root) {
snapshot_->root()->PaintReachableFromOthers(); list.Add(root);
root->paint_reachable_from_others();
} }
while (!list.is_empty()) { while (!list.is_empty()) {
HeapEntry* entry = list.RemoveLast(); HeapEntry* curr = list.RemoveLast();
const int children_count = entry->children_.length(); Vector<HeapGraphEdge> children = curr->children();
for (int i = 0; i < children_count; ++i) { for (int i = 0; i < children.length(); ++i) {
HeapEntry* child = entry->children_[i]->to(); HeapEntry* child = children[i].to();
if (child != this && child->not_painted_reachable_from_others()) { if (child != entry && child->not_painted_reachable_from_others()) {
list.Add(child); list.Add(child);
child->PaintReachableFromOthers(); child->paint_reachable_from_others();
} }
} }
} }
NonSharedSizeCalculator calculator; RetainedSizeCalculator ret_size_calc;
snapshot_->IterateEntries(&calculator); entry->snapshot()->IterateEntries(&ret_size_calc);
non_shared_total_size_ = calculator.non_shared_total_size(); retained_size_ = ret_size_calc.reained_size();
return non_shared_total_size_;
} }
...@@ -1019,124 +1118,33 @@ class CachedHeapGraphPath { ...@@ -1019,124 +1118,33 @@ class CachedHeapGraphPath {
}; };
const List<HeapGraphPath*>* HeapEntry::GetRetainingPaths() { List<HeapGraphPath*>* HeapEntryCalculatedData::GetRetainingPaths(
if (retaining_paths_.length() == 0 && retainers_.length() != 0) { HeapEntry* entry) {
if (retaining_paths_ == NULL) retaining_paths_ = new List<HeapGraphPath*>(4);
if (retaining_paths_->length() == 0 && entry->retainers().length() != 0) {
CachedHeapGraphPath path; CachedHeapGraphPath path;
FindRetainingPaths(this, &path); FindRetainingPaths(entry, &path);
} }
return &retaining_paths_; return retaining_paths_;
} }
void HeapEntry::FindRetainingPaths(HeapEntry* node, void HeapEntryCalculatedData::FindRetainingPaths(
CachedHeapGraphPath* prev_path) { HeapEntry* entry,
for (int i = 0; i < node->retainers_.length(); ++i) { CachedHeapGraphPath* prev_path) {
HeapGraphEdge* ret_edge = node->retainers_[i]; Vector<HeapGraphEdge*> retainers = entry->retainers();
if (prev_path->ContainsNode(ret_edge->from())) continue; for (int i = 0; i < retainers.length(); ++i) {
if (ret_edge->from() != snapshot_->root()) { HeapGraphEdge* ret_edge = retainers[i];
if (prev_path->ContainsNode(ret_edge->From())) continue;
if (ret_edge->From() != entry->snapshot()->root()) {
CachedHeapGraphPath path(*prev_path); CachedHeapGraphPath path(*prev_path);
path.Add(ret_edge); path.Add(ret_edge);
FindRetainingPaths(ret_edge->from(), &path); FindRetainingPaths(ret_edge->From(), &path);
} 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);
}
}
}
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;
} }
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) ...@@ -1151,21 +1159,21 @@ HeapGraphPath::HeapGraphPath(const List<HeapGraphEdge*>& path)
void HeapGraphPath::Print() { void HeapGraphPath::Print() {
path_[0]->from()->Print(1, 0); path_[0]->From()->Print(1, 0);
for (int i = 0; i < path_.length(); ++i) { for (int i = 0; i < path_.length(); ++i) {
OS::Print(" -> "); OS::Print(" -> ");
HeapGraphEdge* edge = path_[i]; HeapGraphEdge* edge = path_[i];
switch (edge->type()) { switch (edge->type()) {
case HeapGraphEdge::CONTEXT_VARIABLE: case HeapGraphEdge::kContextVariable:
OS::Print("[#%s] ", edge->name()); OS::Print("[#%s] ", edge->name());
break; break;
case HeapGraphEdge::ELEMENT: case HeapGraphEdge::kElement:
OS::Print("[%d] ", edge->index()); OS::Print("[%d] ", edge->index());
break; break;
case HeapGraphEdge::INTERNAL: case HeapGraphEdge::kInternal:
OS::Print("[$%s] ", edge->name()); OS::Print("[$%s] ", edge->name());
break; break;
case HeapGraphEdge::PROPERTY: case HeapGraphEdge::kProperty:
OS::Print("[%s] ", edge->name()); OS::Print("[%s] ", edge->name());
break; break;
default: default:
...@@ -1177,76 +1185,8 @@ void HeapGraphPath::Print() { ...@@ -1177,76 +1185,8 @@ void HeapGraphPath::Print() {
} }
class IndexedReferencesExtractor : public ObjectVisitor { HeapObject *const HeapSnapshot::kInternalRootObject =
public: reinterpret_cast<HeapObject*>(1);
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;
}
HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection, HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection,
const char* title, const char* title,
...@@ -1254,176 +1194,151 @@ HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection, ...@@ -1254,176 +1194,151 @@ HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection,
: collection_(collection), : collection_(collection),
title_(title), title_(title),
uid_(uid), uid_(uid),
root_(this), root_entry_index_(-1),
sorted_entries_(NULL) { raw_entries_(NULL),
entries_sorted_(false) {
} }
HeapSnapshot::~HeapSnapshot() { static void DisposeCalculatedData(HeapEntryCalculatedData* cdata) {
delete sorted_entries_; cdata->Dispose();
} }
HeapSnapshot::~HeapSnapshot() {
void HeapSnapshot::ClearPaint() { DeleteArray(raw_entries_);
root_.ClearPaint(); calculated_data_.Iterate(DisposeCalculatedData);
entries_.Apply(&HeapEntry::ClearPaint);
} }
HeapEntry* HeapSnapshot::GetEntry(Object* obj) { void HeapSnapshot::AllocateEntries(int entries_count,
if (!obj->IsHeapObject()) return NULL; int children_count,
HeapObject* object = HeapObject::cast(obj); 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. HeapEntry* HeapSnapshot::AddEntry(HeapObject* object,
if (object->IsJSFunction()) { 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); JSFunction* func = JSFunction::cast(object);
SharedFunctionInfo* shared = func->shared(); SharedFunctionInfo* shared = func->shared();
String* name = String::cast(shared->name())->length() > 0 ? String* name = String::cast(shared->name())->length() > 0 ?
String::cast(shared->name()) : shared->inferred_name(); 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()) { } else if (object->IsJSObject()) {
return AddEntry(object, return AddEntry(object,
HeapEntry::OBJECT, HeapEntry::kObject,
collection_->GetName( collection_->GetName(
JSObject::cast(object)->constructor_name())); JSObject::cast(object)->constructor_name()),
} else if (object->IsJSGlobalPropertyCell()) { children_count,
HeapEntry* value = GetEntry(JSGlobalPropertyCell::cast(object)->value()); retainers_count);
// 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;
} else if (object->IsString()) { } else if (object->IsString()) {
return AddEntry(object, return AddEntry(object,
HeapEntry::STRING, HeapEntry::kString,
collection_->GetName(String::cast(object))); collection_->GetName(String::cast(object)),
children_count,
retainers_count);
} else if (object->IsCode()) { } else if (object->IsCode()) {
return AddEntry(object, HeapEntry::CODE); return AddEntry(object,
HeapEntry::kCode,
"",
children_count,
retainers_count);
} else if (object->IsSharedFunctionInfo()) { } else if (object->IsSharedFunctionInfo()) {
SharedFunctionInfo* shared = SharedFunctionInfo::cast(object); SharedFunctionInfo* shared = SharedFunctionInfo::cast(object);
String* name = String::cast(shared->name())->length() > 0 ? String* name = String::cast(shared->name())->length() > 0 ?
String::cast(shared->name()) : shared->inferred_name(); 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()) { } else if (object->IsScript()) {
Script* script = Script::cast(object); Script* script = Script::cast(object);
return AddEntry(object, return AddEntry(object,
HeapEntry::CODE, HeapEntry::kCode,
script->name()->IsString() ? script->name()->IsString() ?
collection_->GetName(String::cast(script->name())) : ""); collection_->GetName(String::cast(script->name())) : "",
children_count,
retainers_count);
} else if (object->IsFixedArray()) { } else if (object->IsFixedArray()) {
return AddEntry(object, HeapEntry::ARRAY); return AddEntry(object,
HeapEntry::kArray,
"",
children_count,
retainers_count);
} }
// No interest in this object. // No interest in this object.
return NULL; return NULL;
} }
void HeapSnapshot::SetClosureReference(HeapEntry* parent, bool HeapSnapshot::WillAddEntry(HeapObject* object) {
String* reference_name, return object == kInternalRootObject
Object* child) { || object->IsJSFunction()
HeapEntry* child_entry = GetEntry(child); || object->IsJSObject()
if (child_entry != NULL) { || object->IsString()
parent->SetClosureReference( || object->IsCode()
collection_->GetName(reference_name), child_entry); || object->IsSharedFunctionInfo()
} || object->IsScript()
|| object->IsFixedArray();
} }
void HeapSnapshot::SetElementReference(HeapEntry* parent, static void HeapEntryClearPaint(HeapEntry** entry_ptr) {
int index, (*entry_ptr)->clear_paint();
Object* child) {
HeapEntry* child_entry = GetEntry(child);
if (child_entry != NULL) {
parent->SetElementReference(index, child_entry);
}
} }
void HeapSnapshot::ClearPaint() {
void HeapSnapshot::SetInternalReference(HeapEntry* parent, entries_.Iterate(HeapEntryClearPaint);
const char* reference_name,
Object* child) {
HeapEntry* child_entry = GetEntry(child);
if (child_entry != NULL) {
parent->SetInternalReference(reference_name, child_entry);
}
} }
void HeapSnapshot::SetPropertyReference(HeapEntry* parent, int HeapSnapshot::AddCalculatedData() {
String* reference_name, calculated_data_.Add(HeapEntryCalculatedData());
Object* child) { return calculated_data_.length() - 1;
HeapEntry* child_entry = GetEntry(child);
if (child_entry != NULL) {
parent->SetPropertyReference(
collection_->GetName(reference_name), child_entry);
}
} }
HeapEntry* HeapSnapshot::AddEntry(HeapObject* object, HeapEntry* HeapSnapshot::AddEntry(HeapObject* object,
HeapEntry::Type type, HeapEntry::Type type,
const char* name) { const char* name,
HeapEntry* entry = new HeapEntry(this, int children_count,
type, int retainers_count) {
name, HeapEntry* entry = GetNextEntryToInit();
collection_->GetObjectId(object->address()), entry->Init(this,
GetObjectSize(object), type,
GetObjectSecurityToken(object)); name,
entries_.Pair(object, entry); collection_->GetObjectId(object->address()),
GetObjectSize(object),
// Detect, if this is a JS global object of the current context, and children_count,
// add it to snapshot's roots. There can be several JS global objects retainers_count);
// 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);
}
}
return entry; return entry;
} }
class EdgesCutter { HeapEntry* HeapSnapshot::GetNextEntryToInit() {
public: if (entries_.length() > 0) {
explicit EdgesCutter(int global_security_token) HeapEntry* last_entry = entries_.last();
: global_security_token_(global_security_token) { entries_.Add(reinterpret_cast<HeapEntry*>(
} reinterpret_cast<char*>(last_entry) + last_entry->EntrySize()));
} else {
void Apply(HeapEntry* entry) { entries_.Add(reinterpret_cast<HeapEntry*>(raw_entries_));
if (entry->security_token_id() != TokenEnumerator::kNoSecurityToken
&& entry->security_token_id() != global_security_token_) {
entry->CutEdges();
}
} }
return entries_.last();
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());
} }
...@@ -1433,16 +1348,6 @@ int HeapSnapshot::GetObjectSize(HeapObject* obj) { ...@@ -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 HeapSnapshot::CalculateNetworkSize(JSObject* obj) {
int size = obj->Size(); int size = obj->Size();
// If 'properties' and 'elements' are non-empty (thus, non-shared), // If 'properties' and 'elements' are non-empty (thus, non-shared),
...@@ -1467,15 +1372,10 @@ int HeapSnapshot::CalculateNetworkSize(JSObject* obj) { ...@@ -1467,15 +1372,10 @@ int HeapSnapshot::CalculateNetworkSize(JSObject* obj) {
} }
class EntriesCollector { HeapSnapshotsDiff* HeapSnapshot::CompareWith(HeapSnapshot* snapshot) {
public: return collection_->CompareSnapshots(this, snapshot);
explicit EntriesCollector(List<HeapEntry*>* list) : list_(list) { } }
void Apply(HeapEntry* entry) {
list_->Add(entry);
}
private:
List<HeapEntry*>* list_;
};
template<class T> template<class T>
static int SortByIds(const T* entry1_ptr, static int SortByIds(const T* entry1_ptr,
...@@ -1485,22 +1385,16 @@ static int SortByIds(const T* entry1_ptr, ...@@ -1485,22 +1385,16 @@ static int SortByIds(const T* entry1_ptr,
} }
List<HeapEntry*>* HeapSnapshot::GetSortedEntriesList() { List<HeapEntry*>* HeapSnapshot::GetSortedEntriesList() {
if (sorted_entries_ != NULL) return sorted_entries_; if (!entries_sorted_) {
sorted_entries_ = new List<HeapEntry*>(entries_.capacity()); entries_.Sort(SortByIds);
EntriesCollector collector(sorted_entries_); entries_sorted_ = true;
entries_.Apply(&collector); }
sorted_entries_->Sort(SortByIds); return &entries_;
return sorted_entries_;
}
HeapSnapshotsDiff* HeapSnapshot::CompareWith(HeapSnapshot* snapshot) {
return collection_->CompareSnapshots(this, snapshot);
} }
void HeapSnapshot::Print(int max_depth) { void HeapSnapshot::Print(int max_depth) {
root_.Print(max_depth, 0); root()->Print(max_depth, 0);
} }
...@@ -1635,53 +1529,343 @@ HeapSnapshotsDiff* HeapSnapshotsCollection::CompareSnapshots( ...@@ -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) 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() { void HeapSnapshotGenerator::GenerateSnapshot() {
AssertNoAllocation no_alloc; AssertNoAllocation no_alloc;
// Iterate heap contents. // Pass 1. Iterate heap contents to count entries and references.
HeapIterator iterator; SnapshotCounter counter(&entries_);
for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) { filler_ = &counter;
filler_->AddEntry(HeapSnapshot::kInternalRootObject);
HeapIterator iterator1;
for (HeapObject* obj = iterator1.next();
obj != NULL;
obj = iterator1.next()) {
ExtractReferences(obj); 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) { void HeapSnapshotGenerator::ExtractReferences(HeapObject* obj) {
HeapEntry* entry = snapshot_->GetEntry(obj); // We need to reference JS global objects from snapshot's root.
if (entry == NULL) return; // We also need to only include global objects from the current
if (entry->visited()) return; // 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()) { if (obj->IsJSObject()) {
JSObject* js_obj = JSObject::cast(obj); JSObject* js_obj = JSObject::cast(obj);
ExtractClosureReferences(js_obj, entry); ExtractClosureReferences(js_obj, entry);
ExtractPropertyReferences(js_obj, entry); ExtractPropertyReferences(js_obj, entry);
ExtractElementReferences(js_obj, entry); ExtractElementReferences(js_obj, entry);
snapshot_->SetPropertyReference( SetPropertyReference(
entry, Heap::prototype_symbol(), js_obj->map()->prototype()); obj, entry, Heap::prototype_symbol(), js_obj->map()->prototype());
} else if (obj->IsJSGlobalPropertyCell()) {
JSGlobalPropertyCell* cell = JSGlobalPropertyCell::cast(obj);
snapshot_->SetElementReference(entry, 0, cell->value());
} else if (obj->IsString()) { } else if (obj->IsString()) {
if (obj->IsConsString()) { if (obj->IsConsString()) {
ConsString* cs = ConsString::cast(obj); ConsString* cs = ConsString::cast(obj);
snapshot_->SetElementReference(entry, 0, cs->first()); SetElementReference(obj, entry, 0, cs->first());
snapshot_->SetElementReference(entry, 1, cs->second()); SetElementReference(obj, entry, 1, cs->second());
} }
} else if (obj->IsCode() || obj->IsSharedFunctionInfo() || obj->IsScript()) { } else if (obj->IsCode() || obj->IsSharedFunctionInfo() || obj->IsScript()) {
IndexedReferencesExtractor refs_extractor(snapshot_, entry); IndexedReferencesExtractor refs_extractor(this, obj, entry);
obj->Iterate(&refs_extractor); obj->Iterate(&refs_extractor);
} else if (obj->IsFixedArray()) { } else if (obj->IsFixedArray()) {
IndexedReferencesExtractor refs_extractor(snapshot_, entry); IndexedReferencesExtractor refs_extractor(this, obj, entry);
obj->Iterate(&refs_extractor); obj->Iterate(&refs_extractor);
} }
entry->MarkAsVisited();
} }
...@@ -1700,10 +1884,10 @@ void HeapSnapshotGenerator::ExtractClosureReferences(JSObject* js_obj, ...@@ -1700,10 +1884,10 @@ void HeapSnapshotGenerator::ExtractClosureReferences(JSObject* js_obj,
String* local_name = *zone_scope_info.LocalName(i); String* local_name = *zone_scope_info.LocalName(i);
int idx = serialized_scope_info->ContextSlotIndex(local_name, NULL); int idx = serialized_scope_info->ContextSlotIndex(local_name, NULL);
if (idx >= 0 && idx < context->length()) { 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, ...@@ -1716,13 +1900,13 @@ void HeapSnapshotGenerator::ExtractPropertyReferences(JSObject* js_obj,
switch (descs->GetType(i)) { switch (descs->GetType(i)) {
case FIELD: { case FIELD: {
int index = descs->GetFieldIndex(i); int index = descs->GetFieldIndex(i);
snapshot_->SetPropertyReference( SetPropertyReference(
entry, descs->GetKey(i), js_obj->FastPropertyAt(index)); js_obj, entry, descs->GetKey(i), js_obj->FastPropertyAt(index));
break; break;
} }
case CONSTANT_FUNCTION: case CONSTANT_FUNCTION:
snapshot_->SetPropertyReference( SetPropertyReference(
entry, descs->GetKey(i), descs->GetConstantFunction(i)); js_obj, entry, descs->GetKey(i), descs->GetConstantFunction(i));
break; break;
default: ; default: ;
} }
...@@ -1733,8 +1917,8 @@ void HeapSnapshotGenerator::ExtractPropertyReferences(JSObject* js_obj, ...@@ -1733,8 +1917,8 @@ void HeapSnapshotGenerator::ExtractPropertyReferences(JSObject* js_obj,
for (int i = 0; i < length; ++i) { for (int i = 0; i < length; ++i) {
Object* k = dictionary->KeyAt(i); Object* k = dictionary->KeyAt(i);
if (dictionary->IsKey(k)) { if (dictionary->IsKey(k)) {
snapshot_->SetPropertyReference( SetPropertyReference(
entry, String::cast(k), dictionary->ValueAt(i)); js_obj, entry, String::cast(k), dictionary->ValueAt(i));
} }
} }
} }
...@@ -1750,7 +1934,7 @@ void HeapSnapshotGenerator::ExtractElementReferences(JSObject* js_obj, ...@@ -1750,7 +1934,7 @@ void HeapSnapshotGenerator::ExtractElementReferences(JSObject* js_obj,
elements->length(); elements->length();
for (int i = 0; i < length; ++i) { for (int i = 0; i < length; ++i) {
if (!elements->get(i)->IsTheHole()) { 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()) { } else if (js_obj->HasDictionaryElements()) {
...@@ -1761,13 +1945,90 @@ void HeapSnapshotGenerator::ExtractElementReferences(JSObject* js_obj, ...@@ -1761,13 +1945,90 @@ void HeapSnapshotGenerator::ExtractElementReferences(JSObject* js_obj,
if (dictionary->IsKey(k)) { if (dictionary->IsKey(k)) {
ASSERT(k->IsNumber()); ASSERT(k->IsNumber());
uint32_t index = static_cast<uint32_t>(k->Number()); 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) { static void DeleteHeapSnapshotsDiff(HeapSnapshotsDiff** diff_ptr) {
delete *diff_ptr; delete *diff_ptr;
} }
...@@ -1779,8 +2040,6 @@ HeapSnapshotsComparator::~HeapSnapshotsComparator() { ...@@ -1779,8 +2040,6 @@ HeapSnapshotsComparator::~HeapSnapshotsComparator() {
HeapSnapshotsDiff* HeapSnapshotsComparator::Compare(HeapSnapshot* snapshot1, HeapSnapshotsDiff* HeapSnapshotsComparator::Compare(HeapSnapshot* snapshot1,
HeapSnapshot* snapshot2) { HeapSnapshot* snapshot2) {
HeapSnapshotsDiff* diff = new HeapSnapshotsDiff(snapshot1, snapshot2);
diffs_.Add(diff);
List<HeapEntry*>* entries1 = snapshot1->GetSortedEntriesList(); List<HeapEntry*>* entries1 = snapshot1->GetSortedEntriesList();
List<HeapEntry*>* entries2 = snapshot2->GetSortedEntriesList(); List<HeapEntry*>* entries2 = snapshot2->GetSortedEntriesList();
int i = 0, j = 0; int i = 0, j = 0;
...@@ -1810,17 +2069,33 @@ HeapSnapshotsDiff* HeapSnapshotsComparator::Compare(HeapSnapshot* snapshot1, ...@@ -1810,17 +2069,33 @@ HeapSnapshotsDiff* HeapSnapshotsComparator::Compare(HeapSnapshot* snapshot1,
snapshot1->ClearPaint(); snapshot1->ClearPaint();
snapshot1->root()->PaintAllReachable(); 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) { for (int i = 0; i < deleted_entries.length(); ++i) {
HeapEntry* entry = deleted_entries[i]; HeapEntry* entry = deleted_entries[i];
if (entry->painted_reachable()) if (entry->painted_reachable())
diff->AddDeletedEntry(entry); diff->AddDeletedEntry(del_child_index++, deleted_entry_index++, entry);
} }
snapshot2->ClearPaint(); int add_child_index = 0, added_entry_index = 1;
snapshot2->root()->PaintAllReachable();
for (int i = 0; i < added_entries.length(); ++i) { for (int i = 0; i < added_entries.length(); ++i) {
HeapEntry* entry = added_entries[i]; HeapEntry* entry = added_entries[i];
if (entry->painted_reachable()) if (entry->painted_reachable())
diff->AddAddedEntry(entry); diff->AddAddedEntry(add_child_index++, added_entry_index++, entry);
} }
return diff; return diff;
} }
......
...@@ -423,167 +423,194 @@ class ProfileGenerator { ...@@ -423,167 +423,194 @@ class ProfileGenerator {
}; };
class HeapSnapshot;
class HeapEntry; class HeapEntry;
class HeapGraphEdge BASE_EMBEDDED {
class HeapGraphEdge {
public: public:
enum Type { enum Type {
CONTEXT_VARIABLE = v8::HeapGraphEdge::CONTEXT_VARIABLE, kContextVariable = v8::HeapGraphEdge::kContextVariable,
ELEMENT = v8::HeapGraphEdge::ELEMENT, kElement = v8::HeapGraphEdge::kElement,
PROPERTY = v8::HeapGraphEdge::PROPERTY, kProperty = v8::HeapGraphEdge::kProperty,
INTERNAL = v8::HeapGraphEdge::INTERNAL kInternal = v8::HeapGraphEdge::kInternal
}; };
HeapGraphEdge(Type type, const char* name, HeapEntry* from, HeapEntry* to); HeapGraphEdge() { }
HeapGraphEdge(int index, HeapEntry* from, HeapEntry* to); void Init(int child_index, Type type, const char* name, HeapEntry* to);
void Init(int child_index, int index, HeapEntry* to);
Type type() const { return type_; } Type type() { return type_; }
int index() const { int index() {
ASSERT(type_ == ELEMENT); ASSERT(type_ == kElement);
return index_; return index_;
} }
const char* name() const { const char* name() {
ASSERT(type_ == CONTEXT_VARIABLE || type_ == PROPERTY || type_ == INTERNAL); ASSERT(type_ == kContextVariable
|| type_ == kProperty
|| type_ == kInternal);
return name_; return name_;
} }
HeapEntry* from() const { return from_; } HeapEntry* to() { return to_; }
HeapEntry* to() const { return to_; }
HeapEntry* From();
private: private:
Type type_; int child_index_ : 30;
Type type_ : 2;
union { union {
int index_; int index_;
const char* name_; const char* name_;
}; };
HeapEntry* from_;
HeapEntry* to_; HeapEntry* to_;
DISALLOW_COPY_AND_ASSIGN(HeapGraphEdge); DISALLOW_COPY_AND_ASSIGN(HeapGraphEdge);
}; };
class HeapGraphPath;
class CachedHeapGraphPath; class CachedHeapGraphPath;
class HeapGraphPath;
class HeapSnapshot;
class HeapEntry { // HeapEntry instances represent an entity from the heap (or a special
// virtual node, e.g. root). To make heap snapshots more compact,
// HeapEntries has a special memory layout (no Vectors or Lists used):
//
// +-----------------+
// HeapEntry
// +-----------------+
// HeapGraphEdge |
// ... } children_count
// HeapGraphEdge |
// +-----------------+
// HeapGraphEdge* |
// ... } retainers_count
// HeapGraphEdge* |
// +-----------------+
//
// In a HeapSnapshot, all entries are hand-allocated in a continuous array
// of raw bytes.
//
class HeapEntry BASE_EMBEDDED {
public: public:
enum Type { enum Type {
INTERNAL = v8::HeapGraphNode::INTERNAL, kInternal = v8::HeapGraphNode::kInternal,
ARRAY = v8::HeapGraphNode::ARRAY, kArray = v8::HeapGraphNode::kArray,
STRING = v8::HeapGraphNode::STRING, kString = v8::HeapGraphNode::kString,
OBJECT = v8::HeapGraphNode::OBJECT, kObject = v8::HeapGraphNode::kObject,
CODE = v8::HeapGraphNode::CODE, kCode = v8::HeapGraphNode::kCode,
CLOSURE = v8::HeapGraphNode::CLOSURE kClosure = v8::HeapGraphNode::kClosure
}; };
explicit HeapEntry(HeapSnapshot* snapshot) HeapEntry() { }
: snapshot_(snapshot), void Init(HeapSnapshot* snapshot, int children_count, int retainers_count);
visited_(false), void Init(HeapSnapshot* snapshot,
type_(INTERNAL),
name_(""),
id_(0),
next_auto_index_(0),
self_size_(0),
security_token_id_(TokenEnumerator::kNoSecurityToken),
children_(1),
retainers_(0),
retaining_paths_(0),
total_size_(kUnknownSize),
non_shared_total_size_(kUnknownSize),
painted_(kUnpainted) { }
HeapEntry(HeapSnapshot* snapshot,
Type type, Type type,
const char* name, const char* name,
uint64_t id, uint64_t id,
int self_size, int self_size,
int security_token_id) int children_count,
: snapshot_(snapshot), int retainers_count);
visited_(false),
type_(type), HeapSnapshot* snapshot() { return snapshot_; }
name_(name), Type type() { return type_; }
id_(id), const char* name() { return name_; }
next_auto_index_(1), uint64_t id() { return id_; }
self_size_(self_size), int self_size() { return self_size_; }
security_token_id_(security_token_id),
children_(4), Vector<HeapGraphEdge> children() {
retainers_(4), return Vector<HeapGraphEdge>(children_arr(), children_count_); }
retaining_paths_(4), Vector<HeapGraphEdge*> retainers() {
total_size_(kUnknownSize), return Vector<HeapGraphEdge*>(retainers_arr(), retainers_count_); }
non_shared_total_size_(kUnknownSize), List<HeapGraphPath*>* GetRetainingPaths();
painted_(kUnpainted) { }
~HeapEntry(); void clear_paint() { painted_ = kUnpainted; }
bool painted_reachable() { return painted_ == kPainted; }
bool visited() const { return visited_; } void paint_reachable() {
Type type() const { return type_; } ASSERT(painted_ == kUnpainted);
const char* name() const { return name_; } painted_ = kPainted;
uint64_t id() const { return id_; } }
int self_size() const { return self_size_; }
int security_token_id() const { return security_token_id_; }
bool painted_reachable() { return painted_ == kPaintReachable; }
bool not_painted_reachable_from_others() { bool not_painted_reachable_from_others() {
return painted_ != kPaintReachableFromOthers; return painted_ != kPaintedReachableFromOthers;
}
void paint_reachable_from_others() {
painted_ = kPaintedReachableFromOthers;
} }
const List<HeapGraphEdge*>* children() const { return &children_; }
const List<HeapGraphEdge*>* retainers() const { return &retainers_; }
const List<HeapGraphPath*>* GetRetainingPaths();
template<class Visitor> template<class Visitor>
void ApplyAndPaintAllReachable(Visitor* visitor); void ApplyAndPaintAllReachable(Visitor* visitor);
void ClearPaint() { painted_ = kUnpainted; }
void CutEdges();
void MarkAsVisited() { visited_ = true; }
void PaintAllReachable(); void PaintAllReachable();
void PaintReachable() {
ASSERT(painted_ == kUnpainted);
painted_ = kPaintReachable;
}
void PaintReachableFromOthers() { painted_ = kPaintReachableFromOthers; }
void SetClosureReference(const char* name, HeapEntry* entry);
void SetElementReference(int index, HeapEntry* entry);
void SetInternalReference(const char* name, HeapEntry* entry);
void SetPropertyReference(const char* name, HeapEntry* entry);
void SetAutoIndexReference(HeapEntry* entry);
void SetUnidirAutoIndexReference(HeapEntry* entry);
int TotalSize(); void SetElementReference(
int NonSharedTotalSize(); int child_index, int index, HeapEntry* entry, int retainer_index);
void SetNamedReference(HeapGraphEdge::Type type,
int child_index,
const char* name,
HeapEntry* entry,
int retainer_index);
void SetUnidirElementReference(int child_index, int index, HeapEntry* entry);
int EntrySize() { return EntriesSize(1, children_count_, retainers_count_); }
int ReachableSize();
int RetainedSize();
void Print(int max_depth, int indent); void Print(int max_depth, int indent);
private: static int EntriesSize(int entries_count,
void AddEdge(HeapGraphEdge* edge); int children_count,
int CalculateTotalSize(); int retainers_count);
int CalculateNonSharedTotalSize();
void FindRetainingPaths(HeapEntry* node, CachedHeapGraphPath* prev_path);
void RemoveChild(HeapGraphEdge* edge);
void RemoveRetainer(HeapGraphEdge* edge);
private:
HeapGraphEdge* children_arr() {
return reinterpret_cast<HeapGraphEdge*>(this + 1);
}
HeapGraphEdge** retainers_arr() {
return reinterpret_cast<HeapGraphEdge**>(children_arr() + children_count_);
}
const char* TypeAsString(); const char* TypeAsString();
HeapSnapshot* snapshot_; HeapSnapshot* snapshot_;
bool visited_; unsigned painted_: 2;
Type type_; Type type_: 3;
// The calculated data is stored in HeapSnapshot in HeapEntryCalculatedData
// entries. See AddCalculatedData and GetCalculatedData.
int calculated_data_index_: 27;
const char* name_; const char* name_;
uint64_t id_; uint64_t id_;
int next_auto_index_;
int self_size_; int self_size_;
int security_token_id_; int children_count_;
List<HeapGraphEdge*> children_; int retainers_count_;
List<HeapGraphEdge*> retainers_;
List<HeapGraphPath*> retaining_paths_; static const unsigned kUnpainted = 0;
int total_size_; static const unsigned kPainted = 1;
int non_shared_total_size_; static const unsigned kPaintedReachableFromOthers = 2;
int painted_; static const int kNoCalculatedData = -1;
DISALLOW_COPY_AND_ASSIGN(HeapEntry);
};
class HeapEntryCalculatedData {
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_;
int reachable_size_;
int retained_size_;
static const int kUnknownSize = -1; static const int kUnknownSize = -1;
static const int kUnpainted = 0;
static const int kPaintReachable = 1;
static const int kPaintReachableFromOthers = 2;
DISALLOW_IMPLICIT_CONSTRUCTORS(HeapEntry); // Allow generated copy constructor and assignment operator.
}; };
...@@ -595,7 +622,7 @@ class HeapGraphPath { ...@@ -595,7 +622,7 @@ class HeapGraphPath {
void Add(HeapGraphEdge* edge) { path_.Add(edge); } void Add(HeapGraphEdge* edge) { path_.Add(edge); }
void Set(int index, HeapGraphEdge* edge) { path_[index] = edge; } void Set(int index, HeapGraphEdge* edge) { path_[index] = edge; }
const List<HeapGraphEdge*>* path() const { return &path_; } const List<HeapGraphEdge*>* path() { return &path_; }
void Print(); void Print();
...@@ -606,39 +633,6 @@ class HeapGraphPath { ...@@ -606,39 +633,6 @@ class HeapGraphPath {
}; };
class HeapEntriesMap {
public:
HeapEntriesMap();
~HeapEntriesMap();
void Alias(HeapObject* object, HeapEntry* entry);
void Apply(void (HeapEntry::*Func)(void));
template<class Visitor>
void Apply(Visitor* visitor);
HeapEntry* Map(HeapObject* object);
void Pair(HeapObject* object, HeapEntry* entry);
uint32_t capacity() { return entries_.capacity(); }
private:
INLINE(uint32_t Hash(HeapObject* object)) {
return static_cast<uint32_t>(reinterpret_cast<intptr_t>(object));
}
INLINE(static bool HeapObjectsMatch(void* key1, void* key2)) {
return key1 == key2;
}
INLINE(bool IsAlias(void* ptr)) {
return reinterpret_cast<intptr_t>(ptr) & kAliasTag;
}
static const intptr_t kAliasTag = 1;
HashMap entries_;
DISALLOW_COPY_AND_ASSIGN(HeapEntriesMap);
};
class HeapSnapshotsCollection; class HeapSnapshotsCollection;
class HeapSnapshotsDiff; class HeapSnapshotsDiff;
...@@ -653,53 +647,50 @@ class HeapSnapshot { ...@@ -653,53 +647,50 @@ class HeapSnapshot {
const char* title, const char* title,
unsigned uid); unsigned uid);
~HeapSnapshot(); ~HeapSnapshot();
void ClearPaint();
void CutObjectsFromForeignSecurityContexts();
HeapEntry* GetEntry(Object* object);
void SetClosureReference(
HeapEntry* parent, String* reference_name, Object* child);
void SetElementReference(HeapEntry* parent, int index, Object* child);
void SetInternalReference(
HeapEntry* parent, const char* reference_name, Object* child);
void SetPropertyReference(
HeapEntry* parent, String* reference_name, Object* child);
INLINE(const char* title() const) { return title_; } HeapSnapshotsCollection* collection() { return collection_; }
INLINE(unsigned uid() const) { return uid_; } const char* title() { return title_; }
const HeapEntry* const_root() const { return &root_; } unsigned uid() { return uid_; }
HeapEntry* root() { return &root_; } HeapEntry* root() { return entries_[root_entry_index_]; }
template<class Visitor>
void IterateEntries(Visitor* visitor) { entries_.Apply(visitor); } void AllocateEntries(
List<HeapEntry*>* GetSortedEntriesList(); int entries_count, int children_count, int retainers_count);
HeapEntry* AddEntry(
HeapObject* object, int children_count, int retainers_count);
bool WillAddEntry(HeapObject* object);
int AddCalculatedData();
HeapEntryCalculatedData& GetCalculatedData(int index) {
return calculated_data_[index];
}
void ClearPaint();
HeapSnapshotsDiff* CompareWith(HeapSnapshot* snapshot); HeapSnapshotsDiff* CompareWith(HeapSnapshot* snapshot);
List<HeapEntry*>* GetSortedEntriesList();
template<class Visitor>
void IterateEntries(Visitor* visitor) { entries_.Iterate(visitor); }
void Print(int max_depth); void Print(int max_depth);
void PrintEntriesSize();
static HeapObject *const kInternalRootObject;
private: private:
HeapEntry* AddEntry(HeapObject* object, HeapEntry::Type type) { HeapEntry* AddEntry(HeapObject* object,
return AddEntry(object, type, ""); HeapEntry::Type type,
} const char* name,
HeapEntry* AddEntry( int children_count,
HeapObject* object, HeapEntry::Type type, const char* name); int retainers_count);
void AddEntryAlias(HeapObject* object, HeapEntry* entry) { HeapEntry* GetNextEntryToInit();
entries_.Alias(object, entry);
}
HeapEntry* FindEntry(HeapObject* object) {
return entries_.Map(object);
}
int GetGlobalSecurityToken();
int GetObjectSecurityToken(HeapObject* obj);
static int GetObjectSize(HeapObject* obj); static int GetObjectSize(HeapObject* obj);
static int CalculateNetworkSize(JSObject* obj); static int CalculateNetworkSize(JSObject* obj);
HeapSnapshotsCollection* collection_; HeapSnapshotsCollection* collection_;
const char* title_; const char* title_;
unsigned uid_; unsigned uid_;
HeapEntry root_; int root_entry_index_;
// Mapping from HeapObject* pointers to HeapEntry* pointers. char* raw_entries_;
HeapEntriesMap entries_; List<HeapEntry*> entries_;
// Entries sorted by id. bool entries_sorted_;
List<HeapEntry*>* sorted_entries_; List<HeapEntryCalculatedData> calculated_data_;
DISALLOW_COPY_AND_ASSIGN(HeapSnapshot); DISALLOW_COPY_AND_ASSIGN(HeapSnapshot);
}; };
...@@ -748,30 +739,36 @@ class HeapSnapshotsDiff { ...@@ -748,30 +739,36 @@ class HeapSnapshotsDiff {
HeapSnapshotsDiff(HeapSnapshot* snapshot1, HeapSnapshot* snapshot2) HeapSnapshotsDiff(HeapSnapshot* snapshot1, HeapSnapshot* snapshot2)
: snapshot1_(snapshot1), : snapshot1_(snapshot1),
snapshot2_(snapshot2), snapshot2_(snapshot2),
additions_root_(new HeapEntry(snapshot2)), raw_additions_root_(NULL),
deletions_root_(new HeapEntry(snapshot1)) { } raw_deletions_root_(NULL) { }
~HeapSnapshotsDiff() { ~HeapSnapshotsDiff() {
delete deletions_root_; DeleteArray(raw_deletions_root_);
delete additions_root_; DeleteArray(raw_additions_root_);
} }
void AddAddedEntry(HeapEntry* entry) { void AddAddedEntry(int child_index, int index, HeapEntry* entry) {
additions_root_->SetUnidirAutoIndexReference(entry); additions_root()->SetUnidirElementReference(child_index, index, entry);
} }
void AddDeletedEntry(HeapEntry* entry) { void AddDeletedEntry(int child_index, int index, HeapEntry* entry) {
deletions_root_->SetUnidirAutoIndexReference(entry); deletions_root()->SetUnidirElementReference(child_index, index, entry);
} }
const HeapEntry* additions_root() const { return additions_root_; } void CreateRoots(int additions_count, int deletions_count);
const HeapEntry* deletions_root() const { return deletions_root_; }
HeapEntry* additions_root() {
return reinterpret_cast<HeapEntry*>(raw_additions_root_);
}
HeapEntry* deletions_root() {
return reinterpret_cast<HeapEntry*>(raw_deletions_root_);
}
private: private:
HeapSnapshot* snapshot1_; HeapSnapshot* snapshot1_;
HeapSnapshot* snapshot2_; HeapSnapshot* snapshot2_;
HeapEntry* additions_root_; char* raw_additions_root_;
HeapEntry* deletions_root_; char* raw_deletions_root_;
DISALLOW_COPY_AND_ASSIGN(HeapSnapshotsDiff); DISALLOW_COPY_AND_ASSIGN(HeapSnapshotsDiff);
}; };
...@@ -830,18 +827,123 @@ class HeapSnapshotsCollection { ...@@ -830,18 +827,123 @@ class HeapSnapshotsCollection {
}; };
// The HeapEntriesMap instance is used to track a mapping between
// real heap objects and their representations in heap snapshots.
class HeapEntriesMap {
public:
HeapEntriesMap();
~HeapEntriesMap();
// Aliasing is used for skipping intermediate proxy objects, like
// JSGlobalPropertyCell.
void Alias(HeapObject* from, HeapObject* to);
HeapEntry* Map(HeapObject* object);
void Pair(HeapObject* object, HeapEntry* entry);
void CountReference(HeapObject* from, HeapObject* to,
int* prev_children_count = NULL,
int* prev_retainers_count = NULL);
template<class Visitor>
void UpdateEntries(Visitor* visitor);
int entries_count() { return entries_count_; }
int total_children_count() { return total_children_count_; }
int total_retainers_count() { return total_retainers_count_; }
private:
struct EntryInfo {
explicit EntryInfo(HeapEntry* entry)
: entry(entry), children_count(0), retainers_count(0) { }
HeapEntry* entry;
int children_count;
int retainers_count;
};
uint32_t Hash(HeapObject* object) {
return static_cast<uint32_t>(reinterpret_cast<intptr_t>(object));
}
static bool HeapObjectsMatch(void* key1, void* key2) { return key1 == key2; }
bool IsAlias(void* ptr) {
return reinterpret_cast<intptr_t>(ptr) & kAliasTag;
}
void* MakeAlias(void* ptr) {
return reinterpret_cast<void*>(reinterpret_cast<intptr_t>(ptr) | kAliasTag);
}
void* Unalias(void* ptr) {
return reinterpret_cast<void*>(
reinterpret_cast<intptr_t>(ptr) & (~kAliasTag));
}
HashMap entries_;
int entries_count_;
int total_children_count_;
int total_retainers_count_;
static const intptr_t kAliasTag = 1;
DISALLOW_COPY_AND_ASSIGN(HeapEntriesMap);
};
class HeapSnapshotGenerator { class HeapSnapshotGenerator {
public: public:
class SnapshotFillerInterface {
public:
virtual ~SnapshotFillerInterface() { }
virtual HeapEntry* AddEntry(HeapObject* obj) = 0;
virtual void SetElementReference(HeapObject* parent_obj,
HeapEntry* parent_entry,
int index,
Object* child_obj,
HeapEntry* child_entry) = 0;
virtual void SetNamedReference(HeapGraphEdge::Type type,
HeapObject* parent_obj,
HeapEntry* parent_entry,
const char* reference_name,
Object* child_obj,
HeapEntry* child_entry) = 0;
virtual void SetRootReference(Object* child_obj,
HeapEntry* child_entry) = 0;
static HeapEntry *const kHeapEntryPlaceholder;
};
explicit HeapSnapshotGenerator(HeapSnapshot* snapshot); explicit HeapSnapshotGenerator(HeapSnapshot* snapshot);
void GenerateSnapshot(); void GenerateSnapshot();
private: private:
HeapEntry* GetEntry(Object* obj);
int GetGlobalSecurityToken();
int GetObjectSecurityToken(HeapObject* obj);
void ExtractReferences(HeapObject* obj); void ExtractReferences(HeapObject* obj);
void ExtractClosureReferences(JSObject* js_obj, HeapEntry* entry); void ExtractClosureReferences(JSObject* js_obj, HeapEntry* entry);
void ExtractPropertyReferences(JSObject* js_obj, HeapEntry* entry); void ExtractPropertyReferences(JSObject* js_obj, HeapEntry* entry);
void ExtractElementReferences(JSObject* js_obj, HeapEntry* entry); void ExtractElementReferences(JSObject* js_obj, HeapEntry* entry);
void SetClosureReference(HeapObject* parent_obj,
HeapEntry* parent,
String* reference_name,
Object* child);
void SetElementReference(HeapObject* parent_obj,
HeapEntry* parent,
int index,
Object* child);
void SetInternalReference(HeapObject* parent_obj,
HeapEntry* parent,
const char* reference_name,
Object* child);
void SetPropertyReference(HeapObject* parent_obj,
HeapEntry* parent,
String* reference_name,
Object* child);
void SetRootReference(Object* child);
HeapSnapshot* snapshot_; HeapSnapshot* snapshot_;
HeapSnapshotsCollection* collection_;
// Mapping from HeapObject* pointers to HeapEntry* pointers.
HeapEntriesMap entries_;
SnapshotFillerInterface* filler_;
friend class IndexedReferencesExtractor;
DISALLOW_COPY_AND_ASSIGN(HeapSnapshotGenerator); DISALLOW_COPY_AND_ASSIGN(HeapSnapshotGenerator);
}; };
......
...@@ -396,20 +396,17 @@ class NamedEntriesDetector { ...@@ -396,20 +396,17 @@ class NamedEntriesDetector {
has_A2(false), has_B2(false), has_C2(false) { has_A2(false), has_B2(false), has_C2(false) {
} }
void Apply(i::HeapEntry* entry) { void Apply(i::HeapEntry** entry_ptr) {
const char* node_name = entry->name(); if (IsReachableNodeWithName(*entry_ptr, "A1")) has_A1 = true;
if (strcmp("A1", node_name) == 0 if (IsReachableNodeWithName(*entry_ptr, "B1")) has_B1 = true;
&& entry->GetRetainingPaths()->length() > 0) has_A1 = true; if (IsReachableNodeWithName(*entry_ptr, "C1")) has_C1 = true;
if (strcmp("B1", node_name) == 0 if (IsReachableNodeWithName(*entry_ptr, "A2")) has_A2 = true;
&& entry->GetRetainingPaths()->length() > 0) has_B1 = true; if (IsReachableNodeWithName(*entry_ptr, "B2")) has_B2 = true;
if (strcmp("C1", node_name) == 0 if (IsReachableNodeWithName(*entry_ptr, "C2")) has_C2 = true;
&& entry->GetRetainingPaths()->length() > 0) has_C1 = true; }
if (strcmp("A2", node_name) == 0
&& entry->GetRetainingPaths()->length() > 0) has_A2 = true; static bool IsReachableNodeWithName(i::HeapEntry* entry, const char* name) {
if (strcmp("B2", node_name) == 0 return strcmp(name, entry->name()) == 0 && entry->painted_reachable();
&& entry->GetRetainingPaths()->length() > 0) has_B2 = true;
if (strcmp("C2", node_name) == 0
&& entry->GetRetainingPaths()->length() > 0) has_C2 = true;
} }
bool has_A1; bool has_A1;
...@@ -460,7 +457,7 @@ static bool HasString(const v8::HeapGraphNode* node, const char* contents) { ...@@ -460,7 +457,7 @@ static bool HasString(const v8::HeapGraphNode* node, const char* contents) {
for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) { for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
const v8::HeapGraphEdge* prop = node->GetChild(i); const v8::HeapGraphEdge* prop = node->GetChild(i);
const v8::HeapGraphNode* node = prop->GetToNode(); const v8::HeapGraphNode* node = prop->GetToNode();
if (node->GetType() == v8::HeapGraphNode::STRING) { if (node->GetType() == v8::HeapGraphNode::kString) {
v8::String::AsciiValue node_name(node->GetName()); v8::String::AsciiValue node_name(node->GetName());
if (strcmp(contents, *node_name) == 0) return true; if (strcmp(contents, *node_name) == 0) return true;
} }
...@@ -496,26 +493,34 @@ TEST(HeapSnapshot) { ...@@ -496,26 +493,34 @@ TEST(HeapSnapshot) {
"var c2 = new C2(a2);"); "var c2 = new C2(a2);");
const v8::HeapSnapshot* snapshot_env2 = const v8::HeapSnapshot* snapshot_env2 =
v8::HeapProfiler::TakeSnapshot(v8::String::New("env2")); v8::HeapProfiler::TakeSnapshot(v8::String::New("env2"));
i::HeapSnapshot* i_snapshot_env2 =
const_cast<i::HeapSnapshot*>(
reinterpret_cast<const i::HeapSnapshot*>(snapshot_env2));
const v8::HeapGraphNode* global_env2 = GetGlobalObject(snapshot_env2); const v8::HeapGraphNode* global_env2 = GetGlobalObject(snapshot_env2);
// Paint all nodes reachable from global object.
i_snapshot_env2->ClearPaint();
const_cast<i::HeapEntry*>(
reinterpret_cast<const i::HeapEntry*>(global_env2))->PaintAllReachable();
// Verify, that JS global object of env2 doesn't have '..1' // Verify, that JS global object of env2 doesn't have '..1'
// properties, but has '..2' properties. // properties, but has '..2' properties.
CHECK_EQ(NULL, GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "a1")); CHECK_EQ(NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "a1"));
CHECK_EQ(NULL, GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "b1_1")); CHECK_EQ(
CHECK_EQ(NULL, GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "b1_2")); NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "b1_1"));
CHECK_EQ(NULL, GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "c1")); CHECK_EQ(
NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "b1_2"));
CHECK_EQ(NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "c1"));
const v8::HeapGraphNode* a2_node = const v8::HeapGraphNode* a2_node =
GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "a2"); GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "a2");
CHECK_NE(NULL, a2_node); CHECK_NE(NULL, a2_node);
CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "b2_1")); CHECK_NE(
CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "b2_2")); NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "b2_1"));
CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "c2")); CHECK_NE(
NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "b2_2"));
CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "c2"));
// Verify that anything related to '[ABC]1' is not reachable. // Verify that anything related to '[ABC]1' is not reachable.
NamedEntriesDetector det; NamedEntriesDetector det;
i::HeapSnapshot* i_snapshot_env2 =
const_cast<i::HeapSnapshot*>(
reinterpret_cast<const i::HeapSnapshot*>(snapshot_env2));
i_snapshot_env2->IterateEntries(&det); i_snapshot_env2->IterateEntries(&det);
CHECK(!det.has_A1); CHECK(!det.has_A1);
CHECK(!det.has_B1); CHECK(!det.has_B1);
...@@ -539,7 +544,7 @@ TEST(HeapSnapshot) { ...@@ -539,7 +544,7 @@ TEST(HeapSnapshot) {
const v8::HeapGraphEdge* last_edge = path->GetEdge(edges_count - 1); const v8::HeapGraphEdge* last_edge = path->GetEdge(edges_count - 1);
v8::String::AsciiValue last_edge_name(last_edge->GetName()); v8::String::AsciiValue last_edge_name(last_edge->GetName());
if (strcmp("a2", *last_edge_name) == 0 if (strcmp("a2", *last_edge_name) == 0
&& last_edge->GetType() == v8::HeapGraphEdge::PROPERTY) { && last_edge->GetType() == v8::HeapGraphEdge::kProperty) {
has_global_obj_a2_ref = true; has_global_obj_a2_ref = true;
continue; continue;
} }
...@@ -547,19 +552,19 @@ TEST(HeapSnapshot) { ...@@ -547,19 +552,19 @@ TEST(HeapSnapshot) {
const v8::HeapGraphEdge* prev_edge = path->GetEdge(edges_count - 2); const v8::HeapGraphEdge* prev_edge = path->GetEdge(edges_count - 2);
v8::String::AsciiValue prev_edge_name(prev_edge->GetName()); v8::String::AsciiValue prev_edge_name(prev_edge->GetName());
if (strcmp("x1", *last_edge_name) == 0 if (strcmp("x1", *last_edge_name) == 0
&& last_edge->GetType() == v8::HeapGraphEdge::PROPERTY && last_edge->GetType() == v8::HeapGraphEdge::kProperty
&& strcmp("c2", *prev_edge_name) == 0) has_c2_x1_ref = true; && strcmp("c2", *prev_edge_name) == 0) has_c2_x1_ref = true;
if (strcmp("x2", *last_edge_name) == 0 if (strcmp("x2", *last_edge_name) == 0
&& last_edge->GetType() == v8::HeapGraphEdge::PROPERTY && last_edge->GetType() == v8::HeapGraphEdge::kProperty
&& strcmp("c2", *prev_edge_name) == 0) has_c2_x2_ref = true; && strcmp("c2", *prev_edge_name) == 0) has_c2_x2_ref = true;
if (strcmp("1", *last_edge_name) == 0 if (strcmp("1", *last_edge_name) == 0
&& last_edge->GetType() == v8::HeapGraphEdge::ELEMENT && last_edge->GetType() == v8::HeapGraphEdge::kElement
&& strcmp("c2", *prev_edge_name) == 0) has_c2_1_ref = true; && strcmp("c2", *prev_edge_name) == 0) has_c2_1_ref = true;
if (strcmp("x", *last_edge_name) == 0 if (strcmp("x", *last_edge_name) == 0
&& last_edge->GetType() == v8::HeapGraphEdge::CONTEXT_VARIABLE && last_edge->GetType() == v8::HeapGraphEdge::kContextVariable
&& strcmp("b2_1", *prev_edge_name) == 0) has_b2_1_x_ref = true; && strcmp("b2_1", *prev_edge_name) == 0) has_b2_1_x_ref = true;
if (strcmp("x", *last_edge_name) == 0 if (strcmp("x", *last_edge_name) == 0
&& last_edge->GetType() == v8::HeapGraphEdge::CONTEXT_VARIABLE && last_edge->GetType() == v8::HeapGraphEdge::kContextVariable
&& strcmp("b2_2", *prev_edge_name) == 0) has_b2_2_x_ref = true; && strcmp("b2_2", *prev_edge_name) == 0) has_b2_2_x_ref = true;
} }
CHECK(has_global_obj_a2_ref); CHECK(has_global_obj_a2_ref);
...@@ -571,6 +576,73 @@ TEST(HeapSnapshot) { ...@@ -571,6 +576,73 @@ TEST(HeapSnapshot) {
} }
TEST(HeapSnapshotObjectSizes) {
v8::HandleScope scope;
LocalContext env;
// -a-> X1 --a
// x -b-> X2 <-|
CompileAndRunScript(
"function X(a, b) { this.a = a; this.b = b; }\n"
"x = new X(new X(), new X());\n"
"x.a.a = x.b;");
const v8::HeapSnapshot* snapshot =
v8::HeapProfiler::TakeSnapshot(v8::String::New("sizes"));
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
const v8::HeapGraphNode* x =
GetProperty(global, v8::HeapGraphEdge::kProperty, "x");
CHECK_NE(NULL, x);
const v8::HeapGraphNode* x_prototype =
GetProperty(x, v8::HeapGraphEdge::kProperty, "prototype");
CHECK_NE(NULL, x_prototype);
const v8::HeapGraphNode* x1 =
GetProperty(x, v8::HeapGraphEdge::kProperty, "a");
CHECK_NE(NULL, x1);
const v8::HeapGraphNode* x2 =
GetProperty(x, v8::HeapGraphEdge::kProperty, "b");
CHECK_NE(NULL, x2);
CHECK_EQ(
x->GetSelfSize() * 3,
x->GetReachableSize() - x_prototype->GetReachableSize());
CHECK_EQ(
x->GetSelfSize() * 3 + x_prototype->GetSelfSize(), x->GetRetainedSize());
CHECK_EQ(
x1->GetSelfSize() * 2,
x1->GetReachableSize() - x_prototype->GetReachableSize());
CHECK_EQ(
x1->GetSelfSize(), x1->GetRetainedSize());
CHECK_EQ(
x2->GetSelfSize(),
x2->GetReachableSize() - x_prototype->GetReachableSize());
CHECK_EQ(
x2->GetSelfSize(), x2->GetRetainedSize());
}
TEST(HeapSnapshotEntryChildren) {
v8::HandleScope scope;
LocalContext env;
CompileAndRunScript(
"function A() { }\n"
"a = new A;");
const v8::HeapSnapshot* snapshot =
v8::HeapProfiler::TakeSnapshot(v8::String::New("children"));
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
for (int i = 0, count = global->GetChildrenCount(); i < count; ++i) {
const v8::HeapGraphEdge* prop = global->GetChild(i);
CHECK_EQ(global, prop->GetFromNode());
}
const v8::HeapGraphNode* a =
GetProperty(global, v8::HeapGraphEdge::kProperty, "a");
CHECK_NE(NULL, a);
for (int i = 0, count = a->GetChildrenCount(); i < count; ++i) {
const v8::HeapGraphEdge* prop = a->GetChild(i);
CHECK_EQ(a, prop->GetFromNode());
}
}
TEST(HeapSnapshotCodeObjects) { TEST(HeapSnapshotCodeObjects) {
v8::HandleScope scope; v8::HandleScope scope;
LocalContext env; LocalContext env;
...@@ -584,20 +656,20 @@ TEST(HeapSnapshotCodeObjects) { ...@@ -584,20 +656,20 @@ TEST(HeapSnapshotCodeObjects) {
const v8::HeapGraphNode* global = GetGlobalObject(snapshot); const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
const v8::HeapGraphNode* compiled = const v8::HeapGraphNode* compiled =
GetProperty(global, v8::HeapGraphEdge::PROPERTY, "compiled"); GetProperty(global, v8::HeapGraphEdge::kProperty, "compiled");
CHECK_NE(NULL, compiled); CHECK_NE(NULL, compiled);
CHECK_EQ(v8::HeapGraphNode::CLOSURE, compiled->GetType()); CHECK_EQ(v8::HeapGraphNode::kClosure, compiled->GetType());
const v8::HeapGraphNode* lazy = const v8::HeapGraphNode* lazy =
GetProperty(global, v8::HeapGraphEdge::PROPERTY, "lazy"); GetProperty(global, v8::HeapGraphEdge::kProperty, "lazy");
CHECK_NE(NULL, lazy); CHECK_NE(NULL, lazy);
CHECK_EQ(v8::HeapGraphNode::CLOSURE, lazy->GetType()); CHECK_EQ(v8::HeapGraphNode::kClosure, lazy->GetType());
// Find references to code. // Find references to code.
const v8::HeapGraphNode* compiled_code = const v8::HeapGraphNode* compiled_code =
GetProperty(compiled, v8::HeapGraphEdge::INTERNAL, "code"); GetProperty(compiled, v8::HeapGraphEdge::kInternal, "code");
CHECK_NE(NULL, compiled_code); CHECK_NE(NULL, compiled_code);
const v8::HeapGraphNode* lazy_code = const v8::HeapGraphNode* lazy_code =
GetProperty(lazy, v8::HeapGraphEdge::INTERNAL, "code"); GetProperty(lazy, v8::HeapGraphEdge::kInternal, "code");
CHECK_NE(NULL, lazy_code); CHECK_NE(NULL, lazy_code);
// Verify that non-compiled code doesn't contain references to "x" // Verify that non-compiled code doesn't contain references to "x"
...@@ -607,7 +679,7 @@ TEST(HeapSnapshotCodeObjects) { ...@@ -607,7 +679,7 @@ TEST(HeapSnapshotCodeObjects) {
for (int i = 0, count = compiled_code->GetChildrenCount(); i < count; ++i) { for (int i = 0, count = compiled_code->GetChildrenCount(); i < count; ++i) {
const v8::HeapGraphEdge* prop = compiled_code->GetChild(i); const v8::HeapGraphEdge* prop = compiled_code->GetChild(i);
const v8::HeapGraphNode* node = prop->GetToNode(); const v8::HeapGraphNode* node = prop->GetToNode();
if (node->GetType() == v8::HeapGraphNode::ARRAY) { if (node->GetType() == v8::HeapGraphNode::kArray) {
if (HasString(node, "x")) { if (HasString(node, "x")) {
compiled_references_x = true; compiled_references_x = true;
break; break;
...@@ -617,7 +689,7 @@ TEST(HeapSnapshotCodeObjects) { ...@@ -617,7 +689,7 @@ TEST(HeapSnapshotCodeObjects) {
for (int i = 0, count = lazy_code->GetChildrenCount(); i < count; ++i) { for (int i = 0, count = lazy_code->GetChildrenCount(); i < count; ++i) {
const v8::HeapGraphEdge* prop = lazy_code->GetChild(i); const v8::HeapGraphEdge* prop = lazy_code->GetChild(i);
const v8::HeapGraphNode* node = prop->GetToNode(); const v8::HeapGraphNode* node = prop->GetToNode();
if (node->GetType() == v8::HeapGraphNode::ARRAY) { if (node->GetType() == v8::HeapGraphNode::kArray) {
if (HasString(node, "x")) { if (HasString(node, "x")) {
lazy_references_x = true; lazy_references_x = true;
break; break;
...@@ -634,11 +706,8 @@ TEST(HeapSnapshotCodeObjects) { ...@@ -634,11 +706,8 @@ TEST(HeapSnapshotCodeObjects) {
// them to a signed type. // them to a signed type.
#define CHECK_EQ_UINT64_T(a, b) \ #define CHECK_EQ_UINT64_T(a, b) \
CHECK_EQ(static_cast<int64_t>(a), static_cast<int64_t>(b)) CHECK_EQ(static_cast<int64_t>(a), static_cast<int64_t>(b))
#define CHECK_NE_UINT64_T(a, b) do \ #define CHECK_NE_UINT64_T(a, b) \
{ \ CHECK((a) != (b)) // NOLINT
bool ne = a != b; \
CHECK(ne); \
} while (false)
TEST(HeapEntryIdsAndGC) { TEST(HeapEntryIdsAndGC) {
v8::HandleScope scope; v8::HandleScope scope;
...@@ -662,27 +731,35 @@ TEST(HeapEntryIdsAndGC) { ...@@ -662,27 +731,35 @@ TEST(HeapEntryIdsAndGC) {
CHECK_NE_UINT64_T(0, global1->GetId()); CHECK_NE_UINT64_T(0, global1->GetId());
CHECK_EQ_UINT64_T(global1->GetId(), global2->GetId()); CHECK_EQ_UINT64_T(global1->GetId(), global2->GetId());
const v8::HeapGraphNode* A1 = const v8::HeapGraphNode* A1 =
GetProperty(global1, v8::HeapGraphEdge::PROPERTY, "A"); GetProperty(global1, v8::HeapGraphEdge::kProperty, "A");
CHECK_NE(NULL, A1);
const v8::HeapGraphNode* A2 = const v8::HeapGraphNode* A2 =
GetProperty(global2, v8::HeapGraphEdge::PROPERTY, "A"); GetProperty(global2, v8::HeapGraphEdge::kProperty, "A");
CHECK_NE(NULL, A2);
CHECK_NE_UINT64_T(0, A1->GetId()); CHECK_NE_UINT64_T(0, A1->GetId());
CHECK_EQ_UINT64_T(A1->GetId(), A2->GetId()); CHECK_EQ_UINT64_T(A1->GetId(), A2->GetId());
const v8::HeapGraphNode* B1 = const v8::HeapGraphNode* B1 =
GetProperty(global1, v8::HeapGraphEdge::PROPERTY, "B"); GetProperty(global1, v8::HeapGraphEdge::kProperty, "B");
CHECK_NE(NULL, B1);
const v8::HeapGraphNode* B2 = const v8::HeapGraphNode* B2 =
GetProperty(global2, v8::HeapGraphEdge::PROPERTY, "B"); GetProperty(global2, v8::HeapGraphEdge::kProperty, "B");
CHECK_NE(NULL, B2);
CHECK_NE_UINT64_T(0, B1->GetId()); CHECK_NE_UINT64_T(0, B1->GetId());
CHECK_EQ_UINT64_T(B1->GetId(), B2->GetId()); CHECK_EQ_UINT64_T(B1->GetId(), B2->GetId());
const v8::HeapGraphNode* a1 = const v8::HeapGraphNode* a1 =
GetProperty(global1, v8::HeapGraphEdge::PROPERTY, "a"); GetProperty(global1, v8::HeapGraphEdge::kProperty, "a");
CHECK_NE(NULL, a1);
const v8::HeapGraphNode* a2 = const v8::HeapGraphNode* a2 =
GetProperty(global2, v8::HeapGraphEdge::PROPERTY, "a"); GetProperty(global2, v8::HeapGraphEdge::kProperty, "a");
CHECK_NE(NULL, a2);
CHECK_NE_UINT64_T(0, a1->GetId()); CHECK_NE_UINT64_T(0, a1->GetId());
CHECK_EQ_UINT64_T(a1->GetId(), a2->GetId()); CHECK_EQ_UINT64_T(a1->GetId(), a2->GetId());
const v8::HeapGraphNode* b1 = const v8::HeapGraphNode* b1 =
GetProperty(global1, v8::HeapGraphEdge::PROPERTY, "b"); GetProperty(global1, v8::HeapGraphEdge::kProperty, "b");
CHECK_NE(NULL, b1);
const v8::HeapGraphNode* b2 = const v8::HeapGraphNode* b2 =
GetProperty(global2, v8::HeapGraphEdge::PROPERTY, "b"); GetProperty(global2, v8::HeapGraphEdge::kProperty, "b");
CHECK_NE(NULL, b2);
CHECK_NE_UINT64_T(0, b1->GetId()); CHECK_NE_UINT64_T(0, b1->GetId());
CHECK_EQ_UINT64_T(b1->GetId(), b2->GetId()); CHECK_EQ_UINT64_T(b1->GetId(), b2->GetId());
} }
...@@ -717,15 +794,15 @@ TEST(HeapSnapshotsDiff) { ...@@ -717,15 +794,15 @@ TEST(HeapSnapshotsDiff) {
for (int i = 0, count = additions_root->GetChildrenCount(); i < count; ++i) { for (int i = 0, count = additions_root->GetChildrenCount(); i < count; ++i) {
const v8::HeapGraphEdge* prop = additions_root->GetChild(i); const v8::HeapGraphEdge* prop = additions_root->GetChild(i);
const v8::HeapGraphNode* node = prop->GetToNode(); const v8::HeapGraphNode* node = prop->GetToNode();
if (node->GetType() == v8::HeapGraphNode::OBJECT) { if (node->GetType() == v8::HeapGraphNode::kObject) {
v8::String::AsciiValue node_name(node->GetName()); v8::String::AsciiValue node_name(node->GetName());
if (strcmp(*node_name, "A") == 0) { if (strcmp(*node_name, "A") == 0) {
CHECK(IsNodeRetainedAs(node, v8::HeapGraphEdge::PROPERTY, "a")); CHECK(IsNodeRetainedAs(node, v8::HeapGraphEdge::kProperty, "a"));
CHECK(!found_A); CHECK(!found_A);
found_A = true; found_A = true;
s1_A_id = node->GetId(); s1_A_id = node->GetId();
} else if (strcmp(*node_name, "B") == 0) { } else if (strcmp(*node_name, "B") == 0) {
CHECK(IsNodeRetainedAs(node, v8::HeapGraphEdge::PROPERTY, "b2")); CHECK(IsNodeRetainedAs(node, v8::HeapGraphEdge::kProperty, "b2"));
CHECK(!found_B); CHECK(!found_B);
found_B = true; found_B = true;
} }
...@@ -741,10 +818,10 @@ TEST(HeapSnapshotsDiff) { ...@@ -741,10 +818,10 @@ TEST(HeapSnapshotsDiff) {
for (int i = 0, count = deletions_root->GetChildrenCount(); i < count; ++i) { for (int i = 0, count = deletions_root->GetChildrenCount(); i < count; ++i) {
const v8::HeapGraphEdge* prop = deletions_root->GetChild(i); const v8::HeapGraphEdge* prop = deletions_root->GetChild(i);
const v8::HeapGraphNode* node = prop->GetToNode(); const v8::HeapGraphNode* node = prop->GetToNode();
if (node->GetType() == v8::HeapGraphNode::OBJECT) { if (node->GetType() == v8::HeapGraphNode::kObject) {
v8::String::AsciiValue node_name(node->GetName()); v8::String::AsciiValue node_name(node->GetName());
if (strcmp(*node_name, "A") == 0) { if (strcmp(*node_name, "A") == 0) {
CHECK(IsNodeRetainedAs(node, v8::HeapGraphEdge::PROPERTY, "a")); CHECK(IsNodeRetainedAs(node, v8::HeapGraphEdge::kProperty, "a"));
CHECK(!found_A_del); CHECK(!found_A_del);
found_A_del = true; found_A_del = true;
s2_A_id = node->GetId(); s2_A_id = node->GetId();
......
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