Index: test/cctest/test-heap-profiler.cc |
diff --git a/test/cctest/test-heap-profiler.cc b/test/cctest/test-heap-profiler.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a34c6093afa1da74c383a44ba03158efdf5dee9a |
--- /dev/null |
+++ b/test/cctest/test-heap-profiler.cc |
@@ -0,0 +1,323 @@ |
+// Copyright 2009 the V8 project authors. All rights reserved. |
+// |
+// Tests for heap profiler |
+ |
+#ifdef ENABLE_LOGGING_AND_PROFILING |
+ |
+#include "v8.h" |
+#include "heap-profiler.h" |
+#include "string-stream.h" |
+#include "cctest.h" |
+ |
+namespace i = v8::internal; |
+using i::ClustersCoarser; |
+using i::JSObjectsCluster; |
+using i::JSObjectsClusterTree; |
+using i::RetainerHeapProfile; |
+ |
+ |
+static void CompileAndRunScript(const char *src) { |
+ v8::Script::Compile(v8::String::New(src))->Run(); |
+} |
+ |
+ |
+namespace { |
+ |
+class ConstructorHeapProfileTestHelper : public i::ConstructorHeapProfile { |
+ public: |
+ ConstructorHeapProfileTestHelper() |
+ : i::ConstructorHeapProfile(), |
+ f_name_(i::Factory::NewStringFromAscii(i::CStrVector("F"))), |
+ f_count_(0) { |
+ } |
+ |
+ void Call(i::String* name, const i::NumberAndSizeInfo& number_and_size) { |
+ CHECK(name != NULL); |
+ if (f_name_->Equals(name)) { |
+ 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; |
+ v8::Handle<v8::Context> env = v8::Context::New(); |
+ env->Enter(); |
+ |
+ CompileAndRunScript( |
+ "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; |
+ while (iterator.has_next()) { |
+ i::HeapObject* 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( |
+ JSObjectsClusterTree* 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 loc; |
+ if (ref1 != NULL) o_tree->Insert(*ref1, &loc); |
+ if (ref2 != NULL) o_tree->Insert(*ref2, &loc); |
+ if (ref3 != NULL) o_tree->Insert(*ref3, &loc); |
+ tree->Insert(o, &loc); |
+ loc.set_value(o_tree); |
+ return o; |
+} |
+ |
+ |
+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; |
+ v8::Handle<v8::Context> env = v8::Context::New(); |
+ env->Enter(); |
+ |
+ i::ZoneScope zn_scope(i::DELETE_ON_EXIT); |
+ |
+ JSObjectsClusterTree tree; |
+ JSObjectsCluster function(i::Heap::function_class_symbol()); |
+ JSObjectsCluster a(*i::Factory::NewStringFromAscii(i::CStrVector("A"))); |
+ JSObjectsCluster b(*i::Factory::NewStringFromAscii(i::CStrVector("B"))); |
+ |
+ // o1 <- Function |
+ JSObjectsCluster o1 = |
+ AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x100, &function); |
+ // o2 <- Function |
+ JSObjectsCluster o2 = |
+ AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x200, &function); |
+ // o3 <- A, B |
+ JSObjectsCluster o3 = |
+ AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x300, &a, &b); |
+ // o4 <- B, A |
+ JSObjectsCluster o4 = |
+ AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x400, &b, &a); |
+ // o5 <- A, B, Function |
+ JSObjectsCluster o5 = |
+ AddHeapObjectToTree(&tree, i::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; |
+ v8::Handle<v8::Context> env = v8::Context::New(); |
+ env->Enter(); |
+ |
+ i::ZoneScope zn_scope(i::DELETE_ON_EXIT); |
+ |
+ JSObjectsClusterTree tree; |
+ JSObjectsCluster function(i::Heap::function_class_symbol()); |
+ |
+ // o1 <- Function |
+ JSObjectsCluster o1 = |
+ AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x100, &function); |
+ // a1 <- Function |
+ JSObjectsCluster a1 = |
+ AddHeapObjectToTree(&tree, i::Heap::Array_symbol(), 0x1000, &function); |
+ // o2 <- Function |
+ JSObjectsCluster o2 = |
+ AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x200, &function); |
+ // a2 <- Function |
+ JSObjectsCluster a2 = |
+ AddHeapObjectToTree(&tree, i::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; |
+ v8::Handle<v8::Context> env = v8::Context::New(); |
+ env->Enter(); |
+ |
+ i::ZoneScope zn_scope(i::DELETE_ON_EXIT); |
+ |
+ JSObjectsClusterTree 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, i::Heap::Object_symbol(), 0x100); |
+ JSObjectsCluster o11 = |
+ AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x110, &o); |
+ JSObjectsCluster o12 = |
+ AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x120, &o); |
+ JSObjectsCluster o21 = |
+ AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x210, &o11); |
+ JSObjectsCluster o22 = |
+ AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x220, &o12); |
+ JSObjectsCluster p = |
+ AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x300, &o21); |
+ JSObjectsCluster q = |
+ AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x310, &o21, &o22); |
+ JSObjectsCluster r = |
+ AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x320, &o22); |
+ |
+ ClustersCoarser coarser; |
+ coarser.Process(&tree); |
+ |
+ CHECK_EQ(JSObjectsCluster(), coarser.GetCoarseEquivalent(o)); |
+ 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_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)); |
+} |
+ |
+ |
+namespace { |
+ |
+class RetainerProfilePrinter : public RetainerHeapProfile::Printer { |
+ public: |
+ RetainerProfilePrinter() : stream_(&allocator_), lines_(100) {} |
+ |
+ void PrintRetainers(const i::StringStream& retainers) { |
+ 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; |
+ v8::Handle<v8::Context> env = v8::Context::New(); |
+ env->Enter(); |
+ |
+ CompileAndRunScript( |
+ "function A() {}\n" |
+ "function B(x) { this.x = x; }\n" |
+ "var a = new A();\n" |
+ "var b = new B(a);\n"); |
+ |
+ RetainerHeapProfile ret_profile; |
+ i::AssertNoAllocation no_alloc; |
+ i::HeapIterator iterator; |
+ while (iterator.has_next()) { |
+ i::HeapObject* obj = iterator.next(); |
+ ret_profile.CollectStats(obj); |
+ } |
+ RetainerProfilePrinter printer; |
+ ret_profile.DebugPrintStats(&printer); |
+ CHECK_EQ("(global property),B", printer.GetRetainers("A")); |
+ CHECK_EQ("(global property)", printer.GetRetainers("B")); |
+} |
+ |
+#endif // ENABLE_LOGGING_AND_PROFILING |