diff --git a/include/v8-profiler.h b/include/v8-profiler.h index d3880bce56..3e1952c7ad 100644 --- a/include/v8-profiler.h +++ b/include/v8-profiler.h @@ -196,7 +196,9 @@ class V8EXPORT HeapGraphEdge { enum Type { CONTEXT_VARIABLE = 0, // A variable from a function context. ELEMENT = 1, // An element of an array. - PROPERTY = 2 // A named object property. + PROPERTY = 2, // A named object property. + INTERNAL = 3 // A link that can't be accessed from JS, + // thus, its name isn't a real property name. }; /** Returns edge type (see HeapGraphEdge::Type). */ diff --git a/src/api.cc b/src/api.cc index 5c88a876ec..ceb6f81ddf 100644 --- a/src/api.cc +++ b/src/api.cc @@ -4460,6 +4460,7 @@ Handle HeapGraphEdge::GetName() const { reinterpret_cast(this); switch (edge->type()) { case i::HeapGraphEdge::CONTEXT_VARIABLE: + case i::HeapGraphEdge::INTERNAL: case i::HeapGraphEdge::PROPERTY: return Handle(ToApi(i::Factory::LookupAsciiSymbol( edge->name()))); diff --git a/src/checks.h b/src/checks.h index c2e40ba9a4..13374d86dd 100644 --- a/src/checks.h +++ b/src/checks.h @@ -155,9 +155,9 @@ static inline void CheckNonEqualsHelper(const char* file, static inline void CheckEqualsHelper(const char* file, int line, const char* expected_source, - void* expected, + const void* expected, const char* value_source, - void* value) { + const void* value) { if (expected != value) { V8_Fatal(file, line, "CHECK_EQ(%s, %s) failed\n# Expected: %p\n# Found: %p", @@ -170,9 +170,9 @@ static inline void CheckEqualsHelper(const char* file, static inline void CheckNonEqualsHelper(const char* file, int line, const char* expected_source, - void* expected, + const void* expected, const char* value_source, - void* value) { + const void* value) { if (expected == value) { V8_Fatal(file, line, "CHECK_NE(%s, %s) failed\n# Value: %p", expected_source, value_source, value); diff --git a/src/profile-generator.cc b/src/profile-generator.cc index 26457e00d2..57ff6610e8 100644 --- a/src/profile-generator.cc +++ b/src/profile-generator.cc @@ -818,7 +818,7 @@ HeapGraphEdge::HeapGraphEdge(Type type, HeapEntry* from, HeapEntry* to) : type_(type), name_(name), from_(from), to_(to) { - ASSERT(type_ == CONTEXT_VARIABLE || type_ == PROPERTY); + ASSERT(type_ == CONTEXT_VARIABLE || type_ == PROPERTY || type_ == INTERNAL); } @@ -845,26 +845,30 @@ HeapEntry::~HeapEntry() { } -void HeapEntry::SetClosureReference(const char* name, HeapEntry* entry) { - HeapGraphEdge* edge = - new HeapGraphEdge(HeapGraphEdge::CONTEXT_VARIABLE, name, this, entry); +void HeapEntry::AddEdge(HeapGraphEdge* edge) { children_.Add(edge); - entry->retainers_.Add(edge); + edge->to()->retainers_.Add(edge); +} + + +void HeapEntry::SetClosureReference(const char* name, HeapEntry* entry) { + AddEdge( + new HeapGraphEdge(HeapGraphEdge::CONTEXT_VARIABLE, name, this, entry)); } void HeapEntry::SetElementReference(int index, HeapEntry* entry) { - HeapGraphEdge* edge = new HeapGraphEdge(index, this, entry); - children_.Add(edge); - entry->retainers_.Add(edge); + AddEdge(new HeapGraphEdge(index, this, entry)); +} + + +void HeapEntry::SetInternalReference(const char* name, HeapEntry* entry) { + AddEdge(new HeapGraphEdge(HeapGraphEdge::INTERNAL, name, this, entry)); } void HeapEntry::SetPropertyReference(const char* name, HeapEntry* entry) { - HeapGraphEdge* edge = - new HeapGraphEdge(HeapGraphEdge::PROPERTY, name, this, entry); - children_.Add(edge); - entry->retainers_.Add(edge); + AddEdge(new HeapGraphEdge(HeapGraphEdge::PROPERTY, name, this, entry)); } @@ -1074,7 +1078,7 @@ void HeapEntry::CutEdges() { void HeapEntry::Print(int max_depth, int indent) { - OS::Print("%6d %6d %6d", self_size_, TotalSize(), NonSharedTotalSize()); + OS::Print("%6d %6d %6d ", self_size_, TotalSize(), NonSharedTotalSize()); if (type_ != STRING) { OS::Print("%s %.40s\n", TypeAsString(), name_); } else { @@ -1100,6 +1104,9 @@ void HeapEntry::Print(int max_depth, int indent) { 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; @@ -1145,6 +1152,9 @@ void HeapGraphPath::Print() { case HeapGraphEdge::ELEMENT: OS::Print("[%d] ", edge->index()); break; + case HeapGraphEdge::INTERNAL: + OS::Print("[$%s] ", edge->name()); + break; case HeapGraphEdge::PROPERTY: OS::Print("[%s] ", edge->name()); break; @@ -1318,6 +1328,16 @@ void HeapSnapshot::SetElementReference(HeapEntry* parent, } +void HeapSnapshot::SetInternalReference(HeapEntry* parent, + const char* reference_name, + Object* child) { + HeapEntry* child_entry = GetEntry(child); + if (child_entry != NULL) { + parent->SetInternalReference(reference_name, child_entry); + } +} + + void HeapSnapshot::SetPropertyReference(HeapEntry* parent, String* reference_name, Object* child) { @@ -1546,6 +1566,7 @@ void HeapSnapshotGenerator::ExtractClosureReferences(JSObject* js_obj, snapshot_->SetClosureReference(entry, local_name, context->get(idx)); } } + snapshot_->SetInternalReference(entry, "code", func->shared()); } } diff --git a/src/profile-generator.h b/src/profile-generator.h index ecac8e28fb..4e423c8d92 100644 --- a/src/profile-generator.h +++ b/src/profile-generator.h @@ -431,7 +431,8 @@ class HeapGraphEdge { enum Type { CONTEXT_VARIABLE = v8::HeapGraphEdge::CONTEXT_VARIABLE, ELEMENT = v8::HeapGraphEdge::ELEMENT, - PROPERTY = v8::HeapGraphEdge::PROPERTY + PROPERTY = v8::HeapGraphEdge::PROPERTY, + INTERNAL = v8::HeapGraphEdge::INTERNAL }; HeapGraphEdge(Type type, const char* name, HeapEntry* from, HeapEntry* to); @@ -443,7 +444,7 @@ class HeapGraphEdge { return index_; } const char* name() const { - ASSERT(type_ == CONTEXT_VARIABLE || type_ == PROPERTY); + ASSERT(type_ == CONTEXT_VARIABLE || type_ == PROPERTY || type_ == INTERNAL); return name_; } HeapEntry* from() const { return from_; } @@ -533,6 +534,7 @@ class HeapEntry { 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); @@ -542,6 +544,7 @@ class HeapEntry { void Print(int max_depth, int indent); private: + void AddEdge(HeapGraphEdge* edge); int CalculateTotalSize(); int CalculateNonSharedTotalSize(); void FindRetainingPaths(HeapEntry* node, CachedHeapGraphPath* prev_path); @@ -641,6 +644,8 @@ class HeapSnapshot { 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); diff --git a/test/cctest/test-heap-profiler.cc b/test/cctest/test-heap-profiler.cc index 478f00caf7..33477acfa2 100644 --- a/test/cctest/test-heap-profiler.cc +++ b/test/cctest/test-heap-profiler.cc @@ -428,6 +428,53 @@ class NamedEntriesDetector { } // namespace + +static const v8::HeapGraphNode* GetGlobalObject( + const v8::HeapSnapshot* snapshot) { + if (i::Snapshot::IsEnabled()) { + // In case if snapshots are enabled, there will present a + // vanilla deserealized global object, without properties + // added by the test code. + CHECK_EQ(2, snapshot->GetHead()->GetChildrenCount()); + // Choose the global object of a bigger size. + const v8::HeapGraphNode* node0 = + snapshot->GetHead()->GetChild(0)->GetToNode(); + const v8::HeapGraphNode* node1 = + snapshot->GetHead()->GetChild(1)->GetToNode(); + return node0->GetTotalSize() > node1->GetTotalSize() ? node0 : node1; + } else { + CHECK_EQ(1, snapshot->GetHead()->GetChildrenCount()); + return snapshot->GetHead()->GetChild(0)->GetToNode(); + } +} + + +static const v8::HeapGraphNode* GetProperty(const v8::HeapGraphNode* node, + v8::HeapGraphEdge::Type type, + const char* name) { + for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) { + const v8::HeapGraphEdge* prop = node->GetChild(i); + v8::String::AsciiValue prop_name(prop->GetName()); + if (prop->GetType() == type && strcmp(name, *prop_name) == 0) + return prop->GetToNode(); + } + return NULL; +} + + +static bool HasString(const v8::HeapGraphNode* node, const char* contents) { + for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) { + const v8::HeapGraphEdge* prop = node->GetChild(i); + const v8::HeapGraphNode* node = prop->GetToNode(); + if (node->GetType() == v8::HeapGraphNode::STRING) { + v8::String::AsciiValue node_name(node->GetName()); + if (strcmp(contents, *node_name) == 0) return true; + } + } + return false; +} + + TEST(HeapSnapshot) { v8::HandleScope scope; @@ -458,53 +505,20 @@ TEST(HeapSnapshot) { "var c2 = new C2(a2);"); const v8::HeapSnapshot* snapshot_env2 = v8::HeapProfiler::TakeSnapshot(v8::String::New("env2")); - const v8::HeapGraphNode* global_env2; - if (i::Snapshot::IsEnabled()) { - // In case if snapshots are enabled, there will present a - // vanilla deserealized global object, without properties - // added by the test code. - CHECK_EQ(2, snapshot_env2->GetHead()->GetChildrenCount()); - // Choose the global object of a bigger size. - const v8::HeapGraphNode* node0 = - snapshot_env2->GetHead()->GetChild(0)->GetToNode(); - const v8::HeapGraphNode* node1 = - snapshot_env2->GetHead()->GetChild(1)->GetToNode(); - global_env2 = node0->GetTotalSize() > node1->GetTotalSize() ? - node0 : node1; - } else { - CHECK_EQ(1, snapshot_env2->GetHead()->GetChildrenCount()); - global_env2 = snapshot_env2->GetHead()->GetChild(0)->GetToNode(); - } + const v8::HeapGraphNode* global_env2 = GetGlobalObject(snapshot_env2); // Verify, that JS global object of env2 doesn't have '..1' // properties, but has '..2' properties. - bool has_a1 = false, has_b1_1 = false, has_b1_2 = false, has_c1 = false; - bool has_a2 = false, has_b2_1 = false, has_b2_2 = false, has_c2 = false; - // This will be needed further. - const v8::HeapGraphNode* a2_node = NULL; - for (int i = 0, count = global_env2->GetChildrenCount(); i < count; ++i) { - const v8::HeapGraphEdge* prop = global_env2->GetChild(i); - v8::String::AsciiValue prop_name(prop->GetName()); - if (strcmp("a1", *prop_name) == 0) has_a1 = true; - if (strcmp("b1_1", *prop_name) == 0) has_b1_1 = true; - if (strcmp("b1_2", *prop_name) == 0) has_b1_2 = true; - if (strcmp("c1", *prop_name) == 0) has_c1 = true; - if (strcmp("a2", *prop_name) == 0) { - has_a2 = true; - a2_node = prop->GetToNode(); - } - if (strcmp("b2_1", *prop_name) == 0) has_b2_1 = true; - if (strcmp("b2_2", *prop_name) == 0) has_b2_2 = true; - if (strcmp("c2", *prop_name) == 0) has_c2 = true; - } - CHECK(!has_a1); - CHECK(!has_b1_1); - CHECK(!has_b1_2); - CHECK(!has_c1); - CHECK(has_a2); - CHECK(has_b2_1); - CHECK(has_b2_2); - CHECK(has_c2); + CHECK_EQ(NULL, GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "a1")); + CHECK_EQ(NULL, GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "b1_1")); + CHECK_EQ(NULL, GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "b1_2")); + CHECK_EQ(NULL, GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "c1")); + const v8::HeapGraphNode* a2_node = + GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "a2"); + CHECK_NE(NULL, a2_node); + CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "b2_1")); + CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "b2_2")); + CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::PROPERTY, "c2")); // Verify that anything related to '[ABC]1' is not reachable. NamedEntriesDetector det; @@ -565,4 +579,62 @@ TEST(HeapSnapshot) { CHECK(has_b2_2_x_ref); } + +TEST(HeapSnapshotCodeObjects) { + v8::HandleScope scope; + v8::Handle env = v8::Context::New(); + env->Enter(); + + CompileAndRunScript( + "function lazy(x) { return x - 1; }\n" + "function compiled(x) { return x + 1; }\n" + "compiled(1)"); + const v8::HeapSnapshot* snapshot = + v8::HeapProfiler::TakeSnapshot(v8::String::New("code")); + + const v8::HeapGraphNode* global = GetGlobalObject(snapshot); + const v8::HeapGraphNode* compiled = + GetProperty(global, v8::HeapGraphEdge::PROPERTY, "compiled"); + CHECK_NE(NULL, compiled); + CHECK_EQ(v8::HeapGraphNode::CLOSURE, compiled->GetType()); + const v8::HeapGraphNode* lazy = + GetProperty(global, v8::HeapGraphEdge::PROPERTY, "lazy"); + CHECK_NE(NULL, lazy); + CHECK_EQ(v8::HeapGraphNode::CLOSURE, lazy->GetType()); + + // Find references to code. + const v8::HeapGraphNode* compiled_code = + GetProperty(compiled, v8::HeapGraphEdge::INTERNAL, "code"); + CHECK_NE(NULL, compiled_code); + const v8::HeapGraphNode* lazy_code = + GetProperty(lazy, v8::HeapGraphEdge::INTERNAL, "code"); + CHECK_NE(NULL, lazy_code); + + // Verify that non-compiled code doesn't contain references to "x" + // literal, while compiled code does. + bool compiled_references_x = false, lazy_references_x = false; + for (int i = 0, count = compiled_code->GetChildrenCount(); i < count; ++i) { + const v8::HeapGraphEdge* prop = compiled_code->GetChild(i); + const v8::HeapGraphNode* node = prop->GetToNode(); + if (node->GetType() == v8::HeapGraphNode::CODE) { + if (HasString(node, "x")) { + compiled_references_x = true; + break; + } + } + } + for (int i = 0, count = lazy_code->GetChildrenCount(); i < count; ++i) { + const v8::HeapGraphEdge* prop = lazy_code->GetChild(i); + const v8::HeapGraphNode* node = prop->GetToNode(); + if (node->GetType() == v8::HeapGraphNode::CODE) { + if (HasString(node, "x")) { + lazy_references_x = true; + break; + } + } + } + CHECK(compiled_references_x); + CHECK(!lazy_references_x); +} + #endif // ENABLE_LOGGING_AND_PROFILING