New heap profiler: implement fast retaining sizes approximation.

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

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

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5867 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 1e8413e1
......@@ -282,16 +282,19 @@ class V8EXPORT HeapGraphNode {
/** Returns node's own size, in bytes. */
int GetSelfSize() const;
/** Returns node's network (self + reachable nodes) size, in bytes. */
int GetReachableSize() const;
/**
* Returns node's retained size, in bytes. That is, self + sizes of
* the objects that are reachable only from this object. In other
* words, the size of memory that will be reclaimed having this node
* collected.
*
* Exact retained size calculation has O(N) (number of nodes)
* computational complexity, while approximate has O(1). It is
* assumed that initially heap profiling tools provide approximate
* sizes for all nodes, and then exact sizes are calculated for the
* most 'interesting' nodes.
*/
int GetRetainedSize() const;
int GetRetainedSize(bool exact) const;
/** Returns child nodes count of the node. */
int GetChildrenCount() const;
......@@ -310,6 +313,12 @@ class V8EXPORT HeapGraphNode {
/** Returns a retaining path by index. */
const HeapGraphPath* GetRetainingPath(int index) const;
/**
* Returns a dominator node. This is the node that participates in every
* path from the snapshot root to the current node.
*/
const HeapGraphNode* GetDominatorNode() const;
};
......
......@@ -4763,15 +4763,9 @@ int HeapGraphNode::GetSelfSize() const {
}
int HeapGraphNode::GetReachableSize() const {
IsDeadCheck("v8::HeapSnapshot::GetReachableSize");
return ToInternal(this)->ReachableSize();
}
int HeapGraphNode::GetRetainedSize() const {
int HeapGraphNode::GetRetainedSize(bool exact) const {
IsDeadCheck("v8::HeapSnapshot::GetRetainedSize");
return ToInternal(this)->RetainedSize();
return ToInternal(this)->RetainedSize(exact);
}
......@@ -4814,6 +4808,12 @@ const HeapGraphPath* HeapGraphNode::GetRetainingPath(int index) const {
}
const HeapGraphNode* HeapGraphNode::GetDominatorNode() const {
IsDeadCheck("v8::HeapSnapshot::GetDominatorNode");
return reinterpret_cast<const HeapGraphNode*>(ToInternal(this)->dominator());
}
const HeapGraphNode* HeapSnapshotsDiff::GetAdditionsRoot() const {
IsDeadCheck("v8::HeapSnapshotsDiff::GetAdditionsRoot");
i::HeapSnapshotsDiff* diff =
......
......@@ -105,22 +105,6 @@ void CodeMap::DeleteCode(Address addr) {
}
template<class Visitor>
void HeapEntriesMap::UpdateEntries(Visitor* visitor) {
for (HashMap::Entry* p = entries_.Start();
p != NULL;
p = entries_.Next(p)) {
EntryInfo* entry_info = reinterpret_cast<EntryInfo*>(p->value);
entry_info->entry = visitor->GetEntry(
reinterpret_cast<HeapObject*>(p->key),
entry_info->children_count,
entry_info->retainers_count);
entry_info->children_count = 0;
entry_info->retainers_count = 0;
}
}
CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) {
switch (tag) {
case GC:
......@@ -137,6 +121,22 @@ CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) {
}
}
template<class Visitor>
void HeapEntriesMap::UpdateEntries(Visitor* visitor) {
for (HashMap::Entry* p = entries_.Start();
p != NULL;
p = entries_.Next(p)) {
EntryInfo* entry_info = reinterpret_cast<EntryInfo*>(p->value);
entry_info->entry = visitor->GetEntry(
reinterpret_cast<HeapObject*>(p->key),
entry_info->children_count,
entry_info->retainers_count);
entry_info->children_count = 0;
entry_info->retainers_count = 0;
}
}
} } // namespace v8::internal
#endif // ENABLE_LOGGING_AND_PROFILING
......
This diff is collapsed.
......@@ -528,12 +528,19 @@ class HeapEntry BASE_EMBEDDED {
const char* name() { return name_; }
uint64_t id() { return id_; }
int self_size() { return self_size_; }
int retained_size() { return retained_size_; }
void add_retained_size(int size) { retained_size_ += size; }
void set_retained_size(int value) { retained_size_ = value; }
int ordered_index() { return ordered_index_; }
void set_ordered_index(int value) { ordered_index_ = value; }
Vector<HeapGraphEdge> children() {
return Vector<HeapGraphEdge>(children_arr(), children_count_); }
Vector<HeapGraphEdge*> retainers() {
return Vector<HeapGraphEdge*>(retainers_arr(), retainers_count_); }
List<HeapGraphPath*>* GetRetainingPaths();
HeapEntry* dominator() { return dominator_; }
void set_dominator(HeapEntry* entry) { dominator_ = entry; }
void clear_paint() { painted_ = kUnpainted; }
bool painted_reachable() { return painted_ == kPainted; }
......@@ -551,6 +558,13 @@ class HeapEntry BASE_EMBEDDED {
void ApplyAndPaintAllReachable(Visitor* visitor);
void PaintAllReachable();
bool is_leaf() { return painted_ == kLeaf; }
void set_leaf() { painted_ = kLeaf; }
bool is_non_leaf() { return painted_ == kNonLeaf; }
void set_non_leaf() { painted_ = kNonLeaf; }
bool is_processed() { return painted_ == kProcessed; }
void set_processed() { painted_ = kProcessed; }
void SetIndexedReference(HeapGraphEdge::Type type,
int child_index,
int index,
......@@ -564,14 +578,19 @@ class HeapEntry BASE_EMBEDDED {
void SetUnidirElementReference(int child_index, int index, HeapEntry* entry);
int EntrySize() { return EntriesSize(1, children_count_, retainers_count_); }
int ReachableSize();
int RetainedSize();
int RetainedSize(bool exact);
List<HeapGraphPath*>* CalculateRetainingPaths();
void Print(int max_depth, int indent);
static int EntriesSize(int entries_count,
int children_count,
int retainers_count);
static uint32_t Hash(HeapEntry* entry) {
return ComputeIntegerHash(
static_cast<uint32_t>(reinterpret_cast<uintptr_t>(entry)));
}
static bool Match(void* entry1, void* entry2) { return entry1 == entry2; }
private:
HeapGraphEdge* children_arr() {
......@@ -580,53 +599,37 @@ class HeapEntry BASE_EMBEDDED {
HeapGraphEdge** retainers_arr() {
return reinterpret_cast<HeapGraphEdge**>(children_arr() + children_count_);
}
void CalculateExactRetainedSize();
void FindRetainingPaths(CachedHeapGraphPath* prev_path,
List<HeapGraphPath*>* retaining_paths);
const char* TypeAsString();
unsigned painted_: 2;
unsigned type_: 3;
// The calculated data is stored in HeapSnapshot in HeapEntryCalculatedData
// entries. See AddCalculatedData and GetCalculatedData.
int calculated_data_index_: 27;
int self_size_;
int children_count_;
int children_count_: 27;
int retainers_count_;
int self_size_;
union {
int ordered_index_; // Used during dominator tree building.
int retained_size_; // At that moment, there is no retained size yet.
};
HeapEntry* dominator_;
HeapSnapshot* snapshot_;
const char* name_;
uint64_t id_;
// Paints used for exact retained sizes calculation.
static const unsigned kUnpainted = 0;
static const unsigned kPainted = 1;
static const unsigned kPaintedReachableFromOthers = 2;
static const int kNoCalculatedData = -1;
DISALLOW_COPY_AND_ASSIGN(HeapEntry);
};
// Paints used for approximate retained sizes calculation.
static const unsigned kLeaf = 0;
static const unsigned kNonLeaf = 1;
static const unsigned kProcessed = 2;
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);
static const int kExactRetainedSizeTag = 1;
List<HeapGraphPath*>* retaining_paths_;
int reachable_size_;
int retained_size_;
static const int kUnknownSize = -1;
// Allow generated copy constructor and assignment operator.
DISALLOW_COPY_AND_ASSIGN(HeapEntry);
};
......@@ -687,12 +690,10 @@ class HeapSnapshot {
int size,
int children_count,
int retainers_count);
int AddCalculatedData();
HeapEntryCalculatedData& GetCalculatedData(int index) {
return calculated_data_[index];
}
void ApproximateRetainedSizes();
void ClearPaint();
HeapSnapshotsDiff* CompareWith(HeapSnapshot* snapshot);
List<HeapGraphPath*>* GetRetainingPaths(HeapEntry* entry);
List<HeapEntry*>* GetSortedEntriesList();
template<class Visitor>
void IterateEntries(Visitor* visitor) { entries_.Iterate(visitor); }
......@@ -710,6 +711,10 @@ class HeapSnapshot {
int children_count,
int retainers_count);
HeapEntry* GetNextEntryToInit();
void BuildDominatorTree(const Vector<HeapEntry*>& entries,
Vector<HeapEntry*>* dominators);
void FillReversePostorderIndexes(Vector<HeapEntry*>* entries);
void SetEntriesDominators();
HeapSnapshotsCollection* collection_;
Type type_;
......@@ -720,7 +725,7 @@ class HeapSnapshot {
char* raw_entries_;
List<HeapEntry*> entries_;
bool entries_sorted_;
List<HeapEntryCalculatedData> calculated_data_;
HashMap retaining_paths_;
#ifdef DEBUG
int raw_entries_size_;
#endif
......
......@@ -326,6 +326,8 @@ class Vector {
return start_[index];
}
T& at(int i) const { return operator[](i); }
T& first() { return start_[0]; }
T& last() { return start_[length_ - 1]; }
......
......@@ -565,18 +565,21 @@ TEST(HeapSnapshotObjectSizes) {
const v8::HeapGraphNode* x =
GetProperty(global, v8::HeapGraphEdge::kShortcut, "x");
CHECK_NE(NULL, x);
const v8::HeapGraphNode* x_prototype =
GetProperty(x, v8::HeapGraphEdge::kProperty, "__proto__");
CHECK_NE(NULL, x_prototype);
const v8::HeapGraphNode* x1 =
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->GetRetainedSize());
CHECK_EQ(x1->GetSelfSize(), x1->GetRetainedSize());
CHECK_EQ(x2->GetSelfSize(), x2->GetRetainedSize());
// Test approximate sizes.
CHECK_EQ(x->GetSelfSize() * 3, x->GetRetainedSize(false));
CHECK_EQ(x1->GetSelfSize(), x1->GetRetainedSize(false));
CHECK_EQ(x2->GetSelfSize(), x2->GetRetainedSize(false));
// Test exact sizes.
CHECK_EQ(x->GetSelfSize() * 3, x->GetRetainedSize(true));
CHECK_EQ(x1->GetSelfSize(), x1->GetRetainedSize(true));
CHECK_EQ(x2->GetSelfSize(), x2->GetRetainedSize(true));
}
......@@ -963,6 +966,67 @@ TEST(AggregatedHeapSnapshot) {
CHECK(IsNodeRetainedAs(a_from_b, 1)); // B has 1 ref to A.
}
TEST(HeapEntryDominator) {
// The graph looks like this:
//
// -> node1
// a |^
// -> node5 ba
// a v|
// node6 -> node2
// b a |^
// -> node4 ba
// b v|
// -> node3
//
// The dominator for all nodes is node6.
v8::HandleScope scope;
LocalContext env;
CompileRun(
"function X(a, b) { this.a = a; this.b = b; }\n"
"node6 = new X(new X(new X()), new X(new X(),new X()));\n"
"(function(){\n"
"node6.a.a.b = node6.b.a; // node1 -> node2\n"
"node6.b.a.a = node6.a.a; // node2 -> node1\n"
"node6.b.a.b = node6.b.b; // node2 -> node3\n"
"node6.b.b.a = node6.b.a; // node3 -> node2\n"
"})();");
const v8::HeapSnapshot* snapshot =
v8::HeapProfiler::TakeSnapshot(v8::String::New("dominators"));
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
CHECK_NE(NULL, global);
const v8::HeapGraphNode* node6 =
GetProperty(global, v8::HeapGraphEdge::kShortcut, "node6");
CHECK_NE(NULL, node6);
const v8::HeapGraphNode* node5 =
GetProperty(node6, v8::HeapGraphEdge::kProperty, "a");
CHECK_NE(NULL, node5);
const v8::HeapGraphNode* node4 =
GetProperty(node6, v8::HeapGraphEdge::kProperty, "b");
CHECK_NE(NULL, node4);
const v8::HeapGraphNode* node3 =
GetProperty(node4, v8::HeapGraphEdge::kProperty, "b");
CHECK_NE(NULL, node3);
const v8::HeapGraphNode* node2 =
GetProperty(node4, v8::HeapGraphEdge::kProperty, "a");
CHECK_NE(NULL, node2);
const v8::HeapGraphNode* node1 =
GetProperty(node5, v8::HeapGraphEdge::kProperty, "a");
CHECK_NE(NULL, node1);
CHECK_EQ(node6, node1->GetDominatorNode());
CHECK_EQ(node6, node2->GetDominatorNode());
CHECK_EQ(node6, node3->GetDominatorNode());
CHECK_EQ(node6, node4->GetDominatorNode());
CHECK_EQ(node6, node5->GetDominatorNode());
}
namespace {
class TestJSONStream : public v8::OutputStream {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment