diff options
Diffstat (limited to 'deps/v8/test/cctest/test-heap-profiler.cc')
-rw-r--r-- | deps/v8/test/cctest/test-heap-profiler.cc | 330 |
1 files changed, 330 insertions, 0 deletions
diff --git a/deps/v8/test/cctest/test-heap-profiler.cc b/deps/v8/test/cctest/test-heap-profiler.cc new file mode 100644 index 0000000000..f8e5a6b755 --- /dev/null +++ b/deps/v8/test/cctest/test-heap-profiler.cc @@ -0,0 +1,330 @@ +// 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::JSObjectsRetainerTree; +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(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; + 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( + 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 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); + + JSObjectsRetainerTree 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); + + JSObjectsRetainerTree 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); + + 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, 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 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; + v8::Handle<v8::Context> env = v8::Context::New(); + env->Enter(); + + CompileAndRunScript( + "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; + while (iterator.has_next()) { + i::HeapObject* obj = iterator.next(); + ret_profile.CollectStats(obj); + } + RetainerProfilePrinter printer; + ret_profile.DebugPrintStats(&printer); + CHECK_EQ("(global property);1,B;2,C;2", printer.GetRetainers("A")); + CHECK_EQ("(global property);2", printer.GetRetainers("B")); + CHECK_EQ("(global property);1", printer.GetRetainers("C")); +} + +#endif // ENABLE_LOGGING_AND_PROFILING |