Revision: 2997 Author: [email protected] Date: Wed Sep 30 07:20:18 2009 Log: Merge bleeding_edge revisions 2971, 2972 (heap profiler updates) to trunk.
My changes in Chromium DevTools are depend on these updates. Review URL: http://codereview.chromium.org/243045 http://code.google.com/p/v8/source/detail?r=2997 Modified: /trunk/src/heap-profiler.cc /trunk/src/heap-profiler.h /trunk/src/version.cc /trunk/test/cctest/test-heap-profiler.cc ======================================= --- /trunk/src/heap-profiler.cc Wed Sep 23 06:43:07 2009 +++ /trunk/src/heap-profiler.cc Wed Sep 30 07:20:18 2009 @@ -162,30 +162,137 @@ }; -class RetainerTreePrinter BASE_EMBEDDED { +// Visitor for printing a cluster tree. +class ClusterTreePrinter BASE_EMBEDDED { public: - explicit RetainerTreePrinter(StringStream* stream) : stream_(stream) {} + explicit ClusterTreePrinter(StringStream* stream) : stream_(stream) {} void Call(const JSObjectsCluster& cluster, const NumberAndSizeInfo& number_and_size) { Print(stream_, cluster, number_and_size); } static void Print(StringStream* stream, const JSObjectsCluster& cluster, - const NumberAndSizeInfo& numNNber_and_size); + const NumberAndSizeInfo& number_and_size); private: StringStream* stream_; }; -void RetainerTreePrinter::Print(StringStream* stream, - const JSObjectsCluster& cluster, - const NumberAndSizeInfo& number_and_size) { +void ClusterTreePrinter::Print(StringStream* stream, + const JSObjectsCluster& cluster, + const NumberAndSizeInfo& number_and_size) { stream->Put(','); cluster.Print(stream); stream->Add(";%d", number_and_size.number()); } + +// Visitor for printing a retainer tree. +class SimpleRetainerTreePrinter BASE_EMBEDDED { + public: + explicit SimpleRetainerTreePrinter(RetainerHeapProfile::Printer* printer) + : printer_(printer) {} + void Call(const JSObjectsCluster& cluster, JSObjectsClusterTree* tree); + + private: + RetainerHeapProfile::Printer* printer_; +}; + + +void SimpleRetainerTreePrinter::Call(const JSObjectsCluster& cluster, + JSObjectsClusterTree* tree) { + HeapStringAllocator allocator; + StringStream stream(&allocator); + ClusterTreePrinter retainers_printer(&stream); + tree->ForEach(&retainers_printer); + printer_->PrintRetainers(cluster, stream); +} + + +// Visitor for aggregating references count of equivalent clusters. +class RetainersAggregator BASE_EMBEDDED { + public: + RetainersAggregator(ClustersCoarser* coarser, JSObjectsClusterTree* dest_tree) + : coarser_(coarser), dest_tree_(dest_tree) {} + void Call(const JSObjectsCluster& cluster, + const NumberAndSizeInfo& number_and_size); + + private: + ClustersCoarser* coarser_; + JSObjectsClusterTree* dest_tree_; +}; + + +void RetainersAggregator::Call(const JSObjectsCluster& cluster, + const NumberAndSizeInfo& number_and_size) { + JSObjectsCluster eq = coarser_->GetCoarseEquivalent(cluster); + if (eq.is_null()) eq = cluster; + JSObjectsClusterTree::Locator loc; + dest_tree_->Insert(eq, &loc); + NumberAndSizeInfo aggregated_number = loc.value(); + aggregated_number.increment_number(number_and_size.number()); + loc.set_value(aggregated_number); +} + + +// Visitor for printing retainers tree. Aggregates equivalent retainer clusters. +class AggregatingRetainerTreePrinter BASE_EMBEDDED { + public: + AggregatingRetainerTreePrinter(ClustersCoarser* coarser, + RetainerHeapProfile::Printer* printer) + : coarser_(coarser), printer_(printer) {} + void Call(const JSObjectsCluster& cluster, JSObjectsClusterTree* tree); + + private: + ClustersCoarser* coarser_; + RetainerHeapProfile::Printer* printer_; +}; + + +void AggregatingRetainerTreePrinter::Call(const JSObjectsCluster& cluster, + JSObjectsClusterTree* tree) { + if (!coarser_->GetCoarseEquivalent(cluster).is_null()) return; + JSObjectsClusterTree dest_tree_; + RetainersAggregator retainers_aggregator(coarser_, &dest_tree_); + tree->ForEach(&retainers_aggregator); + HeapStringAllocator allocator; + StringStream stream(&allocator); + ClusterTreePrinter retainers_printer(&stream); + dest_tree_.ForEach(&retainers_printer); + printer_->PrintRetainers(cluster, stream); +} + + +// A helper class for building a retainers tree, that aggregates +// all equivalent clusters. +class RetainerTreeAggregator BASE_EMBEDDED { + public: + explicit RetainerTreeAggregator(ClustersCoarser* coarser) + : coarser_(coarser) {} + void Process(JSObjectsRetainerTree* input_tree) { + input_tree->ForEach(this); + } + void Call(const JSObjectsCluster& cluster, JSObjectsClusterTree* tree); + JSObjectsRetainerTree& output_tree() { return output_tree_; } + + private: + ClustersCoarser* coarser_; + JSObjectsRetainerTree output_tree_; +}; + + +void RetainerTreeAggregator::Call(const JSObjectsCluster& cluster, + JSObjectsClusterTree* tree) { + JSObjectsCluster eq = coarser_->GetCoarseEquivalent(cluster); + if (eq.is_null()) return; + JSObjectsRetainerTree::Locator loc; + if (output_tree_.Insert(eq, &loc)) { + loc.set_value(new JSObjectsClusterTree()); + } + RetainersAggregator retainers_aggregator(coarser_, loc.value()); + tree->ForEach(&retainers_aggregator); +} } // namespace @@ -226,6 +333,8 @@ accumulator->Add("(roots)"); } else if (constructor_ == FromSpecialCase(GLOBAL_PROPERTY)) { accumulator->Add("(global property)"); + } else if (constructor_ == FromSpecialCase(SELF)) { + accumulator->Add("(self)"); } else { SmartPointer<char> s_name( constructor_->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL)); @@ -286,9 +395,11 @@ ClustersCoarser::ClustersCoarser() - : zscope_(DELETE_ON_EXIT), - sim_list_(ClustersCoarser::kInitialSimilarityListCapacity), - current_pair_(NULL) { + : zscope_(DELETE_ON_EXIT), + sim_list_(ClustersCoarser::kInitialSimilarityListCapacity), + current_pair_(NULL), + current_set_(NULL), + self_(NULL) { } @@ -299,10 +410,12 @@ ASSERT(current_pair_ == NULL); current_pair_ = &pair; current_set_ = new JSObjectsRetainerTree(); + self_ = &cluster; tree->ForEach(this); sim_list_.Add(pair); current_pair_ = NULL; current_set_ = NULL; + self_ = NULL; } @@ -310,8 +423,13 @@ const NumberAndSizeInfo& number_and_size) { ASSERT(current_pair_ != NULL); ASSERT(current_set_ != NULL); - JSObjectsCluster eq = GetCoarseEquivalent(cluster); + ASSERT(self_ != NULL); JSObjectsRetainerTree::Locator loc; + if (JSObjectsCluster::Compare(*self_, cluster) == 0) { + current_pair_->refs.Add(JSObjectsCluster(JSObjectsCluster::SELF)); + return; + } + JSObjectsCluster eq = GetCoarseEquivalent(cluster); if (!eq.is_null()) { if (current_set_->Find(eq, &loc)) return; current_pair_->refs.Add(eq); @@ -336,11 +454,7 @@ int ClustersCoarser::DoProcess(JSObjectsRetainerTree* tree) { tree->ForEach(this); - // To sort similarity list properly, references list of a cluster is - // required to be sorted, thus 'O1 <- A, B' and 'O2 <- B, A' would - // be considered equivalent. But we don't sort them explicitly - // because we know that they come from a splay tree traversal, so - // they are already sorted. + sim_list_.Iterate(ClusterBackRefs::SortRefsIterator); sim_list_.Sort(ClusterBackRefsCmp); return FillEqualityTree(); } @@ -356,8 +470,9 @@ bool ClustersCoarser::HasAnEquivalent(const JSObjectsCluster& cluster) { // Return true for coarsible clusters that have a non-identical equivalent. - return cluster.can_be_coarsed() && - JSObjectsCluster::Compare(cluster, GetCoarseEquivalent(cluster)) != 0; + if (!cluster.can_be_coarsed()) return false; + JSObjectsCluster eq = GetCoarseEquivalent(cluster); + return !eq.is_null() && JSObjectsCluster::Compare(cluster, eq) != 0; } @@ -395,10 +510,7 @@ RetainerHeapProfile::RetainerHeapProfile() - : zscope_(DELETE_ON_EXIT), - coarse_cluster_tree_(NULL), - current_printer_(NULL), - current_stream_(NULL) { + : zscope_(DELETE_ON_EXIT) { JSObjectsCluster roots(JSObjectsCluster::ROOTS); ReferencesExtractor extractor(roots, this); Heap::IterateRoots(&extractor); @@ -433,10 +545,15 @@ void RetainerHeapProfile::DebugPrintStats( RetainerHeapProfile::Printer* printer) { coarser_.Process(&retainers_tree_); - ASSERT(current_printer_ == NULL); - current_printer_ = printer; - retainers_tree_.ForEach(this); - current_printer_ = NULL; + // Print clusters that have no equivalents, aggregating their retainers. + AggregatingRetainerTreePrinter agg_printer(&coarser_, printer); + retainers_tree_.ForEach(&agg_printer); + // Now aggregate clusters that have equivalents... + RetainerTreeAggregator aggregator(&coarser_); + aggregator.Process(&retainers_tree_); + // ...and print them. + SimpleRetainerTreePrinter s_printer(printer); + aggregator.output_tree().ForEach(&s_printer); } @@ -444,44 +561,6 @@ RetainersPrinter printer; DebugPrintStats(&printer); } - - -void RetainerHeapProfile::Call(const JSObjectsCluster& cluster, - JSObjectsClusterTree* tree) { - // First level of retainer graph. - if (coarser_.HasAnEquivalent(cluster)) return; - ASSERT(current_stream_ == NULL); - HeapStringAllocator allocator; - StringStream stream(&allocator); - current_stream_ = &stream; - ASSERT(coarse_cluster_tree_ == NULL); - coarse_cluster_tree_ = new JSObjectsClusterTree(); - tree->ForEach(this); - // Print aggregated counts and sizes. - RetainerTreePrinter printer(current_stream_); - coarse_cluster_tree_->ForEach(&printer); - coarse_cluster_tree_ = NULL; - current_printer_->PrintRetainers(cluster, stream); - current_stream_ = NULL; -} - - -void RetainerHeapProfile::Call(const JSObjectsCluster& cluster, - const NumberAndSizeInfo& number_and_size) { - ASSERT(coarse_cluster_tree_ != NULL); - ASSERT(current_stream_ != NULL); - JSObjectsCluster eq = coarser_.GetCoarseEquivalent(cluster); - if (eq.is_null()) { - RetainerTreePrinter::Print(current_stream_, cluster, number_and_size); - } else { - // Aggregate counts and sizes for equivalent clusters. - JSObjectsClusterTree::Locator loc; - coarse_cluster_tree_->Insert(eq, &loc); - NumberAndSizeInfo eq_number_and_size = loc.value(); - eq_number_and_size.increment_number(number_and_size.number()); - loc.set_value(eq_number_and_size); - } -} // ======================================= --- /trunk/src/heap-profiler.h Tue Sep 22 03:00:30 2009 +++ /trunk/src/heap-profiler.h Wed Sep 30 07:20:18 2009 @@ -53,7 +53,8 @@ // These special cases are used in retainer profile. enum SpecialCase { ROOTS = 1, - GLOBAL_PROPERTY = 2 + GLOBAL_PROPERTY = 2, + SELF = 3 // This case is used in ClustersCoarser only. }; JSObjectsCluster() : constructor_(NULL), instance_(NULL) {} @@ -77,6 +78,9 @@ (a.instance_ == b.instance_ ? 0 : (a.instance_ < b.instance_ ? -1 : 1)) : cons_cmp; } + static int Compare(const JSObjectsCluster* a, const JSObjectsCluster* b) { + return Compare(*a, *b); + } bool is_null() const { return constructor_ == NULL; } bool can_be_coarsed() const { return instance_ != NULL; } @@ -93,6 +97,7 @@ switch (special) { case ROOTS: return Heap::result_symbol(); case GLOBAL_PROPERTY: return Heap::code_symbol(); + case SELF: return Heap::catch_var_symbol(); default: UNREACHABLE(); return NULL; @@ -183,6 +188,8 @@ ClusterBackRefs& operator=(const ClusterBackRefs& src); static int Compare(const ClusterBackRefs& a, const ClusterBackRefs& b); + void SortRefs() { refs.Sort(JSObjectsCluster::Compare); } + static void SortRefsIterator(ClusterBackRefs* ref) { ref->SortRefs(); } JSObjectsCluster cluster; ZoneList<JSObjectsCluster> refs; @@ -219,6 +226,7 @@ EqualityTree eq_tree_; ClusterBackRefs* current_pair_; JSObjectsRetainerTree* current_set_; + const JSObjectsCluster* self_; }; @@ -242,20 +250,9 @@ void StoreReference(const JSObjectsCluster& cluster, HeapObject* ref); private: - // Limit on the number of retainers to be printed per cluster. - static const int kMaxRetainersToPrint = 50; ZoneScope zscope_; JSObjectsRetainerTree retainers_tree_; ClustersCoarser coarser_; - // TODO(mnaganov): Use some helper class to hold these state variables. - JSObjectsClusterTree* coarse_cluster_tree_; - Printer* current_printer_; - StringStream* current_stream_; - public: - // Used by JSObjectsRetainerTree::ForEach. - void Call(const JSObjectsCluster& cluster, JSObjectsClusterTree* tree); - void Call(const JSObjectsCluster& cluster, - const NumberAndSizeInfo& number_and_size); }; ======================================= --- /trunk/src/version.cc Wed Sep 30 01:27:10 2009 +++ /trunk/src/version.cc Wed Sep 30 07:20:18 2009 @@ -35,7 +35,7 @@ #define MAJOR_VERSION 1 #define MINOR_VERSION 3 #define BUILD_NUMBER 13 -#define PATCH_LEVEL 4 +#define PATCH_LEVEL 5 #define CANDIDATE_VERSION false // Define SONAME to have the SCons build the put a specific SONAME into the ======================================= --- /trunk/test/cctest/test-heap-profiler.cc Tue Sep 22 03:00:30 2009 +++ /trunk/test/cctest/test-heap-profiler.cc Wed Sep 30 07:20:18 2009 @@ -74,13 +74,12 @@ } -static JSObjectsCluster AddHeapObjectToTree( - JSObjectsRetainerTree* tree, - i::String* constructor, - int instance, - JSObjectsCluster* ref1 = NULL, - JSObjectsCluster* ref2 = NULL, - JSObjectsCluster* ref3 = NULL) { +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; @@ -92,6 +91,16 @@ 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, @@ -121,7 +130,7 @@ if (JSObjectsCluster::Compare(expected, value) == 0) { i::HeapStringAllocator allocator; i::StringStream stream(&allocator); - stream.Add("# Expected: "); + stream.Add("# !Expected: "); expected.DebugPrint(&stream); stream.Add("\n# Found: "); value.DebugPrint(&stream); @@ -243,14 +252,64 @@ 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; + 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 (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, i::Heap::Object_symbol(), 0x100); + JSObjectsCluster o1 = + AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x110, &o); + JSObjectsCluster o2 = + AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x120, &o); + JSObjectsCluster p = + AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x300, &o1); + AddSelfReferenceToTree(&tree, &p); + JSObjectsCluster q = + AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x310, &o1, &o2); + AddSelfReferenceToTree(&tree, &q); + JSObjectsCluster r = + AddHeapObjectToTree(&tree, i::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 { @@ -322,7 +381,14 @@ } RetainerProfilePrinter printer; ret_profile.DebugPrintStats(&printer); - CHECK_EQ("(global property);1,B;2,C;2", printer.GetRetainers("A")); + 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(static_cast<int>(strlen("(global property);1,B;2,C;2")), + static_cast<int>(strlen(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")); } --~--~---------~--~----~------------~-------~--~----~ v8-dev mailing list [email protected] http://groups.google.com/group/v8-dev -~----------~----~----~----~------~----~------~--~---
