Home | History | Annotate | Download | only in src
      1 // Copyright 2009 the V8 project authors. All rights reserved.
      2 // Redistribution and use in source and binary forms, with or without
      3 // modification, are permitted provided that the following conditions are
      4 // met:
      5 //
      6 //     * Redistributions of source code must retain the above copyright
      7 //       notice, this list of conditions and the following disclaimer.
      8 //     * Redistributions in binary form must reproduce the above
      9 //       copyright notice, this list of conditions and the following
     10 //       disclaimer in the documentation and/or other materials provided
     11 //       with the distribution.
     12 //     * Neither the name of Google Inc. nor the names of its
     13 //       contributors may be used to endorse or promote products derived
     14 //       from this software without specific prior written permission.
     15 //
     16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27 
     28 #include "v8.h"
     29 
     30 #include "heap-profiler.h"
     31 #include "frames-inl.h"
     32 #include "global-handles.h"
     33 #include "string-stream.h"
     34 
     35 namespace v8 {
     36 namespace internal {
     37 
     38 
     39 #ifdef ENABLE_LOGGING_AND_PROFILING
     40 namespace {
     41 
     42 // Clusterizer is a set of helper functions for converting
     43 // object references into clusters.
     44 class Clusterizer : public AllStatic {
     45  public:
     46   static JSObjectsCluster Clusterize(HeapObject* obj) {
     47     return Clusterize(obj, true);
     48   }
     49   static void InsertIntoTree(JSObjectsClusterTree* tree,
     50                              HeapObject* obj, bool fine_grain);
     51   static void InsertReferenceIntoTree(JSObjectsClusterTree* tree,
     52                                       const JSObjectsCluster& cluster) {
     53     InsertIntoTree(tree, cluster, 0);
     54   }
     55 
     56  private:
     57   static JSObjectsCluster Clusterize(HeapObject* obj, bool fine_grain);
     58   static int CalculateNetworkSize(JSObject* obj);
     59   static int GetObjectSize(HeapObject* obj) {
     60     return obj->IsJSObject() ?
     61         CalculateNetworkSize(JSObject::cast(obj)) : obj->Size();
     62   }
     63   static void InsertIntoTree(JSObjectsClusterTree* tree,
     64                              const JSObjectsCluster& cluster, int size);
     65 };
     66 
     67 
     68 JSObjectsCluster Clusterizer::Clusterize(HeapObject* obj, bool fine_grain) {
     69   if (obj->IsJSObject()) {
     70     JSObject* js_obj = JSObject::cast(obj);
     71     String* constructor = JSObject::cast(js_obj)->constructor_name();
     72     // Differentiate Object and Array instances.
     73     if (fine_grain && (constructor == Heap::Object_symbol() ||
     74                        constructor == Heap::Array_symbol())) {
     75       return JSObjectsCluster(constructor, obj);
     76     } else {
     77       return JSObjectsCluster(constructor);
     78     }
     79   } else if (obj->IsString()) {
     80     return JSObjectsCluster(Heap::String_symbol());
     81   } else if (obj->IsJSGlobalPropertyCell()) {
     82     return JSObjectsCluster(JSObjectsCluster::GLOBAL_PROPERTY);
     83   } else if (obj->IsCode() || obj->IsSharedFunctionInfo() || obj->IsScript()) {
     84     return JSObjectsCluster(JSObjectsCluster::CODE);
     85   }
     86   return JSObjectsCluster();
     87 }
     88 
     89 
     90 void Clusterizer::InsertIntoTree(JSObjectsClusterTree* tree,
     91                                  HeapObject* obj, bool fine_grain) {
     92   JSObjectsCluster cluster = Clusterize(obj, fine_grain);
     93   if (cluster.is_null()) return;
     94   InsertIntoTree(tree, cluster, GetObjectSize(obj));
     95 }
     96 
     97 
     98 void Clusterizer::InsertIntoTree(JSObjectsClusterTree* tree,
     99                                  const JSObjectsCluster& cluster, int size) {
    100   JSObjectsClusterTree::Locator loc;
    101   tree->Insert(cluster, &loc);
    102   NumberAndSizeInfo number_and_size = loc.value();
    103   number_and_size.increment_number(1);
    104   number_and_size.increment_bytes(size);
    105   loc.set_value(number_and_size);
    106 }
    107 
    108 
    109 int Clusterizer::CalculateNetworkSize(JSObject* obj) {
    110   int size = obj->Size();
    111   // If 'properties' and 'elements' are non-empty (thus, non-shared),
    112   // take their size into account.
    113   if (FixedArray::cast(obj->properties())->length() != 0) {
    114     size += obj->properties()->Size();
    115   }
    116   if (FixedArray::cast(obj->elements())->length() != 0) {
    117     size += obj->elements()->Size();
    118   }
    119   // For functions, also account non-empty context and literals sizes.
    120   if (obj->IsJSFunction()) {
    121     JSFunction* f = JSFunction::cast(obj);
    122     if (f->unchecked_context()->IsContext()) {
    123       size += f->context()->Size();
    124     }
    125     if (f->literals()->length() != 0) {
    126       size += f->literals()->Size();
    127     }
    128   }
    129   return size;
    130 }
    131 
    132 
    133 // A helper class for recording back references.
    134 class ReferencesExtractor : public ObjectVisitor {
    135  public:
    136   ReferencesExtractor(const JSObjectsCluster& cluster,
    137                       RetainerHeapProfile* profile)
    138       : cluster_(cluster),
    139         profile_(profile),
    140         inside_array_(false) {
    141   }
    142 
    143   void VisitPointer(Object** o) {
    144     if ((*o)->IsFixedArray() && !inside_array_) {
    145       // Traverse one level deep for data members that are fixed arrays.
    146       // This covers the case of 'elements' and 'properties' of JSObject,
    147       // and function contexts.
    148       inside_array_ = true;
    149       FixedArray::cast(*o)->Iterate(this);
    150       inside_array_ = false;
    151     } else if ((*o)->IsHeapObject()) {
    152       profile_->StoreReference(cluster_, HeapObject::cast(*o));
    153     }
    154   }
    155 
    156   void VisitPointers(Object** start, Object** end) {
    157     for (Object** p = start; p < end; p++) VisitPointer(p);
    158   }
    159 
    160  private:
    161   const JSObjectsCluster& cluster_;
    162   RetainerHeapProfile* profile_;
    163   bool inside_array_;
    164 };
    165 
    166 
    167 // A printer interface implementation for the Retainers profile.
    168 class RetainersPrinter : public RetainerHeapProfile::Printer {
    169  public:
    170   void PrintRetainers(const JSObjectsCluster& cluster,
    171                       const StringStream& retainers) {
    172     HeapStringAllocator allocator;
    173     StringStream stream(&allocator);
    174     cluster.Print(&stream);
    175     LOG(HeapSampleJSRetainersEvent(
    176         *(stream.ToCString()), *(retainers.ToCString())));
    177   }
    178 };
    179 
    180 
    181 // Visitor for printing a cluster tree.
    182 class ClusterTreePrinter BASE_EMBEDDED {
    183  public:
    184   explicit ClusterTreePrinter(StringStream* stream) : stream_(stream) {}
    185   void Call(const JSObjectsCluster& cluster,
    186             const NumberAndSizeInfo& number_and_size) {
    187     Print(stream_, cluster, number_and_size);
    188   }
    189   static void Print(StringStream* stream,
    190                     const JSObjectsCluster& cluster,
    191                     const NumberAndSizeInfo& number_and_size);
    192 
    193  private:
    194   StringStream* stream_;
    195 };
    196 
    197 
    198 void ClusterTreePrinter::Print(StringStream* stream,
    199                                const JSObjectsCluster& cluster,
    200                                const NumberAndSizeInfo& number_and_size) {
    201   stream->Put(',');
    202   cluster.Print(stream);
    203   stream->Add(";%d", number_and_size.number());
    204 }
    205 
    206 
    207 // Visitor for printing a retainer tree.
    208 class SimpleRetainerTreePrinter BASE_EMBEDDED {
    209  public:
    210   explicit SimpleRetainerTreePrinter(RetainerHeapProfile::Printer* printer)
    211       : printer_(printer) {}
    212   void Call(const JSObjectsCluster& cluster, JSObjectsClusterTree* tree);
    213 
    214  private:
    215   RetainerHeapProfile::Printer* printer_;
    216 };
    217 
    218 
    219 void SimpleRetainerTreePrinter::Call(const JSObjectsCluster& cluster,
    220                                      JSObjectsClusterTree* tree) {
    221   HeapStringAllocator allocator;
    222   StringStream stream(&allocator);
    223   ClusterTreePrinter retainers_printer(&stream);
    224   tree->ForEach(&retainers_printer);
    225   printer_->PrintRetainers(cluster, stream);
    226 }
    227 
    228 
    229 // Visitor for aggregating references count of equivalent clusters.
    230 class RetainersAggregator BASE_EMBEDDED {
    231  public:
    232   RetainersAggregator(ClustersCoarser* coarser, JSObjectsClusterTree* dest_tree)
    233       : coarser_(coarser), dest_tree_(dest_tree) {}
    234   void Call(const JSObjectsCluster& cluster,
    235             const NumberAndSizeInfo& number_and_size);
    236 
    237  private:
    238   ClustersCoarser* coarser_;
    239   JSObjectsClusterTree* dest_tree_;
    240 };
    241 
    242 
    243 void RetainersAggregator::Call(const JSObjectsCluster& cluster,
    244                                const NumberAndSizeInfo& number_and_size) {
    245   JSObjectsCluster eq = coarser_->GetCoarseEquivalent(cluster);
    246   if (eq.is_null()) eq = cluster;
    247   JSObjectsClusterTree::Locator loc;
    248   dest_tree_->Insert(eq, &loc);
    249   NumberAndSizeInfo aggregated_number = loc.value();
    250   aggregated_number.increment_number(number_and_size.number());
    251   loc.set_value(aggregated_number);
    252 }
    253 
    254 
    255 // Visitor for printing retainers tree. Aggregates equivalent retainer clusters.
    256 class AggregatingRetainerTreePrinter BASE_EMBEDDED {
    257  public:
    258   AggregatingRetainerTreePrinter(ClustersCoarser* coarser,
    259                                  RetainerHeapProfile::Printer* printer)
    260       : coarser_(coarser), printer_(printer) {}
    261   void Call(const JSObjectsCluster& cluster, JSObjectsClusterTree* tree);
    262 
    263  private:
    264   ClustersCoarser* coarser_;
    265   RetainerHeapProfile::Printer* printer_;
    266 };
    267 
    268 
    269 void AggregatingRetainerTreePrinter::Call(const JSObjectsCluster& cluster,
    270                                           JSObjectsClusterTree* tree) {
    271   if (!coarser_->GetCoarseEquivalent(cluster).is_null()) return;
    272   JSObjectsClusterTree dest_tree_;
    273   RetainersAggregator retainers_aggregator(coarser_, &dest_tree_);
    274   tree->ForEach(&retainers_aggregator);
    275   HeapStringAllocator allocator;
    276   StringStream stream(&allocator);
    277   ClusterTreePrinter retainers_printer(&stream);
    278   dest_tree_.ForEach(&retainers_printer);
    279   printer_->PrintRetainers(cluster, stream);
    280 }
    281 
    282 
    283 // A helper class for building a retainers tree, that aggregates
    284 // all equivalent clusters.
    285 class RetainerTreeAggregator BASE_EMBEDDED {
    286  public:
    287   explicit RetainerTreeAggregator(ClustersCoarser* coarser)
    288       : coarser_(coarser) {}
    289   void Process(JSObjectsRetainerTree* input_tree) {
    290     input_tree->ForEach(this);
    291   }
    292   void Call(const JSObjectsCluster& cluster, JSObjectsClusterTree* tree);
    293   JSObjectsRetainerTree& output_tree() { return output_tree_; }
    294 
    295  private:
    296   ClustersCoarser* coarser_;
    297   JSObjectsRetainerTree output_tree_;
    298 };
    299 
    300 
    301 void RetainerTreeAggregator::Call(const JSObjectsCluster& cluster,
    302                                   JSObjectsClusterTree* tree) {
    303   JSObjectsCluster eq = coarser_->GetCoarseEquivalent(cluster);
    304   if (eq.is_null()) return;
    305   JSObjectsRetainerTree::Locator loc;
    306   if (output_tree_.Insert(eq, &loc)) {
    307     loc.set_value(new JSObjectsClusterTree());
    308   }
    309   RetainersAggregator retainers_aggregator(coarser_, loc.value());
    310   tree->ForEach(&retainers_aggregator);
    311 }
    312 
    313 }  // namespace
    314 
    315 
    316 const JSObjectsClusterTreeConfig::Key JSObjectsClusterTreeConfig::kNoKey;
    317 const JSObjectsClusterTreeConfig::Value JSObjectsClusterTreeConfig::kNoValue;
    318 
    319 
    320 ConstructorHeapProfile::ConstructorHeapProfile()
    321     : zscope_(DELETE_ON_EXIT) {
    322 }
    323 
    324 
    325 void ConstructorHeapProfile::Call(const JSObjectsCluster& cluster,
    326                                   const NumberAndSizeInfo& number_and_size) {
    327   HeapStringAllocator allocator;
    328   StringStream stream(&allocator);
    329   cluster.Print(&stream);
    330   LOG(HeapSampleJSConstructorEvent(*(stream.ToCString()),
    331                                    number_and_size.number(),
    332                                    number_and_size.bytes()));
    333 }
    334 
    335 
    336 void ConstructorHeapProfile::CollectStats(HeapObject* obj) {
    337   Clusterizer::InsertIntoTree(&js_objects_info_tree_, obj, false);
    338 }
    339 
    340 
    341 void ConstructorHeapProfile::PrintStats() {
    342   js_objects_info_tree_.ForEach(this);
    343 }
    344 
    345 
    346 static const char* GetConstructorName(const char* name) {
    347   return name[0] != '\0' ? name : "(anonymous)";
    348 }
    349 
    350 
    351 void JSObjectsCluster::Print(StringStream* accumulator) const {
    352   ASSERT(!is_null());
    353   if (constructor_ == FromSpecialCase(ROOTS)) {
    354     accumulator->Add("(roots)");
    355   } else if (constructor_ == FromSpecialCase(GLOBAL_PROPERTY)) {
    356     accumulator->Add("(global property)");
    357   } else if (constructor_ == FromSpecialCase(CODE)) {
    358     accumulator->Add("(code)");
    359   } else if (constructor_ == FromSpecialCase(SELF)) {
    360     accumulator->Add("(self)");
    361   } else {
    362     SmartPointer<char> s_name(
    363         constructor_->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL));
    364     accumulator->Add("%s", GetConstructorName(*s_name));
    365     if (instance_ != NULL) {
    366       accumulator->Add(":%p", static_cast<void*>(instance_));
    367     }
    368   }
    369 }
    370 
    371 
    372 void JSObjectsCluster::DebugPrint(StringStream* accumulator) const {
    373   if (!is_null()) {
    374     Print(accumulator);
    375   } else {
    376     accumulator->Add("(null cluster)");
    377   }
    378 }
    379 
    380 
    381 inline ClustersCoarser::ClusterBackRefs::ClusterBackRefs(
    382     const JSObjectsCluster& cluster_)
    383     : cluster(cluster_), refs(kInitialBackrefsListCapacity) {
    384 }
    385 
    386 
    387 inline ClustersCoarser::ClusterBackRefs::ClusterBackRefs(
    388     const ClustersCoarser::ClusterBackRefs& src)
    389     : cluster(src.cluster), refs(src.refs.capacity()) {
    390   refs.AddAll(src.refs);
    391 }
    392 
    393 
    394 inline ClustersCoarser::ClusterBackRefs&
    395     ClustersCoarser::ClusterBackRefs::operator=(
    396     const ClustersCoarser::ClusterBackRefs& src) {
    397   if (this == &src) return *this;
    398   cluster = src.cluster;
    399   refs.Clear();
    400   refs.AddAll(src.refs);
    401   return *this;
    402 }
    403 
    404 
    405 inline int ClustersCoarser::ClusterBackRefs::Compare(
    406     const ClustersCoarser::ClusterBackRefs& a,
    407     const ClustersCoarser::ClusterBackRefs& b) {
    408   int cmp = JSObjectsCluster::CompareConstructors(a.cluster, b.cluster);
    409   if (cmp != 0) return cmp;
    410   if (a.refs.length() < b.refs.length()) return -1;
    411   if (a.refs.length() > b.refs.length()) return 1;
    412   for (int i = 0; i < a.refs.length(); ++i) {
    413     int cmp = JSObjectsCluster::Compare(a.refs[i], b.refs[i]);
    414     if (cmp != 0) return cmp;
    415   }
    416   return 0;
    417 }
    418 
    419 
    420 ClustersCoarser::ClustersCoarser()
    421     : zscope_(DELETE_ON_EXIT),
    422       sim_list_(ClustersCoarser::kInitialSimilarityListCapacity),
    423       current_pair_(NULL),
    424       current_set_(NULL),
    425       self_(NULL) {
    426 }
    427 
    428 
    429 void ClustersCoarser::Call(const JSObjectsCluster& cluster,
    430                            JSObjectsClusterTree* tree) {
    431   if (!cluster.can_be_coarsed()) return;
    432   ClusterBackRefs pair(cluster);
    433   ASSERT(current_pair_ == NULL);
    434   current_pair_ = &pair;
    435   current_set_ = new JSObjectsRetainerTree();
    436   self_ = &cluster;
    437   tree->ForEach(this);
    438   sim_list_.Add(pair);
    439   current_pair_ = NULL;
    440   current_set_ = NULL;
    441   self_ = NULL;
    442 }
    443 
    444 
    445 void ClustersCoarser::Call(const JSObjectsCluster& cluster,
    446                            const NumberAndSizeInfo& number_and_size) {
    447   ASSERT(current_pair_ != NULL);
    448   ASSERT(current_set_ != NULL);
    449   ASSERT(self_ != NULL);
    450   JSObjectsRetainerTree::Locator loc;
    451   if (JSObjectsCluster::Compare(*self_, cluster) == 0) {
    452     current_pair_->refs.Add(JSObjectsCluster(JSObjectsCluster::SELF));
    453     return;
    454   }
    455   JSObjectsCluster eq = GetCoarseEquivalent(cluster);
    456   if (!eq.is_null()) {
    457     if (current_set_->Find(eq, &loc)) return;
    458     current_pair_->refs.Add(eq);
    459     current_set_->Insert(eq, &loc);
    460   } else {
    461     current_pair_->refs.Add(cluster);
    462   }
    463 }
    464 
    465 
    466 void ClustersCoarser::Process(JSObjectsRetainerTree* tree) {
    467   int last_eq_clusters = -1;
    468   for (int i = 0; i < kMaxPassesCount; ++i) {
    469     sim_list_.Clear();
    470     const int curr_eq_clusters = DoProcess(tree);
    471     // If no new cluster equivalents discovered, abort processing.
    472     if (last_eq_clusters == curr_eq_clusters) break;
    473     last_eq_clusters = curr_eq_clusters;
    474   }
    475 }
    476 
    477 
    478 int ClustersCoarser::DoProcess(JSObjectsRetainerTree* tree) {
    479   tree->ForEach(this);
    480   sim_list_.Iterate(ClusterBackRefs::SortRefsIterator);
    481   sim_list_.Sort(ClusterBackRefsCmp);
    482   return FillEqualityTree();
    483 }
    484 
    485 
    486 JSObjectsCluster ClustersCoarser::GetCoarseEquivalent(
    487     const JSObjectsCluster& cluster) {
    488   if (!cluster.can_be_coarsed()) return JSObjectsCluster();
    489   EqualityTree::Locator loc;
    490   return eq_tree_.Find(cluster, &loc) ? loc.value() : JSObjectsCluster();
    491 }
    492 
    493 
    494 bool ClustersCoarser::HasAnEquivalent(const JSObjectsCluster& cluster) {
    495   // Return true for coarsible clusters that have a non-identical equivalent.
    496   if (!cluster.can_be_coarsed()) return false;
    497   JSObjectsCluster eq = GetCoarseEquivalent(cluster);
    498   return !eq.is_null() && JSObjectsCluster::Compare(cluster, eq) != 0;
    499 }
    500 
    501 
    502 int ClustersCoarser::FillEqualityTree() {
    503   int eq_clusters_count = 0;
    504   int eq_to = 0;
    505   bool first_added = false;
    506   for (int i = 1; i < sim_list_.length(); ++i) {
    507     if (ClusterBackRefs::Compare(sim_list_[i], sim_list_[eq_to]) == 0) {
    508       EqualityTree::Locator loc;
    509       if (!first_added) {
    510         // Add self-equivalence, if we have more than one item in this
    511         // equivalence class.
    512         eq_tree_.Insert(sim_list_[eq_to].cluster, &loc);
    513         loc.set_value(sim_list_[eq_to].cluster);
    514         first_added = true;
    515       }
    516       eq_tree_.Insert(sim_list_[i].cluster, &loc);
    517       loc.set_value(sim_list_[eq_to].cluster);
    518       ++eq_clusters_count;
    519     } else {
    520       eq_to = i;
    521       first_added = false;
    522     }
    523   }
    524   return eq_clusters_count;
    525 }
    526 
    527 
    528 const JSObjectsCluster ClustersCoarser::ClusterEqualityConfig::kNoKey;
    529 const JSObjectsCluster ClustersCoarser::ClusterEqualityConfig::kNoValue;
    530 const JSObjectsRetainerTreeConfig::Key JSObjectsRetainerTreeConfig::kNoKey;
    531 const JSObjectsRetainerTreeConfig::Value JSObjectsRetainerTreeConfig::kNoValue =
    532     NULL;
    533 
    534 
    535 RetainerHeapProfile::RetainerHeapProfile()
    536     : zscope_(DELETE_ON_EXIT) {
    537   JSObjectsCluster roots(JSObjectsCluster::ROOTS);
    538   ReferencesExtractor extractor(roots, this);
    539   Heap::IterateRoots(&extractor, VISIT_ONLY_STRONG);
    540 }
    541 
    542 
    543 void RetainerHeapProfile::StoreReference(const JSObjectsCluster& cluster,
    544                                          HeapObject* ref) {
    545   JSObjectsCluster ref_cluster = Clusterizer::Clusterize(ref);
    546   if (ref_cluster.is_null()) return;
    547   JSObjectsRetainerTree::Locator ref_loc;
    548   if (retainers_tree_.Insert(ref_cluster, &ref_loc)) {
    549     ref_loc.set_value(new JSObjectsClusterTree());
    550   }
    551   JSObjectsClusterTree* referenced_by = ref_loc.value();
    552   Clusterizer::InsertReferenceIntoTree(referenced_by, cluster);
    553 }
    554 
    555 
    556 void RetainerHeapProfile::CollectStats(HeapObject* obj) {
    557   const JSObjectsCluster cluster = Clusterizer::Clusterize(obj);
    558   if (cluster.is_null()) return;
    559   ReferencesExtractor extractor(cluster, this);
    560   obj->Iterate(&extractor);
    561 }
    562 
    563 
    564 void RetainerHeapProfile::DebugPrintStats(
    565     RetainerHeapProfile::Printer* printer) {
    566   coarser_.Process(&retainers_tree_);
    567   // Print clusters that have no equivalents, aggregating their retainers.
    568   AggregatingRetainerTreePrinter agg_printer(&coarser_, printer);
    569   retainers_tree_.ForEach(&agg_printer);
    570   // Now aggregate clusters that have equivalents...
    571   RetainerTreeAggregator aggregator(&coarser_);
    572   aggregator.Process(&retainers_tree_);
    573   // ...and print them.
    574   SimpleRetainerTreePrinter s_printer(printer);
    575   aggregator.output_tree().ForEach(&s_printer);
    576 }
    577 
    578 
    579 void RetainerHeapProfile::PrintStats() {
    580   RetainersPrinter printer;
    581   DebugPrintStats(&printer);
    582 }
    583 
    584 
    585 //
    586 // HeapProfiler class implementation.
    587 //
    588 void HeapProfiler::CollectStats(HeapObject* obj, HistogramInfo* info) {
    589   InstanceType type = obj->map()->instance_type();
    590   ASSERT(0 <= type && type <= LAST_TYPE);
    591   if (!FreeListNode::IsFreeListNode(obj)) {
    592     info[type].increment_number(1);
    593     info[type].increment_bytes(obj->Size());
    594   }
    595 }
    596 
    597 
    598 static void StackWeakReferenceCallback(Persistent<Value> object,
    599                                        void* trace) {
    600   DeleteArray(static_cast<Address*>(trace));
    601   object.Dispose();
    602 }
    603 
    604 
    605 static void PrintProducerStackTrace(Object* obj, void* trace) {
    606   if (!obj->IsJSObject()) return;
    607   String* constructor = JSObject::cast(obj)->constructor_name();
    608   SmartPointer<char> s_name(
    609       constructor->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL));
    610   LOG(HeapSampleJSProducerEvent(GetConstructorName(*s_name),
    611                                 reinterpret_cast<Address*>(trace)));
    612 }
    613 
    614 
    615 void HeapProfiler::WriteSample() {
    616   LOG(HeapSampleBeginEvent("Heap", "allocated"));
    617   LOG(HeapSampleStats(
    618       "Heap", "allocated", Heap::CommittedMemory(), Heap::SizeOfObjects()));
    619 
    620   HistogramInfo info[LAST_TYPE+1];
    621 #define DEF_TYPE_NAME(name) info[name].set_name(#name);
    622   INSTANCE_TYPE_LIST(DEF_TYPE_NAME)
    623 #undef DEF_TYPE_NAME
    624 
    625   ConstructorHeapProfile js_cons_profile;
    626   RetainerHeapProfile js_retainer_profile;
    627   HeapIterator iterator;
    628   for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
    629     CollectStats(obj, info);
    630     js_cons_profile.CollectStats(obj);
    631     js_retainer_profile.CollectStats(obj);
    632   }
    633 
    634   // Lump all the string types together.
    635   int string_number = 0;
    636   int string_bytes = 0;
    637 #define INCREMENT_SIZE(type, size, name, camel_name)   \
    638     string_number += info[type].number();              \
    639     string_bytes += info[type].bytes();
    640   STRING_TYPE_LIST(INCREMENT_SIZE)
    641 #undef INCREMENT_SIZE
    642   if (string_bytes > 0) {
    643     LOG(HeapSampleItemEvent("STRING_TYPE", string_number, string_bytes));
    644   }
    645 
    646   for (int i = FIRST_NONSTRING_TYPE; i <= LAST_TYPE; ++i) {
    647     if (info[i].bytes() > 0) {
    648       LOG(HeapSampleItemEvent(info[i].name(), info[i].number(),
    649                               info[i].bytes()));
    650     }
    651   }
    652 
    653   js_cons_profile.PrintStats();
    654   js_retainer_profile.PrintStats();
    655 
    656   GlobalHandles::IterateWeakRoots(PrintProducerStackTrace,
    657                                   StackWeakReferenceCallback);
    658 
    659   LOG(HeapSampleEndEvent("Heap", "allocated"));
    660 }
    661 
    662 
    663 bool ProducerHeapProfile::can_log_ = false;
    664 
    665 void ProducerHeapProfile::Setup() {
    666   can_log_ = true;
    667 }
    668 
    669 void ProducerHeapProfile::DoRecordJSObjectAllocation(Object* obj) {
    670   ASSERT(FLAG_log_producers);
    671   if (!can_log_) return;
    672   int framesCount = 0;
    673   for (JavaScriptFrameIterator it; !it.done(); it.Advance()) {
    674     ++framesCount;
    675   }
    676   if (framesCount == 0) return;
    677   ++framesCount;  // Reserve place for the terminator item.
    678   Vector<Address> stack(NewArray<Address>(framesCount), framesCount);
    679   int i = 0;
    680   for (JavaScriptFrameIterator it; !it.done(); it.Advance()) {
    681     stack[i++] = it.frame()->pc();
    682   }
    683   stack[i] = NULL;
    684   Handle<Object> handle = GlobalHandles::Create(obj);
    685   GlobalHandles::MakeWeak(handle.location(),
    686                           static_cast<void*>(stack.start()),
    687                           StackWeakReferenceCallback);
    688 }
    689 
    690 
    691 #endif  // ENABLE_LOGGING_AND_PROFILING
    692 
    693 
    694 } }  // namespace v8::internal
    695