const char* name,
SnapshotObjectId id,
int self_size)
- : painted_(false),
- user_reachable_(false),
- dominator_(kNoEntry),
- type_(type),
- retainers_count_(0),
- retainers_index_(-1),
+ : type_(type),
children_count_(0),
children_index_(-1),
self_size_(self_size),
- retained_size_(0),
id_(id),
snapshot_(snapshot),
name_(name) { }
HeapGraphEdge edge(type, name, this->index(), entry->index());
snapshot_->edges().Add(edge);
++children_count_;
- ++entry->retainers_count_;
}
HeapGraphEdge edge(type, index, this->index(), entry->index());
snapshot_->edges().Add(edge);
++children_count_;
- ++entry->retainers_count_;
}
void HeapEntry::Print(
const char* prefix, const char* edge_name, int max_depth, int indent) {
STATIC_CHECK(sizeof(unsigned) == sizeof(id()));
- OS::Print("%6d %7d @%6u %*c %s%s: ",
- self_size(), retained_size(), id(),
- indent, ' ', prefix, edge_name);
+ OS::Print("%6d @%6u %*c %s%s: ",
+ self_size(), id(), indent, ' ', prefix, edge_name);
if (type() != kString) {
OS::Print("%s %.40s\n", TypeAsString(), name_);
} else {
template <> struct SnapshotSizeConstants<4> {
static const int kExpectedHeapGraphEdgeSize = 12;
- static const int kExpectedHeapEntrySize = 40;
+ static const int kExpectedHeapEntrySize = 24;
static const size_t kMaxSerializableSnapshotRawSize = 256 * MB;
};
template <> struct SnapshotSizeConstants<8> {
static const int kExpectedHeapGraphEdgeSize = 24;
- static const int kExpectedHeapEntrySize = 48;
+ static const int kExpectedHeapEntrySize = 32;
static const uint64_t kMaxSerializableSnapshotRawSize =
static_cast<uint64_t>(6000) * MB;
};
}
-static void HeapEntryClearPaint(HeapEntry* entry_ptr) {
- entry_ptr->clear_paint();
-}
-
-
-void HeapSnapshot::ClearPaint() {
- entries_.Iterate(HeapEntryClearPaint);
-}
-
-
HeapEntry* HeapSnapshot::AddRootEntry() {
ASSERT(root_index_ == HeapEntry::kNoEntry);
ASSERT(entries_.is_empty()); // Root entry must be the first one.
}
-void HeapSnapshot::FillChildrenAndRetainers() {
+void HeapSnapshot::FillChildren() {
ASSERT(children().is_empty());
children().Allocate(edges().length());
- ASSERT(retainers().is_empty());
- retainers().Allocate(edges().length());
int children_index = 0;
- int retainers_index = 0;
for (int i = 0; i < entries().length(); ++i) {
HeapEntry* entry = &entries()[i];
children_index = entry->set_children_index(children_index);
- retainers_index = entry->set_retainers_index(retainers_index);
}
ASSERT(edges().length() == children_index);
- ASSERT(edges().length() == retainers_index);
for (int i = 0; i < edges().length(); ++i) {
HeapGraphEdge* edge = &edges()[i];
edge->ReplaceToIndexWithEntry(this);
edge->from()->add_child(edge);
- edge->to()->add_retainer(edge);
- }
-}
-
-
-void HeapSnapshot::SetDominatorsToSelf() {
- for (int i = 0; i < entries_.length(); ++i) {
- entries_[i].set_dominator(&entries_[i]);
}
}
GetMemoryUsedByList(entries_) +
GetMemoryUsedByList(edges_) +
GetMemoryUsedByList(children_) +
- GetMemoryUsedByList(retainers_) +
GetMemoryUsedByList(sorted_entries_);
}
if (!FillReferences()) return false;
- snapshot_->FillChildrenAndRetainers();
+ snapshot_->FillChildren();
snapshot_->RememberLastJSObjectId();
- if (!SetEntriesDominators()) return false;
- if (!CalculateRetainedSizes()) return false;
-
progress_counter_ = progress_total_;
if (!ProgressReport(true)) return false;
return true;
}
-bool HeapSnapshotGenerator::IsUserGlobalReference(const HeapGraphEdge* edge) {
- ASSERT(edge->from() == snapshot_->root());
- return edge->type() == HeapGraphEdge::kShortcut;
-}
-
-
-void HeapSnapshotGenerator::MarkUserReachableObjects() {
- List<HeapEntry*> worklist;
-
- Vector<HeapGraphEdge*> children = snapshot_->root()->children();
- for (int i = 0; i < children.length(); ++i) {
- if (IsUserGlobalReference(children[i])) {
- worklist.Add(children[i]->to());
- }
- }
-
- while (!worklist.is_empty()) {
- HeapEntry* entry = worklist.RemoveLast();
- if (entry->user_reachable()) continue;
- entry->set_user_reachable();
- Vector<HeapGraphEdge*> children = entry->children();
- for (int i = 0; i < children.length(); ++i) {
- HeapEntry* child = children[i]->to();
- if (!child->user_reachable()) {
- worklist.Add(child);
- }
- }
- }
-}
-
-
-static bool IsRetainingEdge(HeapGraphEdge* edge) {
- if (edge->type() == HeapGraphEdge::kShortcut) return false;
- // The edge is not retaining if it goes from system domain
- // (i.e. an object not reachable from window) to the user domain
- // (i.e. a reachable object).
- return edge->from()->user_reachable()
- || !edge->to()->user_reachable();
-}
-
-
-void HeapSnapshotGenerator::FillPostorderIndexes(
- Vector<HeapEntry*>* entries) {
- snapshot_->ClearPaint();
- int current_entry = 0;
- List<HeapEntry*> nodes_to_visit;
- HeapEntry* root = snapshot_->root();
- nodes_to_visit.Add(root);
- snapshot_->root()->paint();
- while (!nodes_to_visit.is_empty()) {
- HeapEntry* entry = nodes_to_visit.last();
- Vector<HeapGraphEdge*> children = entry->children();
- bool has_new_edges = false;
- for (int i = 0; i < children.length(); ++i) {
- if (entry != root && !IsRetainingEdge(children[i])) continue;
- HeapEntry* child = children[i]->to();
- if (!child->painted()) {
- nodes_to_visit.Add(child);
- child->paint();
- has_new_edges = true;
- }
- }
- if (!has_new_edges) {
- entry->set_postorder_index(current_entry);
- (*entries)[current_entry++] = entry;
- nodes_to_visit.RemoveLast();
- }
- }
- ASSERT_EQ(current_entry, entries->length());
-}
-
-
-static int Intersect(int i1, int i2, const Vector<int>& dominators) {
- int finger1 = i1, finger2 = i2;
- while (finger1 != finger2) {
- while (finger1 < finger2) finger1 = dominators[finger1];
- while (finger2 < finger1) finger2 = dominators[finger2];
- }
- return finger1;
-}
-
-
-// The algorithm is based on the article:
-// K. Cooper, T. Harvey and K. Kennedy "A Simple, Fast Dominance Algorithm"
-// Softw. Pract. Exper. 4 (2001), pp. 1-10.
-bool HeapSnapshotGenerator::BuildDominatorTree(
- const Vector<HeapEntry*>& entries,
- Vector<int>* dominators) {
- if (entries.length() == 0) return true;
- HeapEntry* root = snapshot_->root();
- const int entries_length = entries.length(), root_index = entries_length - 1;
- for (int i = 0; i < root_index; ++i) (*dominators)[i] = HeapEntry::kNoEntry;
- (*dominators)[root_index] = root_index;
-
- // The affected array is used to mark entries which dominators
- // have to be racalculated because of changes in their retainers.
- ScopedVector<bool> affected(entries_length);
- for (int i = 0; i < affected.length(); ++i) affected[i] = false;
- // Mark the root direct children as affected.
- Vector<HeapGraphEdge*> children = entries[root_index]->children();
- for (int i = 0; i < children.length(); ++i) {
- affected[children[i]->to()->postorder_index()] = true;
- }
-
- bool changed = true;
- while (changed) {
- changed = false;
- if (!ProgressReport(false)) return false;
- for (int i = root_index - 1; i >= 0; --i) {
- if (!affected[i]) continue;
- affected[i] = false;
- // If dominator of the entry has already been set to root,
- // then it can't propagate any further.
- if ((*dominators)[i] == root_index) continue;
- int new_idom_index = HeapEntry::kNoEntry;
- Vector<HeapGraphEdge*> rets = entries[i]->retainers();
- for (int j = 0; j < rets.length(); ++j) {
- if (rets[j]->from() != root && !IsRetainingEdge(rets[j])) continue;
- int ret_index = rets[j]->from()->postorder_index();
- if (dominators->at(ret_index) != HeapEntry::kNoEntry) {
- new_idom_index = new_idom_index == HeapEntry::kNoEntry
- ? ret_index
- : Intersect(ret_index, new_idom_index, *dominators);
- // If idom has already reached the root, it doesn't make sense
- // to check other retainers.
- if (new_idom_index == root_index) break;
- }
- }
- if (new_idom_index != HeapEntry::kNoEntry
- && dominators->at(i) != new_idom_index) {
- (*dominators)[i] = new_idom_index;
- changed = true;
- Vector<HeapGraphEdge*> children = entries[i]->children();
- for (int j = 0; j < children.length(); ++j) {
- affected[children[j]->to()->postorder_index()] = true;
- }
- }
- }
- }
- return true;
-}
-
-
-bool HeapSnapshotGenerator::SetEntriesDominators() {
- MarkUserReachableObjects();
- // This array is used for maintaining postorder of nodes.
- ScopedVector<HeapEntry*> ordered_entries(snapshot_->entries().length());
- FillPostorderIndexes(&ordered_entries);
- ScopedVector<int> dominators(ordered_entries.length());
- if (!BuildDominatorTree(ordered_entries, &dominators)) return false;
- for (int i = 0; i < ordered_entries.length(); ++i) {
- ASSERT(dominators[i] != HeapEntry::kNoEntry);
- ordered_entries[i]->set_dominator(ordered_entries[dominators[i]]);
- }
- return true;
-}
-
-
-bool HeapSnapshotGenerator::CalculateRetainedSizes() {
- // As for the dominators tree we only know parent nodes, not
- // children, to sum up total sizes we "bubble" node's self size
- // adding it to all of its parents.
- List<HeapEntry>& entries = snapshot_->entries();
- for (int i = 0; i < entries.length(); ++i) {
- HeapEntry* entry = &entries[i];
- entry->set_retained_size(entry->self_size());
- }
- for (int i = 0; i < entries.length(); ++i) {
- int entry_size = entries[i].self_size();
- HeapEntry* current = &entries[i];
- for (HeapEntry* dominator = current->dominator();
- dominator != current;
- current = dominator, dominator = current->dominator()) {
- ASSERT(current->dominator() != NULL);
- dominator->add_retained_size(entry_size);
- }
- }
- return true;
-}
-
-
template<int bytes> struct MaxDecimalDigitsIn;
template<> struct MaxDecimalDigitsIn<4> {
static const int kSigned = 11;
// type, name|index, to_node.
const int HeapSnapshotJSONSerializer::kEdgeFieldsCount = 3;
-// type, name, id, self_size, retained_size, dominator, children_index.
-const int HeapSnapshotJSONSerializer::kNodeFieldsCount = 7;
+// type, name, id, self_size, children_index.
+const int HeapSnapshotJSONSerializer::kNodeFieldsCount = 5;
void HeapSnapshotJSONSerializer::Serialize(v8::OutputStream* stream) {
ASSERT(writer_ == NULL);
(snapshot_->RawSnapshotSize() + MB - 1) / MB);
HeapEntry* message = result->AddEntry(HeapEntry::kString, text, 0, 4);
result->root()->SetIndexedReference(HeapGraphEdge::kElement, 1, message);
- result->FillChildrenAndRetainers();
- result->SetDominatorsToSelf();
+ result->FillChildren();
return result;
}
void HeapSnapshotJSONSerializer::SerializeNode(HeapEntry* entry,
int edges_index) {
- // The buffer needs space for 6 ints, 1 uint32_t, 7 commas, \n and \0
+ // The buffer needs space for 5 uint32_t, 5 commas, \n and \0
static const int kBufferSize =
- 6 * MaxDecimalDigitsIn<sizeof(int)>::kSigned // NOLINT
- + MaxDecimalDigitsIn<sizeof(uint32_t)>::kUnsigned // NOLINT
- + 7 + 1 + 1;
+ 5 * MaxDecimalDigitsIn<sizeof(uint32_t)>::kUnsigned // NOLINT
+ + 5 + 1 + 1;
EmbeddedVector<char, kBufferSize> buffer;
int buffer_pos = 0;
if (entry_index(entry) != 0) {
buffer[buffer_pos++] = ',';
buffer_pos = utoa(entry->self_size(), buffer, buffer_pos);
buffer[buffer_pos++] = ',';
- buffer_pos = utoa(entry->retained_size(), buffer, buffer_pos);
- buffer[buffer_pos++] = ',';
- buffer_pos = utoa(entry_index(entry->dominator()), buffer, buffer_pos);
- buffer[buffer_pos++] = ',';
buffer_pos = utoa(edges_index, buffer, buffer_pos);
buffer[buffer_pos++] = '\n';
buffer[buffer_pos++] = '\0';
JSON_S("name") ","
JSON_S("id") ","
JSON_S("self_size") ","
- JSON_S("retained_size") ","
- JSON_S("dominator") ","
JSON_S("edges_index")) ","
JSON_S("node_types") ":" JSON_A(
JSON_A(
void set_name(const char* name) { name_ = name; }
inline SnapshotObjectId 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 size) { retained_size_ = size; }
INLINE(int index() const);
- int postorder_index() { return postorder_index_; }
- void set_postorder_index(int value) { postorder_index_ = value; }
int children_count() const { return children_count_; }
INLINE(int set_children_index(int index));
- INLINE(int set_retainers_index(int index));
void add_child(HeapGraphEdge* edge) {
children_arr()[children_count_++] = edge;
}
- void add_retainer(HeapGraphEdge* edge) {
- retainers_arr()[retainers_count_++] = edge;
- }
Vector<HeapGraphEdge*> children() {
return Vector<HeapGraphEdge*>(children_arr(), children_count_); }
- Vector<HeapGraphEdge*> retainers() {
- return Vector<HeapGraphEdge*>(retainers_arr(), retainers_count_); }
- INLINE(HeapEntry* dominator() const);
- void set_dominator(HeapEntry* entry) {
- ASSERT(entry != NULL);
- dominator_ = entry->index();
- }
- void clear_paint() { painted_ = false; }
- bool painted() { return painted_; }
- void paint() { painted_ = true; }
- bool user_reachable() { return user_reachable_; }
- void set_user_reachable() { user_reachable_ = true; }
void SetIndexedReference(
HeapGraphEdge::Type type, int index, HeapEntry* entry);
private:
INLINE(HeapGraphEdge** children_arr());
- INLINE(HeapGraphEdge** retainers_arr());
const char* TypeAsString();
- unsigned painted_: 1;
- unsigned user_reachable_: 1;
- int dominator_: 30;
unsigned type_: 4;
- int retainers_count_: 28;
- int retainers_index_;
- int children_count_;
+ int children_count_: 28;
int children_index_;
int self_size_;
- union {
- int postorder_index_; // Used during dominator tree building.
- int retained_size_; // At that moment, there is no retained size yet.
- };
SnapshotObjectId id_;
HeapSnapshot* snapshot_;
const char* name_;
List<HeapEntry>& entries() { return entries_; }
List<HeapGraphEdge>& edges() { return edges_; }
List<HeapGraphEdge*>& children() { return children_; }
- List<HeapGraphEdge*>& retainers() { return retainers_; }
void RememberLastJSObjectId();
SnapshotObjectId max_snapshot_js_object_id() const {
return max_snapshot_js_object_id_;
HeapEntry* AddGcRootsEntry();
HeapEntry* AddGcSubrootEntry(int tag);
HeapEntry* AddNativesRootEntry();
- void ClearPaint();
HeapEntry* GetEntryById(SnapshotObjectId id);
List<HeapEntry*>* GetSortedEntriesList();
- void SetDominatorsToSelf();
- void FillChildrenAndRetainers();
+ void FillChildren();
void Print(int max_depth);
void PrintEntriesSize();
List<HeapEntry> entries_;
List<HeapGraphEdge> edges_;
List<HeapGraphEdge*> children_;
- List<HeapGraphEdge*> retainers_;
List<HeapEntry*> sorted_entries_;
SnapshotObjectId max_snapshot_js_object_id_;
bool GenerateSnapshot();
private:
- bool BuildDominatorTree(const Vector<HeapEntry*>& entries,
- Vector<int>* dominators);
- bool CalculateRetainedSizes();
bool FillReferences();
- void FillPostorderIndexes(Vector<HeapEntry*>* entries);
- bool IsUserGlobalReference(const HeapGraphEdge* edge);
- void MarkUserReachableObjects();
void ProgressStep();
bool ProgressReport(bool force = false);
- bool SetEntriesDominators();
void SetProgressTotal(int iterations_count);
HeapSnapshot* snapshot_;
#include "v8.h"
#include "cctest.h"
+#include "hashmap.h"
#include "heap-profiler.h"
#include "snapshot.h"
#include "debug.h"
if (strcmp(entry->name(), "C2") == 0) has_C2 = true;
}
+ static bool AddressesMatch(void* key1, void* key2) {
+ return key1 == key2;
+ }
+
void CheckAllReachables(i::HeapEntry* root) {
+ i::HashMap visited(AddressesMatch);
i::List<i::HeapEntry*> list(10);
list.Add(root);
- root->paint();
CheckEntry(root);
while (!list.is_empty()) {
i::HeapEntry* entry = list.RemoveLast();
for (int i = 0; i < children.length(); ++i) {
if (children[i]->type() == i::HeapGraphEdge::kShortcut) continue;
i::HeapEntry* child = children[i]->to();
- if (!child->painted()) {
- list.Add(child);
- child->paint();
- CheckEntry(child);
- }
+ i::HashMap::Entry* entry = visited.Lookup(
+ reinterpret_cast<void*>(child),
+ static_cast<uint32_t>(reinterpret_cast<uintptr_t>(child)),
+ true);
+ if (entry->value)
+ continue;
+ entry->value = reinterpret_cast<void*>(1);
+ list.Add(child);
+ CheckEntry(child);
}
}
}
"var c2 = new C2(a2);");
const v8::HeapSnapshot* snapshot_env2 =
v8::HeapProfiler::TakeSnapshot(v8_str("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);
// Verify, that JS global object of env2 has '..2' properties.
NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "b2_2"));
CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "c2"));
- // Paint all nodes reachable from global object.
NamedEntriesDetector det;
- i_snapshot_env2->ClearPaint();
det.CheckAllReachables(const_cast<i::HeapEntry*>(
reinterpret_cast<const i::HeapEntry*>(global_env2)));
CHECK(det.has_A2);
CHECK_NE(NULL, x2);
// Test sizes.
- CHECK_EQ(x->GetSelfSize() * 3, x->GetRetainedSize());
- CHECK_EQ(x1->GetSelfSize(), x1->GetRetainedSize());
- CHECK_EQ(x2->GetSelfSize(), x2->GetRetainedSize());
+ CHECK_NE(0, x->GetSelfSize());
+ CHECK_NE(0, x1->GetSelfSize());
+ CHECK_NE(0, x2->GetSelfSize());
}
}
-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_str("dominators"));
-
- const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
- CHECK_NE(NULL, global);
- const v8::HeapGraphNode* node6 =
- GetProperty(global, v8::HeapGraphEdge::kProperty, "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 {