Index: test/cctest/test-heap-profiler.cc |
diff --git a/test/cctest/test-heap-profiler.cc b/test/cctest/test-heap-profiler.cc |
index a3e14cb74fbf6bdc6924ae7eafed12087e0c9be9..a2426cc0fe7cf80be3845b597f6221165261b691 100644 |
--- a/test/cctest/test-heap-profiler.cc |
+++ b/test/cctest/test-heap-profiler.cc |
@@ -9,381 +9,10 @@ |
#include "cctest.h" |
#include "heap-profiler.h" |
#include "snapshot.h" |
-#include "string-stream.h" |
#include "utils-inl.h" |
-#include "zone-inl.h" |
#include "../include/v8-profiler.h" |
namespace i = v8::internal; |
-using i::ClustersCoarser; |
-using i::JSObjectsCluster; |
-using i::JSObjectsRetainerTree; |
-using i::JSObjectsClusterTree; |
-using i::RetainerHeapProfile; |
- |
- |
-namespace { |
- |
-class ConstructorHeapProfileTestHelper : public i::ConstructorHeapProfile { |
- public: |
- ConstructorHeapProfileTestHelper() |
- : i::ConstructorHeapProfile(), |
- f_name_(FACTORY->NewStringFromAscii(i::CStrVector("F"))), |
- f_count_(0) { |
- } |
- |
- void Call(const JSObjectsCluster& cluster, |
- const i::NumberAndSizeInfo& number_and_size) { |
- if (f_name_->Equals(cluster.constructor())) { |
- CHECK_EQ(f_count_, 0); |
- f_count_ = number_and_size.number(); |
- CHECK_GT(f_count_, 0); |
- } |
- } |
- |
- int f_count() { return f_count_; } |
- |
- private: |
- i::Handle<i::String> f_name_; |
- int f_count_; |
-}; |
- |
-} // namespace |
- |
- |
-TEST(ConstructorProfile) { |
- v8::HandleScope scope; |
- LocalContext env; |
- |
- CompileRun( |
- "function F() {} // A constructor\n" |
- "var f1 = new F();\n" |
- "var f2 = new F();\n"); |
- |
- ConstructorHeapProfileTestHelper cons_profile; |
- i::AssertNoAllocation no_alloc; |
- i::HeapIterator iterator; |
- for (i::HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) |
- cons_profile.CollectStats(obj); |
- CHECK_EQ(0, cons_profile.f_count()); |
- cons_profile.PrintStats(); |
- CHECK_EQ(2, cons_profile.f_count()); |
-} |
- |
- |
-static JSObjectsCluster AddHeapObjectToTree(JSObjectsRetainerTree* tree, |
- i::String* constructor, |
- int instance, |
- JSObjectsCluster* ref1 = NULL, |
- JSObjectsCluster* ref2 = NULL, |
- JSObjectsCluster* ref3 = NULL) { |
- JSObjectsCluster o(constructor, reinterpret_cast<i::Object*>(instance)); |
- JSObjectsClusterTree* o_tree = new JSObjectsClusterTree(); |
- JSObjectsClusterTree::Locator o_loc; |
- if (ref1 != NULL) o_tree->Insert(*ref1, &o_loc); |
- if (ref2 != NULL) o_tree->Insert(*ref2, &o_loc); |
- if (ref3 != NULL) o_tree->Insert(*ref3, &o_loc); |
- JSObjectsRetainerTree::Locator loc; |
- tree->Insert(o, &loc); |
- loc.set_value(o_tree); |
- return o; |
-} |
- |
- |
-static void AddSelfReferenceToTree(JSObjectsRetainerTree* tree, |
- JSObjectsCluster* self_ref) { |
- JSObjectsRetainerTree::Locator loc; |
- CHECK(tree->Find(*self_ref, &loc)); |
- JSObjectsClusterTree::Locator o_loc; |
- CHECK_NE(NULL, loc.value()); |
- loc.value()->Insert(*self_ref, &o_loc); |
-} |
- |
- |
-static inline void CheckEqualsHelper(const char* file, int line, |
- const char* expected_source, |
- const JSObjectsCluster& expected, |
- const char* value_source, |
- const JSObjectsCluster& value) { |
- if (JSObjectsCluster::Compare(expected, value) != 0) { |
- i::HeapStringAllocator allocator; |
- i::StringStream stream(&allocator); |
- stream.Add("# Expected: "); |
- expected.DebugPrint(&stream); |
- stream.Add("\n# Found: "); |
- value.DebugPrint(&stream); |
- V8_Fatal(file, line, "CHECK_EQ(%s, %s) failed\n%s", |
- expected_source, value_source, |
- *stream.ToCString()); |
- } |
-} |
- |
- |
-static inline void CheckNonEqualsHelper(const char* file, int line, |
- const char* expected_source, |
- const JSObjectsCluster& expected, |
- const char* value_source, |
- const JSObjectsCluster& value) { |
- if (JSObjectsCluster::Compare(expected, value) == 0) { |
- i::HeapStringAllocator allocator; |
- i::StringStream stream(&allocator); |
- stream.Add("# !Expected: "); |
- expected.DebugPrint(&stream); |
- stream.Add("\n# Found: "); |
- value.DebugPrint(&stream); |
- V8_Fatal(file, line, "CHECK_NE(%s, %s) failed\n%s", |
- expected_source, value_source, |
- *stream.ToCString()); |
- } |
-} |
- |
- |
-TEST(ClustersCoarserSimple) { |
- v8::HandleScope scope; |
- LocalContext env; |
- |
- i::ZoneScope zn_scope(i::Isolate::Current(), i::DELETE_ON_EXIT); |
- |
- JSObjectsRetainerTree tree; |
- JSObjectsCluster function(HEAP->function_class_symbol()); |
- JSObjectsCluster a(*FACTORY->NewStringFromAscii(i::CStrVector("A"))); |
- JSObjectsCluster b(*FACTORY->NewStringFromAscii(i::CStrVector("B"))); |
- |
- // o1 <- Function |
- JSObjectsCluster o1 = |
- AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x100, &function); |
- // o2 <- Function |
- JSObjectsCluster o2 = |
- AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x200, &function); |
- // o3 <- A, B |
- JSObjectsCluster o3 = |
- AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x300, &a, &b); |
- // o4 <- B, A |
- JSObjectsCluster o4 = |
- AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x400, &b, &a); |
- // o5 <- A, B, Function |
- JSObjectsCluster o5 = |
- AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x500, |
- &a, &b, &function); |
- |
- ClustersCoarser coarser; |
- coarser.Process(&tree); |
- |
- CHECK_EQ(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(o2)); |
- CHECK_EQ(coarser.GetCoarseEquivalent(o3), coarser.GetCoarseEquivalent(o4)); |
- CHECK_NE(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(o3)); |
- CHECK_EQ(JSObjectsCluster(), coarser.GetCoarseEquivalent(o5)); |
-} |
- |
- |
-TEST(ClustersCoarserMultipleConstructors) { |
- v8::HandleScope scope; |
- LocalContext env; |
- |
- i::ZoneScope zn_scope(i::Isolate::Current(), i::DELETE_ON_EXIT); |
- |
- JSObjectsRetainerTree tree; |
- JSObjectsCluster function(HEAP->function_class_symbol()); |
- |
- // o1 <- Function |
- JSObjectsCluster o1 = |
- AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x100, &function); |
- // a1 <- Function |
- JSObjectsCluster a1 = |
- AddHeapObjectToTree(&tree, HEAP->Array_symbol(), 0x1000, &function); |
- // o2 <- Function |
- JSObjectsCluster o2 = |
- AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x200, &function); |
- // a2 <- Function |
- JSObjectsCluster a2 = |
- AddHeapObjectToTree(&tree, HEAP->Array_symbol(), 0x2000, &function); |
- |
- ClustersCoarser coarser; |
- coarser.Process(&tree); |
- |
- CHECK_EQ(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(o2)); |
- CHECK_EQ(coarser.GetCoarseEquivalent(a1), coarser.GetCoarseEquivalent(a2)); |
-} |
- |
- |
-TEST(ClustersCoarserPathsTraversal) { |
- v8::HandleScope scope; |
- LocalContext env; |
- |
- i::ZoneScope zn_scope(i::Isolate::Current(), i::DELETE_ON_EXIT); |
- |
- JSObjectsRetainerTree tree; |
- |
- // On the following graph: |
- // |
- // p |
- // <- o21 <- o11 <- |
- // q o |
- // <- o22 <- o12 <- |
- // r |
- // |
- // we expect that coarser will deduce equivalences: p ~ q ~ r, |
- // o21 ~ o22, and o11 ~ o12. |
- |
- JSObjectsCluster o = |
- AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x100); |
- JSObjectsCluster o11 = |
- AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x110, &o); |
- JSObjectsCluster o12 = |
- AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x120, &o); |
- JSObjectsCluster o21 = |
- AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x210, &o11); |
- JSObjectsCluster o22 = |
- AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x220, &o12); |
- JSObjectsCluster p = |
- AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x300, &o21); |
- JSObjectsCluster q = |
- AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x310, &o21, &o22); |
- JSObjectsCluster r = |
- AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x320, &o22); |
- |
- ClustersCoarser coarser; |
- coarser.Process(&tree); |
- |
- CHECK_EQ(JSObjectsCluster(), coarser.GetCoarseEquivalent(o)); |
- CHECK_NE(JSObjectsCluster(), coarser.GetCoarseEquivalent(o11)); |
- CHECK_EQ(coarser.GetCoarseEquivalent(o11), coarser.GetCoarseEquivalent(o12)); |
- CHECK_EQ(coarser.GetCoarseEquivalent(o21), coarser.GetCoarseEquivalent(o22)); |
- CHECK_NE(coarser.GetCoarseEquivalent(o11), coarser.GetCoarseEquivalent(o21)); |
- CHECK_NE(JSObjectsCluster(), coarser.GetCoarseEquivalent(p)); |
- CHECK_EQ(coarser.GetCoarseEquivalent(p), coarser.GetCoarseEquivalent(q)); |
- CHECK_EQ(coarser.GetCoarseEquivalent(q), coarser.GetCoarseEquivalent(r)); |
- CHECK_NE(coarser.GetCoarseEquivalent(o11), coarser.GetCoarseEquivalent(p)); |
- CHECK_NE(coarser.GetCoarseEquivalent(o21), coarser.GetCoarseEquivalent(p)); |
-} |
- |
- |
-TEST(ClustersCoarserSelf) { |
- v8::HandleScope scope; |
- LocalContext env; |
- |
- i::ZoneScope zn_scope(i::Isolate::Current(), i::DELETE_ON_EXIT); |
- |
- JSObjectsRetainerTree tree; |
- |
- // On the following graph: |
- // |
- // p (self-referencing) |
- // <- o1 <- |
- // q (self-referencing) o |
- // <- o2 <- |
- // r (self-referencing) |
- // |
- // we expect that coarser will deduce equivalences: p ~ q ~ r, o1 ~ o2; |
- |
- JSObjectsCluster o = |
- AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x100); |
- JSObjectsCluster o1 = |
- AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x110, &o); |
- JSObjectsCluster o2 = |
- AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x120, &o); |
- JSObjectsCluster p = |
- AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x300, &o1); |
- AddSelfReferenceToTree(&tree, &p); |
- JSObjectsCluster q = |
- AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x310, &o1, &o2); |
- AddSelfReferenceToTree(&tree, &q); |
- JSObjectsCluster r = |
- AddHeapObjectToTree(&tree, HEAP->Object_symbol(), 0x320, &o2); |
- AddSelfReferenceToTree(&tree, &r); |
- |
- ClustersCoarser coarser; |
- coarser.Process(&tree); |
- |
- CHECK_EQ(JSObjectsCluster(), coarser.GetCoarseEquivalent(o)); |
- CHECK_NE(JSObjectsCluster(), coarser.GetCoarseEquivalent(o1)); |
- CHECK_EQ(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(o2)); |
- CHECK_NE(JSObjectsCluster(), coarser.GetCoarseEquivalent(p)); |
- CHECK_EQ(coarser.GetCoarseEquivalent(p), coarser.GetCoarseEquivalent(q)); |
- CHECK_EQ(coarser.GetCoarseEquivalent(q), coarser.GetCoarseEquivalent(r)); |
- CHECK_NE(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(p)); |
-} |
- |
- |
-namespace { |
- |
-class RetainerProfilePrinter : public RetainerHeapProfile::Printer { |
- public: |
- RetainerProfilePrinter() : stream_(&allocator_), lines_(100) {} |
- |
- void PrintRetainers(const JSObjectsCluster& cluster, |
- const i::StringStream& retainers) { |
- cluster.Print(&stream_); |
- stream_.Add("%s", *(retainers.ToCString())); |
- stream_.Put('\0'); |
- } |
- |
- const char* GetRetainers(const char* constructor) { |
- FillLines(); |
- const size_t cons_len = strlen(constructor); |
- for (int i = 0; i < lines_.length(); ++i) { |
- if (strncmp(constructor, lines_[i], cons_len) == 0 && |
- lines_[i][cons_len] == ',') { |
- return lines_[i] + cons_len + 1; |
- } |
- } |
- return NULL; |
- } |
- |
- private: |
- void FillLines() { |
- if (lines_.length() > 0) return; |
- stream_.Put('\0'); |
- stream_str_ = stream_.ToCString(); |
- const char* pos = *stream_str_; |
- while (pos != NULL && *pos != '\0') { |
- lines_.Add(pos); |
- pos = strchr(pos, '\0'); |
- if (pos != NULL) ++pos; |
- } |
- } |
- |
- i::HeapStringAllocator allocator_; |
- i::StringStream stream_; |
- i::SmartPointer<const char> stream_str_; |
- i::List<const char*> lines_; |
-}; |
- |
-} // namespace |
- |
- |
-TEST(RetainerProfile) { |
- v8::HandleScope scope; |
- LocalContext env; |
- |
- CompileRun( |
- "function A() {}\n" |
- "function B(x) { this.x = x; }\n" |
- "function C(x) { this.x1 = x; this.x2 = x; }\n" |
- "var a = new A();\n" |
- "var b1 = new B(a), b2 = new B(a);\n" |
- "var c = new C(a);"); |
- |
- RetainerHeapProfile ret_profile; |
- i::AssertNoAllocation no_alloc; |
- i::HeapIterator iterator; |
- for (i::HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) |
- ret_profile.CollectStats(obj); |
- ret_profile.CoarseAndAggregate(); |
- RetainerProfilePrinter printer; |
- ret_profile.DebugPrintStats(&printer); |
- const char* retainers_of_a = printer.GetRetainers("A"); |
- // The order of retainers is unspecified, so we check string length, and |
- // verify each retainer separately. |
- CHECK_EQ(i::StrLength("(global property);1,B;2,C;2"), |
- i::StrLength(retainers_of_a)); |
- CHECK(strstr(retainers_of_a, "(global property);1") != NULL); |
- CHECK(strstr(retainers_of_a, "B;2") != NULL); |
- CHECK(strstr(retainers_of_a, "C;2") != NULL); |
- CHECK_EQ("(global property);2", printer.GetRetainers("B")); |
- CHECK_EQ("(global property);1", printer.GetRetainers("C")); |
-} |
- |
namespace { |
@@ -726,116 +355,6 @@ TEST(HeapSnapshotRootPreservedAfterSorting) { |
} |
-static const v8::HeapGraphNode* GetChild( |
- const v8::HeapGraphNode* node, |
- v8::HeapGraphNode::Type type, |
- const char* name, |
- const v8::HeapGraphNode* after = NULL) { |
- bool ignore_child = after == NULL ? false : true; |
- for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) { |
- const v8::HeapGraphEdge* prop = node->GetChild(i); |
- const v8::HeapGraphNode* child = prop->GetToNode(); |
- v8::String::AsciiValue child_name(child->GetName()); |
- if (!ignore_child |
- && child->GetType() == type |
- && strcmp(name, *child_name) == 0) |
- return child; |
- if (after != NULL && child == after) ignore_child = false; |
- } |
- return NULL; |
-} |
- |
-static bool IsNodeRetainedAs(const v8::HeapGraphNode* node, |
- int element) { |
- for (int i = 0, count = node->GetRetainersCount(); i < count; ++i) { |
- const v8::HeapGraphEdge* prop = node->GetRetainer(i); |
- if (prop->GetType() == v8::HeapGraphEdge::kElement |
- && element == prop->GetName()->Int32Value()) |
- return true; |
- } |
- return false; |
-} |
- |
-TEST(AggregatedHeapSnapshot) { |
- v8::HandleScope scope; |
- LocalContext env; |
- |
- CompileRun( |
- "function A() {}\n" |
- "function B(x) { this.x = x; }\n" |
- "var a = new A();\n" |
- "var b = new B(a);"); |
- const v8::HeapSnapshot* snapshot = |
- v8::HeapProfiler::TakeSnapshot( |
- v8::String::New("agg"), v8::HeapSnapshot::kAggregated); |
- const v8::HeapGraphNode* strings = GetChild(snapshot->GetRoot(), |
- v8::HeapGraphNode::kHidden, |
- "STRING_TYPE"); |
- CHECK_NE(NULL, strings); |
- CHECK_NE(0, strings->GetSelfSize()); |
- CHECK_NE(0, strings->GetInstancesCount()); |
- const v8::HeapGraphNode* maps = GetChild(snapshot->GetRoot(), |
- v8::HeapGraphNode::kHidden, |
- "MAP_TYPE"); |
- CHECK_NE(NULL, maps); |
- CHECK_NE(0, maps->GetSelfSize()); |
- CHECK_NE(0, maps->GetInstancesCount()); |
- |
- const v8::HeapGraphNode* a = GetChild(snapshot->GetRoot(), |
- v8::HeapGraphNode::kObject, |
- "A"); |
- CHECK_NE(NULL, a); |
- CHECK_NE(0, a->GetSelfSize()); |
- CHECK_EQ(1, a->GetInstancesCount()); |
- |
- const v8::HeapGraphNode* b = GetChild(snapshot->GetRoot(), |
- v8::HeapGraphNode::kObject, |
- "B"); |
- CHECK_NE(NULL, b); |
- CHECK_NE(0, b->GetSelfSize()); |
- CHECK_EQ(1, b->GetInstancesCount()); |
- |
- const v8::HeapGraphNode* glob_prop = GetChild(snapshot->GetRoot(), |
- v8::HeapGraphNode::kObject, |
- "(global property)", |
- b); |
- CHECK_NE(NULL, glob_prop); |
- CHECK_EQ(0, glob_prop->GetSelfSize()); |
- CHECK_EQ(0, glob_prop->GetInstancesCount()); |
- CHECK_NE(0, glob_prop->GetChildrenCount()); |
- |
- const v8::HeapGraphNode* a_from_glob_prop = GetChild( |
- glob_prop, |
- v8::HeapGraphNode::kObject, |
- "A"); |
- CHECK_NE(NULL, a_from_glob_prop); |
- CHECK_EQ(0, a_from_glob_prop->GetSelfSize()); |
- CHECK_EQ(0, a_from_glob_prop->GetInstancesCount()); |
- CHECK_EQ(0, a_from_glob_prop->GetChildrenCount()); // Retains nothing. |
- CHECK(IsNodeRetainedAs(a_from_glob_prop, 1)); // (global propery) has 1 ref. |
- |
- const v8::HeapGraphNode* b_with_children = GetChild( |
- snapshot->GetRoot(), |
- v8::HeapGraphNode::kObject, |
- "B", |
- b); |
- CHECK_NE(NULL, b_with_children); |
- CHECK_EQ(0, b_with_children->GetSelfSize()); |
- CHECK_EQ(0, b_with_children->GetInstancesCount()); |
- CHECK_NE(0, b_with_children->GetChildrenCount()); |
- |
- const v8::HeapGraphNode* a_from_b = GetChild( |
- b_with_children, |
- v8::HeapGraphNode::kObject, |
- "A"); |
- CHECK_NE(NULL, a_from_b); |
- CHECK_EQ(0, a_from_b->GetSelfSize()); |
- CHECK_EQ(0, a_from_b->GetInstancesCount()); |
- CHECK_EQ(0, a_from_b->GetChildrenCount()); // Retains nothing. |
- CHECK(IsNodeRetainedAs(a_from_b, 1)); // B has 1 ref to A. |
-} |
- |
- |
TEST(HeapEntryDominator) { |
// The graph looks like this: |
// |
@@ -1048,21 +567,6 @@ TEST(HeapSnapshotJSONSerializationAborting) { |
} |
-// Must not crash in debug mode. |
-TEST(AggregatedHeapSnapshotJSONSerialization) { |
- v8::HandleScope scope; |
- LocalContext env; |
- |
- const v8::HeapSnapshot* snapshot = |
- v8::HeapProfiler::TakeSnapshot( |
- v8::String::New("agg"), v8::HeapSnapshot::kAggregated); |
- TestJSONStream stream; |
- snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON); |
- CHECK_GT(stream.size(), 0); |
- CHECK_EQ(1, stream.eos_signaled()); |
-} |
- |
- |
TEST(HeapSnapshotGetNodeById) { |
v8::HandleScope scope; |
LocalContext env; |