Home | History | Annotate | Download | only in src
      1 // Copyright 2009-2010 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 "profile-generator.h"
     34 #include "string-stream.h"
     35 
     36 namespace v8 {
     37 namespace internal {
     38 
     39 
     40 #ifdef ENABLE_LOGGING_AND_PROFILING
     41 namespace {
     42 
     43 // Clusterizer is a set of helper functions for converting
     44 // object references into clusters.
     45 class Clusterizer : public AllStatic {
     46  public:
     47   static JSObjectsCluster Clusterize(HeapObject* obj) {
     48     return Clusterize(obj, true);
     49   }
     50   static void InsertIntoTree(JSObjectsClusterTree* tree,
     51                              HeapObject* obj, bool fine_grain);
     52   static void InsertReferenceIntoTree(JSObjectsClusterTree* tree,
     53                                       const JSObjectsCluster& cluster) {
     54     InsertIntoTree(tree, cluster, 0);
     55   }
     56 
     57  private:
     58   static JSObjectsCluster Clusterize(HeapObject* obj, bool fine_grain);
     59   static int CalculateNetworkSize(JSObject* obj);
     60   static int GetObjectSize(HeapObject* obj) {
     61     return obj->IsJSObject() ?
     62         CalculateNetworkSize(JSObject::cast(obj)) : obj->Size();
     63   }
     64   static void InsertIntoTree(JSObjectsClusterTree* tree,
     65                              const JSObjectsCluster& cluster, int size);
     66 };
     67 
     68 
     69 JSObjectsCluster Clusterizer::Clusterize(HeapObject* obj, bool fine_grain) {
     70   if (obj->IsJSObject()) {
     71     JSObject* js_obj = JSObject::cast(obj);
     72     String* constructor = GetConstructorNameForHeapProfile(
     73         JSObject::cast(js_obj));
     74     // Differentiate Object and Array instances.
     75     if (fine_grain && (constructor == HEAP->Object_symbol() ||
     76                        constructor == HEAP->Array_symbol())) {
     77       return JSObjectsCluster(constructor, obj);
     78     } else {
     79       return JSObjectsCluster(constructor);
     80     }
     81   } else if (obj->IsString()) {
     82     return JSObjectsCluster(HEAP->String_symbol());
     83   } else if (obj->IsJSGlobalPropertyCell()) {
     84     return JSObjectsCluster(JSObjectsCluster::GLOBAL_PROPERTY);
     85   } else if (obj->IsCode() || obj->IsSharedFunctionInfo() || obj->IsScript()) {
     86     return JSObjectsCluster(JSObjectsCluster::CODE);
     87   }
     88   return JSObjectsCluster();
     89 }
     90 
     91 
     92 void Clusterizer::InsertIntoTree(JSObjectsClusterTree* tree,
     93                                  HeapObject* obj, bool fine_grain) {
     94   JSObjectsCluster cluster = Clusterize(obj, fine_grain);
     95   if (cluster.is_null()) return;
     96   InsertIntoTree(tree, cluster, GetObjectSize(obj));
     97 }
     98 
     99 
    100 void Clusterizer::InsertIntoTree(JSObjectsClusterTree* tree,
    101                                  const JSObjectsCluster& cluster, int size) {
    102   JSObjectsClusterTree::Locator loc;
    103   tree->Insert(cluster, &loc);
    104   NumberAndSizeInfo number_and_size = loc.value();
    105   number_and_size.increment_number(1);
    106   number_and_size.increment_bytes(size);
    107   loc.set_value(number_and_size);
    108 }
    109 
    110 
    111 int Clusterizer::CalculateNetworkSize(JSObject* obj) {
    112   int size = obj->Size();
    113   // If 'properties' and 'elements' are non-empty (thus, non-shared),
    114   // take their size into account.
    115   if (obj->properties() != HEAP->empty_fixed_array()) {
    116     size += obj->properties()->Size();
    117   }
    118   if (obj->elements() != HEAP->empty_fixed_array()) {
    119     size += obj->elements()->Size();
    120   }
    121   // For functions, also account non-empty context and literals sizes.
    122   if (obj->IsJSFunction()) {
    123     JSFunction* f = JSFunction::cast(obj);
    124     if (f->unchecked_context()->IsContext()) {
    125       size += f->context()->Size();
    126     }
    127     if (f->literals()->length() != 0) {
    128       size += f->literals()->Size();
    129     }
    130   }
    131   return size;
    132 }
    133 
    134 
    135 // A helper class for recording back references.
    136 class ReferencesExtractor : public ObjectVisitor {
    137  public:
    138   ReferencesExtractor(const JSObjectsCluster& cluster,
    139                       RetainerHeapProfile* profile)
    140       : cluster_(cluster),
    141         profile_(profile),
    142         inside_array_(false) {
    143   }
    144 
    145   void VisitPointer(Object** o) {
    146     if ((*o)->IsFixedArray() && !inside_array_) {
    147       // Traverse one level deep for data members that are fixed arrays.
    148       // This covers the case of 'elements' and 'properties' of JSObject,
    149       // and function contexts.
    150       inside_array_ = true;
    151       FixedArray::cast(*o)->Iterate(this);
    152       inside_array_ = false;
    153     } else if ((*o)->IsHeapObject()) {
    154       profile_->StoreReference(cluster_, HeapObject::cast(*o));
    155     }
    156   }
    157 
    158   void VisitPointers(Object** start, Object** end) {
    159     for (Object** p = start; p < end; p++) VisitPointer(p);
    160   }
    161 
    162  private:
    163   const JSObjectsCluster& cluster_;
    164   RetainerHeapProfile* profile_;
    165   bool inside_array_;
    166 };
    167 
    168 
    169 // A printer interface implementation for the Retainers profile.
    170 class RetainersPrinter : public RetainerHeapProfile::Printer {
    171  public:
    172   void PrintRetainers(const JSObjectsCluster& cluster,
    173                       const StringStream& retainers) {
    174     HeapStringAllocator allocator;
    175     StringStream stream(&allocator);
    176     cluster.Print(&stream);
    177     LOG(ISOLATE,
    178         HeapSampleJSRetainersEvent(
    179         *(stream.ToCString()), *(retainers.ToCString())));
    180   }
    181 };
    182 
    183 
    184 // Visitor for printing a cluster tree.
    185 class ClusterTreePrinter BASE_EMBEDDED {
    186  public:
    187   explicit ClusterTreePrinter(StringStream* stream) : stream_(stream) {}
    188   void Call(const JSObjectsCluster& cluster,
    189             const NumberAndSizeInfo& number_and_size) {
    190     Print(stream_, cluster, number_and_size);
    191   }
    192   static void Print(StringStream* stream,
    193                     const JSObjectsCluster& cluster,
    194                     const NumberAndSizeInfo& number_and_size);
    195 
    196  private:
    197   StringStream* stream_;
    198 };
    199 
    200 
    201 void ClusterTreePrinter::Print(StringStream* stream,
    202                                const JSObjectsCluster& cluster,
    203                                const NumberAndSizeInfo& number_and_size) {
    204   stream->Put(',');
    205   cluster.Print(stream);
    206   stream->Add(";%d", number_and_size.number());
    207 }
    208 
    209 
    210 // Visitor for printing a retainer tree.
    211 class SimpleRetainerTreePrinter BASE_EMBEDDED {
    212  public:
    213   explicit SimpleRetainerTreePrinter(RetainerHeapProfile::Printer* printer)
    214       : printer_(printer) {}
    215   void Call(const JSObjectsCluster& cluster, JSObjectsClusterTree* tree);
    216 
    217  private:
    218   RetainerHeapProfile::Printer* printer_;
    219 };
    220 
    221 
    222 void SimpleRetainerTreePrinter::Call(const JSObjectsCluster& cluster,
    223                                      JSObjectsClusterTree* tree) {
    224   HeapStringAllocator allocator;
    225   StringStream stream(&allocator);
    226   ClusterTreePrinter retainers_printer(&stream);
    227   tree->ForEach(&retainers_printer);
    228   printer_->PrintRetainers(cluster, stream);
    229 }
    230 
    231 
    232 // Visitor for aggregating references count of equivalent clusters.
    233 class RetainersAggregator BASE_EMBEDDED {
    234  public:
    235   RetainersAggregator(ClustersCoarser* coarser, JSObjectsClusterTree* dest_tree)
    236       : coarser_(coarser), dest_tree_(dest_tree) {}
    237   void Call(const JSObjectsCluster& cluster,
    238             const NumberAndSizeInfo& number_and_size);
    239 
    240  private:
    241   ClustersCoarser* coarser_;
    242   JSObjectsClusterTree* dest_tree_;
    243 };
    244 
    245 
    246 void RetainersAggregator::Call(const JSObjectsCluster& cluster,
    247                                const NumberAndSizeInfo& number_and_size) {
    248   JSObjectsCluster eq = coarser_->GetCoarseEquivalent(cluster);
    249   if (eq.is_null()) eq = cluster;
    250   JSObjectsClusterTree::Locator loc;
    251   dest_tree_->Insert(eq, &loc);
    252   NumberAndSizeInfo aggregated_number = loc.value();
    253   aggregated_number.increment_number(number_and_size.number());
    254   loc.set_value(aggregated_number);
    255 }
    256 
    257 
    258 // Visitor for printing retainers tree. Aggregates equivalent retainer clusters.
    259 class AggregatingRetainerTreePrinter BASE_EMBEDDED {
    260  public:
    261   AggregatingRetainerTreePrinter(ClustersCoarser* coarser,
    262                                  RetainerHeapProfile::Printer* printer)
    263       : coarser_(coarser), printer_(printer) {}
    264   void Call(const JSObjectsCluster& cluster, JSObjectsClusterTree* tree);
    265 
    266  private:
    267   ClustersCoarser* coarser_;
    268   RetainerHeapProfile::Printer* printer_;
    269 };
    270 
    271 
    272 void AggregatingRetainerTreePrinter::Call(const JSObjectsCluster& cluster,
    273                                           JSObjectsClusterTree* tree) {
    274   if (!coarser_->GetCoarseEquivalent(cluster).is_null()) return;
    275   JSObjectsClusterTree dest_tree_;
    276   RetainersAggregator retainers_aggregator(coarser_, &dest_tree_);
    277   tree->ForEach(&retainers_aggregator);
    278   HeapStringAllocator allocator;
    279   StringStream stream(&allocator);
    280   ClusterTreePrinter retainers_printer(&stream);
    281   dest_tree_.ForEach(&retainers_printer);
    282   printer_->PrintRetainers(cluster, stream);
    283 }
    284 
    285 }  // namespace
    286 
    287 
    288 // A helper class for building a retainers tree, that aggregates
    289 // all equivalent clusters.
    290 class RetainerTreeAggregator {
    291  public:
    292   explicit RetainerTreeAggregator(ClustersCoarser* coarser)
    293       : coarser_(coarser) {}
    294   void Process(JSObjectsRetainerTree* input_tree) {
    295     input_tree->ForEach(this);
    296   }
    297   void Call(const JSObjectsCluster& cluster, JSObjectsClusterTree* tree);
    298   JSObjectsRetainerTree& output_tree() { return output_tree_; }
    299 
    300  private:
    301   ClustersCoarser* coarser_;
    302   JSObjectsRetainerTree output_tree_;
    303 };
    304 
    305 
    306 void RetainerTreeAggregator::Call(const JSObjectsCluster& cluster,
    307                                   JSObjectsClusterTree* tree) {
    308   JSObjectsCluster eq = coarser_->GetCoarseEquivalent(cluster);
    309   if (eq.is_null()) return;
    310   JSObjectsRetainerTree::Locator loc;
    311   if (output_tree_.Insert(eq, &loc)) {
    312     loc.set_value(new JSObjectsClusterTree());
    313   }
    314   RetainersAggregator retainers_aggregator(coarser_, loc.value());
    315   tree->ForEach(&retainers_aggregator);
    316 }
    317 
    318 
    319 HeapProfiler::HeapProfiler()
    320     : snapshots_(new HeapSnapshotsCollection()),
    321       next_snapshot_uid_(1) {
    322 }
    323 
    324 
    325 HeapProfiler::~HeapProfiler() {
    326   delete snapshots_;
    327 }
    328 
    329 
    330 void HeapProfiler::ResetSnapshots() {
    331   delete snapshots_;
    332   snapshots_ = new HeapSnapshotsCollection();
    333 }
    334 
    335 
    336 #endif  // ENABLE_LOGGING_AND_PROFILING
    337 
    338 void HeapProfiler::Setup() {
    339 #ifdef ENABLE_LOGGING_AND_PROFILING
    340   Isolate* isolate = Isolate::Current();
    341   if (isolate->heap_profiler() == NULL) {
    342     isolate->set_heap_profiler(new HeapProfiler());
    343   }
    344 #endif
    345 }
    346 
    347 
    348 void HeapProfiler::TearDown() {
    349 #ifdef ENABLE_LOGGING_AND_PROFILING
    350   Isolate* isolate = Isolate::Current();
    351   delete isolate->heap_profiler();
    352   isolate->set_heap_profiler(NULL);
    353 #endif
    354 }
    355 
    356 
    357 #ifdef ENABLE_LOGGING_AND_PROFILING
    358 
    359 HeapSnapshot* HeapProfiler::TakeSnapshot(const char* name,
    360                                          int type,
    361                                          v8::ActivityControl* control) {
    362   ASSERT(Isolate::Current()->heap_profiler() != NULL);
    363   return Isolate::Current()->heap_profiler()->TakeSnapshotImpl(name,
    364                                                                type,
    365                                                                control);
    366 }
    367 
    368 
    369 HeapSnapshot* HeapProfiler::TakeSnapshot(String* name,
    370                                          int type,
    371                                          v8::ActivityControl* control) {
    372   ASSERT(Isolate::Current()->heap_profiler() != NULL);
    373   return Isolate::Current()->heap_profiler()->TakeSnapshotImpl(name,
    374                                                                type,
    375                                                                control);
    376 }
    377 
    378 
    379 void HeapProfiler::DefineWrapperClass(
    380     uint16_t class_id, v8::HeapProfiler::WrapperInfoCallback callback) {
    381   ASSERT(class_id != v8::HeapProfiler::kPersistentHandleNoClassId);
    382   if (wrapper_callbacks_.length() <= class_id) {
    383     wrapper_callbacks_.AddBlock(
    384         NULL, class_id - wrapper_callbacks_.length() + 1);
    385   }
    386   wrapper_callbacks_[class_id] = callback;
    387 }
    388 
    389 
    390 v8::RetainedObjectInfo* HeapProfiler::ExecuteWrapperClassCallback(
    391     uint16_t class_id, Object** wrapper) {
    392   if (wrapper_callbacks_.length() <= class_id) return NULL;
    393   return wrapper_callbacks_[class_id](
    394       class_id, Utils::ToLocal(Handle<Object>(wrapper)));
    395 }
    396 
    397 
    398 HeapSnapshot* HeapProfiler::TakeSnapshotImpl(const char* name,
    399                                              int type,
    400                                              v8::ActivityControl* control) {
    401   HeapSnapshot::Type s_type = static_cast<HeapSnapshot::Type>(type);
    402   HeapSnapshot* result =
    403       snapshots_->NewSnapshot(s_type, name, next_snapshot_uid_++);
    404   bool generation_completed = true;
    405   switch (s_type) {
    406     case HeapSnapshot::kFull: {
    407       HEAP->CollectAllGarbage(true);
    408       HeapSnapshotGenerator generator(result, control);
    409       generation_completed = generator.GenerateSnapshot();
    410       break;
    411     }
    412     case HeapSnapshot::kAggregated: {
    413       HEAP->CollectAllGarbage(true);
    414       AggregatedHeapSnapshot agg_snapshot;
    415       AggregatedHeapSnapshotGenerator generator(&agg_snapshot);
    416       generator.GenerateSnapshot();
    417       generator.FillHeapSnapshot(result);
    418       break;
    419     }
    420     default:
    421       UNREACHABLE();
    422   }
    423   if (!generation_completed) {
    424     delete result;
    425     result = NULL;
    426   }
    427   snapshots_->SnapshotGenerationFinished(result);
    428   return result;
    429 }
    430 
    431 
    432 HeapSnapshot* HeapProfiler::TakeSnapshotImpl(String* name,
    433                                              int type,
    434                                              v8::ActivityControl* control) {
    435   return TakeSnapshotImpl(snapshots_->names()->GetName(name), type, control);
    436 }
    437 
    438 
    439 int HeapProfiler::GetSnapshotsCount() {
    440   HeapProfiler* profiler = Isolate::Current()->heap_profiler();
    441   ASSERT(profiler != NULL);
    442   return profiler->snapshots_->snapshots()->length();
    443 }
    444 
    445 
    446 HeapSnapshot* HeapProfiler::GetSnapshot(int index) {
    447   HeapProfiler* profiler = Isolate::Current()->heap_profiler();
    448   ASSERT(profiler != NULL);
    449   return profiler->snapshots_->snapshots()->at(index);
    450 }
    451 
    452 
    453 HeapSnapshot* HeapProfiler::FindSnapshot(unsigned uid) {
    454   HeapProfiler* profiler = Isolate::Current()->heap_profiler();
    455   ASSERT(profiler != NULL);
    456   return profiler->snapshots_->GetSnapshot(uid);
    457 }
    458 
    459 
    460 void HeapProfiler::DeleteAllSnapshots() {
    461   HeapProfiler* profiler = Isolate::Current()->heap_profiler();
    462   ASSERT(profiler != NULL);
    463   profiler->ResetSnapshots();
    464 }
    465 
    466 
    467 void HeapProfiler::ObjectMoveEvent(Address from, Address to) {
    468   snapshots_->ObjectMoveEvent(from, to);
    469 }
    470 
    471 
    472 const JSObjectsClusterTreeConfig::Key JSObjectsClusterTreeConfig::kNoKey;
    473 const JSObjectsClusterTreeConfig::Value JSObjectsClusterTreeConfig::kNoValue;
    474 
    475 
    476 ConstructorHeapProfile::ConstructorHeapProfile()
    477     : zscope_(DELETE_ON_EXIT) {
    478 }
    479 
    480 
    481 void ConstructorHeapProfile::Call(const JSObjectsCluster& cluster,
    482                                   const NumberAndSizeInfo& number_and_size) {
    483   HeapStringAllocator allocator;
    484   StringStream stream(&allocator);
    485   cluster.Print(&stream);
    486   LOG(ISOLATE,
    487       HeapSampleJSConstructorEvent(*(stream.ToCString()),
    488                                    number_and_size.number(),
    489                                    number_and_size.bytes()));
    490 }
    491 
    492 
    493 void ConstructorHeapProfile::CollectStats(HeapObject* obj) {
    494   Clusterizer::InsertIntoTree(&js_objects_info_tree_, obj, false);
    495 }
    496 
    497 
    498 void ConstructorHeapProfile::PrintStats() {
    499   js_objects_info_tree_.ForEach(this);
    500 }
    501 
    502 
    503 static const char* GetConstructorName(const char* name) {
    504   return name[0] != '\0' ? name : "(anonymous)";
    505 }
    506 
    507 
    508 const char* JSObjectsCluster::GetSpecialCaseName() const {
    509   if (constructor_ == FromSpecialCase(ROOTS)) {
    510     return "(roots)";
    511   } else if (constructor_ == FromSpecialCase(GLOBAL_PROPERTY)) {
    512     return "(global property)";
    513   } else if (constructor_ == FromSpecialCase(CODE)) {
    514     return "(code)";
    515   } else if (constructor_ == FromSpecialCase(SELF)) {
    516     return "(self)";
    517   }
    518   return NULL;
    519 }
    520 
    521 
    522 void JSObjectsCluster::Print(StringStream* accumulator) const {
    523   ASSERT(!is_null());
    524   const char* special_case_name = GetSpecialCaseName();
    525   if (special_case_name != NULL) {
    526     accumulator->Add(special_case_name);
    527   } else {
    528     SmartPointer<char> s_name(
    529         constructor_->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL));
    530     accumulator->Add("%s", GetConstructorName(*s_name));
    531     if (instance_ != NULL) {
    532       accumulator->Add(":%p", static_cast<void*>(instance_));
    533     }
    534   }
    535 }
    536 
    537 
    538 void JSObjectsCluster::DebugPrint(StringStream* accumulator) const {
    539   if (!is_null()) {
    540     Print(accumulator);
    541   } else {
    542     accumulator->Add("(null cluster)");
    543   }
    544 }
    545 
    546 
    547 inline ClustersCoarser::ClusterBackRefs::ClusterBackRefs(
    548     const JSObjectsCluster& cluster_)
    549     : cluster(cluster_), refs(kInitialBackrefsListCapacity) {
    550 }
    551 
    552 
    553 inline ClustersCoarser::ClusterBackRefs::ClusterBackRefs(
    554     const ClustersCoarser::ClusterBackRefs& src)
    555     : cluster(src.cluster), refs(src.refs.capacity()) {
    556   refs.AddAll(src.refs);
    557 }
    558 
    559 
    560 inline ClustersCoarser::ClusterBackRefs&
    561     ClustersCoarser::ClusterBackRefs::operator=(
    562     const ClustersCoarser::ClusterBackRefs& src) {
    563   if (this == &src) return *this;
    564   cluster = src.cluster;
    565   refs.Clear();
    566   refs.AddAll(src.refs);
    567   return *this;
    568 }
    569 
    570 
    571 inline int ClustersCoarser::ClusterBackRefs::Compare(
    572     const ClustersCoarser::ClusterBackRefs& a,
    573     const ClustersCoarser::ClusterBackRefs& b) {
    574   int cmp = JSObjectsCluster::CompareConstructors(a.cluster, b.cluster);
    575   if (cmp != 0) return cmp;
    576   if (a.refs.length() < b.refs.length()) return -1;
    577   if (a.refs.length() > b.refs.length()) return 1;
    578   for (int i = 0; i < a.refs.length(); ++i) {
    579     int cmp = JSObjectsCluster::Compare(a.refs[i], b.refs[i]);
    580     if (cmp != 0) return cmp;
    581   }
    582   return 0;
    583 }
    584 
    585 
    586 ClustersCoarser::ClustersCoarser()
    587     : zscope_(DELETE_ON_EXIT),
    588       sim_list_(ClustersCoarser::kInitialSimilarityListCapacity),
    589       current_pair_(NULL),
    590       current_set_(NULL),
    591       self_(NULL) {
    592 }
    593 
    594 
    595 void ClustersCoarser::Call(const JSObjectsCluster& cluster,
    596                            JSObjectsClusterTree* tree) {
    597   if (!cluster.can_be_coarsed()) return;
    598   ClusterBackRefs pair(cluster);
    599   ASSERT(current_pair_ == NULL);
    600   current_pair_ = &pair;
    601   current_set_ = new JSObjectsRetainerTree();
    602   self_ = &cluster;
    603   tree->ForEach(this);
    604   sim_list_.Add(pair);
    605   current_pair_ = NULL;
    606   current_set_ = NULL;
    607   self_ = NULL;
    608 }
    609 
    610 
    611 void ClustersCoarser::Call(const JSObjectsCluster& cluster,
    612                            const NumberAndSizeInfo& number_and_size) {
    613   ASSERT(current_pair_ != NULL);
    614   ASSERT(current_set_ != NULL);
    615   ASSERT(self_ != NULL);
    616   JSObjectsRetainerTree::Locator loc;
    617   if (JSObjectsCluster::Compare(*self_, cluster) == 0) {
    618     current_pair_->refs.Add(JSObjectsCluster(JSObjectsCluster::SELF));
    619     return;
    620   }
    621   JSObjectsCluster eq = GetCoarseEquivalent(cluster);
    622   if (!eq.is_null()) {
    623     if (current_set_->Find(eq, &loc)) return;
    624     current_pair_->refs.Add(eq);
    625     current_set_->Insert(eq, &loc);
    626   } else {
    627     current_pair_->refs.Add(cluster);
    628   }
    629 }
    630 
    631 
    632 void ClustersCoarser::Process(JSObjectsRetainerTree* tree) {
    633   int last_eq_clusters = -1;
    634   for (int i = 0; i < kMaxPassesCount; ++i) {
    635     sim_list_.Clear();
    636     const int curr_eq_clusters = DoProcess(tree);
    637     // If no new cluster equivalents discovered, abort processing.
    638     if (last_eq_clusters == curr_eq_clusters) break;
    639     last_eq_clusters = curr_eq_clusters;
    640   }
    641 }
    642 
    643 
    644 int ClustersCoarser::DoProcess(JSObjectsRetainerTree* tree) {
    645   tree->ForEach(this);
    646   sim_list_.Iterate(ClusterBackRefs::SortRefsIterator);
    647   sim_list_.Sort(ClusterBackRefsCmp);
    648   return FillEqualityTree();
    649 }
    650 
    651 
    652 JSObjectsCluster ClustersCoarser::GetCoarseEquivalent(
    653     const JSObjectsCluster& cluster) {
    654   if (!cluster.can_be_coarsed()) return JSObjectsCluster();
    655   EqualityTree::Locator loc;
    656   return eq_tree_.Find(cluster, &loc) ? loc.value() : JSObjectsCluster();
    657 }
    658 
    659 
    660 bool ClustersCoarser::HasAnEquivalent(const JSObjectsCluster& cluster) {
    661   // Return true for coarsible clusters that have a non-identical equivalent.
    662   if (!cluster.can_be_coarsed()) return false;
    663   JSObjectsCluster eq = GetCoarseEquivalent(cluster);
    664   return !eq.is_null() && JSObjectsCluster::Compare(cluster, eq) != 0;
    665 }
    666 
    667 
    668 int ClustersCoarser::FillEqualityTree() {
    669   int eq_clusters_count = 0;
    670   int eq_to = 0;
    671   bool first_added = false;
    672   for (int i = 1; i < sim_list_.length(); ++i) {
    673     if (ClusterBackRefs::Compare(sim_list_[i], sim_list_[eq_to]) == 0) {
    674       EqualityTree::Locator loc;
    675       if (!first_added) {
    676         // Add self-equivalence, if we have more than one item in this
    677         // equivalence class.
    678         eq_tree_.Insert(sim_list_[eq_to].cluster, &loc);
    679         loc.set_value(sim_list_[eq_to].cluster);
    680         first_added = true;
    681       }
    682       eq_tree_.Insert(sim_list_[i].cluster, &loc);
    683       loc.set_value(sim_list_[eq_to].cluster);
    684       ++eq_clusters_count;
    685     } else {
    686       eq_to = i;
    687       first_added = false;
    688     }
    689   }
    690   return eq_clusters_count;
    691 }
    692 
    693 
    694 const JSObjectsCluster ClustersCoarser::ClusterEqualityConfig::kNoKey;
    695 const JSObjectsCluster ClustersCoarser::ClusterEqualityConfig::kNoValue;
    696 const JSObjectsRetainerTreeConfig::Key JSObjectsRetainerTreeConfig::kNoKey;
    697 const JSObjectsRetainerTreeConfig::Value JSObjectsRetainerTreeConfig::kNoValue =
    698     NULL;
    699 
    700 
    701 RetainerHeapProfile::RetainerHeapProfile()
    702     : zscope_(DELETE_ON_EXIT),
    703       aggregator_(NULL) {
    704   JSObjectsCluster roots(JSObjectsCluster::ROOTS);
    705   ReferencesExtractor extractor(roots, this);
    706   HEAP->IterateRoots(&extractor, VISIT_ONLY_STRONG);
    707 }
    708 
    709 
    710 RetainerHeapProfile::~RetainerHeapProfile() {
    711   delete aggregator_;
    712 }
    713 
    714 
    715 void RetainerHeapProfile::StoreReference(const JSObjectsCluster& cluster,
    716                                          HeapObject* ref) {
    717   JSObjectsCluster ref_cluster = Clusterizer::Clusterize(ref);
    718   if (ref_cluster.is_null()) return;
    719   JSObjectsRetainerTree::Locator ref_loc;
    720   if (retainers_tree_.Insert(ref_cluster, &ref_loc)) {
    721     ref_loc.set_value(new JSObjectsClusterTree());
    722   }
    723   JSObjectsClusterTree* referenced_by = ref_loc.value();
    724   Clusterizer::InsertReferenceIntoTree(referenced_by, cluster);
    725 }
    726 
    727 
    728 void RetainerHeapProfile::CollectStats(HeapObject* obj) {
    729   const JSObjectsCluster cluster = Clusterizer::Clusterize(obj);
    730   if (cluster.is_null()) return;
    731   ReferencesExtractor extractor(cluster, this);
    732   obj->Iterate(&extractor);
    733 }
    734 
    735 
    736 void RetainerHeapProfile::CoarseAndAggregate() {
    737   coarser_.Process(&retainers_tree_);
    738   ASSERT(aggregator_ == NULL);
    739   aggregator_ = new RetainerTreeAggregator(&coarser_);
    740   aggregator_->Process(&retainers_tree_);
    741 }
    742 
    743 
    744 void RetainerHeapProfile::DebugPrintStats(
    745     RetainerHeapProfile::Printer* printer) {
    746   // Print clusters that have no equivalents, aggregating their retainers.
    747   AggregatingRetainerTreePrinter agg_printer(&coarser_, printer);
    748   retainers_tree_.ForEach(&agg_printer);
    749   // Print clusters that have equivalents.
    750   SimpleRetainerTreePrinter s_printer(printer);
    751   aggregator_->output_tree().ForEach(&s_printer);
    752 }
    753 
    754 
    755 void RetainerHeapProfile::PrintStats() {
    756   RetainersPrinter printer;
    757   DebugPrintStats(&printer);
    758 }
    759 
    760 
    761 //
    762 // HeapProfiler class implementation.
    763 //
    764 static void StackWeakReferenceCallback(Persistent<Value> object,
    765                                        void* trace) {
    766   DeleteArray(static_cast<Address*>(trace));
    767   object.Dispose();
    768 }
    769 
    770 
    771 static void PrintProducerStackTrace(Object* obj, void* trace) {
    772   if (!obj->IsJSObject()) return;
    773   String* constructor = GetConstructorNameForHeapProfile(JSObject::cast(obj));
    774   SmartPointer<char> s_name(
    775       constructor->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL));
    776   LOG(ISOLATE,
    777       HeapSampleJSProducerEvent(GetConstructorName(*s_name),
    778                                 reinterpret_cast<Address*>(trace)));
    779 }
    780 
    781 
    782 void HeapProfiler::WriteSample() {
    783   Isolate* isolate = Isolate::Current();
    784   LOG(isolate, HeapSampleBeginEvent("Heap", "allocated"));
    785   LOG(isolate,
    786       HeapSampleStats(
    787           "Heap", "allocated", HEAP->CommittedMemory(), HEAP->SizeOfObjects()));
    788 
    789   AggregatedHeapSnapshot snapshot;
    790   AggregatedHeapSnapshotGenerator generator(&snapshot);
    791   generator.GenerateSnapshot();
    792 
    793   HistogramInfo* info = snapshot.info();
    794   for (int i = FIRST_NONSTRING_TYPE;
    795        i <= AggregatedHeapSnapshotGenerator::kAllStringsType;
    796        ++i) {
    797     if (info[i].bytes() > 0) {
    798       LOG(isolate,
    799           HeapSampleItemEvent(info[i].name(), info[i].number(),
    800                               info[i].bytes()));
    801     }
    802   }
    803 
    804   snapshot.js_cons_profile()->PrintStats();
    805   snapshot.js_retainer_profile()->PrintStats();
    806 
    807   isolate->global_handles()->IterateWeakRoots(PrintProducerStackTrace,
    808                                               StackWeakReferenceCallback);
    809 
    810   LOG(isolate, HeapSampleEndEvent("Heap", "allocated"));
    811 }
    812 
    813 
    814 AggregatedHeapSnapshot::AggregatedHeapSnapshot()
    815     : info_(NewArray<HistogramInfo>(
    816         AggregatedHeapSnapshotGenerator::kAllStringsType + 1)) {
    817 #define DEF_TYPE_NAME(name) info_[name].set_name(#name);
    818   INSTANCE_TYPE_LIST(DEF_TYPE_NAME);
    819 #undef DEF_TYPE_NAME
    820   info_[AggregatedHeapSnapshotGenerator::kAllStringsType].set_name(
    821       "STRING_TYPE");
    822 }
    823 
    824 
    825 AggregatedHeapSnapshot::~AggregatedHeapSnapshot() {
    826   DeleteArray(info_);
    827 }
    828 
    829 
    830 AggregatedHeapSnapshotGenerator::AggregatedHeapSnapshotGenerator(
    831     AggregatedHeapSnapshot* agg_snapshot)
    832     : agg_snapshot_(agg_snapshot) {
    833 }
    834 
    835 
    836 void AggregatedHeapSnapshotGenerator::CalculateStringsStats() {
    837   HistogramInfo* info = agg_snapshot_->info();
    838   HistogramInfo& strings = info[kAllStringsType];
    839   // Lump all the string types together.
    840 #define INCREMENT_SIZE(type, size, name, camel_name)   \
    841   strings.increment_number(info[type].number());       \
    842   strings.increment_bytes(info[type].bytes());
    843   STRING_TYPE_LIST(INCREMENT_SIZE);
    844 #undef INCREMENT_SIZE
    845 }
    846 
    847 
    848 void AggregatedHeapSnapshotGenerator::CollectStats(HeapObject* obj) {
    849   InstanceType type = obj->map()->instance_type();
    850   ASSERT(0 <= type && type <= LAST_TYPE);
    851   agg_snapshot_->info()[type].increment_number(1);
    852   agg_snapshot_->info()[type].increment_bytes(obj->Size());
    853 }
    854 
    855 
    856 void AggregatedHeapSnapshotGenerator::GenerateSnapshot() {
    857   HeapIterator iterator(HeapIterator::kFilterUnreachable);
    858   for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
    859     CollectStats(obj);
    860     agg_snapshot_->js_cons_profile()->CollectStats(obj);
    861     agg_snapshot_->js_retainer_profile()->CollectStats(obj);
    862   }
    863   CalculateStringsStats();
    864   agg_snapshot_->js_retainer_profile()->CoarseAndAggregate();
    865 }
    866 
    867 
    868 class CountingConstructorHeapProfileIterator {
    869  public:
    870   CountingConstructorHeapProfileIterator()
    871       : entities_count_(0), children_count_(0) {
    872   }
    873 
    874   void Call(const JSObjectsCluster& cluster,
    875             const NumberAndSizeInfo& number_and_size) {
    876     ++entities_count_;
    877     children_count_ += number_and_size.number();
    878   }
    879 
    880   int entities_count() { return entities_count_; }
    881   int children_count() { return children_count_; }
    882 
    883  private:
    884   int entities_count_;
    885   int children_count_;
    886 };
    887 
    888 
    889 static HeapEntry* AddEntryFromAggregatedSnapshot(HeapSnapshot* snapshot,
    890                                                  int* root_child_index,
    891                                                  HeapEntry::Type type,
    892                                                  const char* name,
    893                                                  int count,
    894                                                  int size,
    895                                                  int children_count,
    896                                                  int retainers_count) {
    897   HeapEntry* entry = snapshot->AddEntry(
    898       type, name, count, size, children_count, retainers_count);
    899   ASSERT(entry != NULL);
    900   snapshot->root()->SetUnidirElementReference(*root_child_index,
    901                                               *root_child_index + 1,
    902                                               entry);
    903   *root_child_index = *root_child_index + 1;
    904   return entry;
    905 }
    906 
    907 
    908 class AllocatingConstructorHeapProfileIterator {
    909  public:
    910   AllocatingConstructorHeapProfileIterator(HeapSnapshot* snapshot,
    911                                   int* root_child_index)
    912       : snapshot_(snapshot),
    913         root_child_index_(root_child_index) {
    914   }
    915 
    916   void Call(const JSObjectsCluster& cluster,
    917             const NumberAndSizeInfo& number_and_size) {
    918     const char* name = cluster.GetSpecialCaseName();
    919     if (name == NULL) {
    920       name = snapshot_->collection()->names()->GetFunctionName(
    921           cluster.constructor());
    922     }
    923     AddEntryFromAggregatedSnapshot(snapshot_,
    924                                    root_child_index_,
    925                                    HeapEntry::kObject,
    926                                    name,
    927                                    number_and_size.number(),
    928                                    number_and_size.bytes(),
    929                                    0,
    930                                    0);
    931   }
    932 
    933  private:
    934   HeapSnapshot* snapshot_;
    935   int* root_child_index_;
    936 };
    937 
    938 
    939 static HeapObject* ClusterAsHeapObject(const JSObjectsCluster& cluster) {
    940   return cluster.can_be_coarsed() ?
    941       reinterpret_cast<HeapObject*>(cluster.instance()) : cluster.constructor();
    942 }
    943 
    944 
    945 static JSObjectsCluster HeapObjectAsCluster(HeapObject* object) {
    946   if (object->IsString()) {
    947     return JSObjectsCluster(String::cast(object));
    948   } else {
    949     JSObject* js_obj = JSObject::cast(object);
    950     String* constructor = GetConstructorNameForHeapProfile(
    951         JSObject::cast(js_obj));
    952     return JSObjectsCluster(constructor, object);
    953   }
    954 }
    955 
    956 
    957 class CountingRetainersIterator {
    958  public:
    959   CountingRetainersIterator(const JSObjectsCluster& child_cluster,
    960                             HeapEntriesAllocator* allocator,
    961                             HeapEntriesMap* map)
    962       : child_(ClusterAsHeapObject(child_cluster)),
    963         allocator_(allocator),
    964         map_(map) {
    965     if (map_->Map(child_) == NULL)
    966       map_->Pair(child_, allocator_, HeapEntriesMap::kHeapEntryPlaceholder);
    967   }
    968 
    969   void Call(const JSObjectsCluster& cluster,
    970             const NumberAndSizeInfo& number_and_size) {
    971     if (map_->Map(ClusterAsHeapObject(cluster)) == NULL)
    972       map_->Pair(ClusterAsHeapObject(cluster),
    973                  allocator_,
    974                  HeapEntriesMap::kHeapEntryPlaceholder);
    975     map_->CountReference(ClusterAsHeapObject(cluster), child_);
    976   }
    977 
    978  private:
    979   HeapObject* child_;
    980   HeapEntriesAllocator* allocator_;
    981   HeapEntriesMap* map_;
    982 };
    983 
    984 
    985 class AllocatingRetainersIterator {
    986  public:
    987   AllocatingRetainersIterator(const JSObjectsCluster& child_cluster,
    988                               HeapEntriesAllocator*,
    989                               HeapEntriesMap* map)
    990       : child_(ClusterAsHeapObject(child_cluster)), map_(map) {
    991     child_entry_ = map_->Map(child_);
    992     ASSERT(child_entry_ != NULL);
    993   }
    994 
    995   void Call(const JSObjectsCluster& cluster,
    996             const NumberAndSizeInfo& number_and_size) {
    997     int child_index, retainer_index;
    998     map_->CountReference(ClusterAsHeapObject(cluster),
    999                          child_,
   1000                          &child_index,
   1001                          &retainer_index);
   1002     map_->Map(ClusterAsHeapObject(cluster))->SetIndexedReference(
   1003         HeapGraphEdge::kElement,
   1004         child_index,
   1005         number_and_size.number(),
   1006         child_entry_,
   1007         retainer_index);
   1008   }
   1009 
   1010  private:
   1011   HeapObject* child_;
   1012   HeapEntriesMap* map_;
   1013   HeapEntry* child_entry_;
   1014 };
   1015 
   1016 
   1017 template<class RetainersIterator>
   1018 class AggregatingRetainerTreeIterator {
   1019  public:
   1020   explicit AggregatingRetainerTreeIterator(ClustersCoarser* coarser,
   1021                                            HeapEntriesAllocator* allocator,
   1022                                            HeapEntriesMap* map)
   1023       : coarser_(coarser), allocator_(allocator), map_(map) {
   1024   }
   1025 
   1026   void Call(const JSObjectsCluster& cluster, JSObjectsClusterTree* tree) {
   1027     if (coarser_ != NULL &&
   1028         !coarser_->GetCoarseEquivalent(cluster).is_null()) return;
   1029     JSObjectsClusterTree* tree_to_iterate = tree;
   1030     ZoneScope zs(DELETE_ON_EXIT);
   1031     JSObjectsClusterTree dest_tree_;
   1032     if (coarser_ != NULL) {
   1033       RetainersAggregator retainers_aggregator(coarser_, &dest_tree_);
   1034       tree->ForEach(&retainers_aggregator);
   1035       tree_to_iterate = &dest_tree_;
   1036     }
   1037     RetainersIterator iterator(cluster, allocator_, map_);
   1038     tree_to_iterate->ForEach(&iterator);
   1039   }
   1040 
   1041  private:
   1042   ClustersCoarser* coarser_;
   1043   HeapEntriesAllocator* allocator_;
   1044   HeapEntriesMap* map_;
   1045 };
   1046 
   1047 
   1048 class AggregatedRetainerTreeAllocator : public HeapEntriesAllocator {
   1049  public:
   1050   AggregatedRetainerTreeAllocator(HeapSnapshot* snapshot,
   1051                                   int* root_child_index)
   1052       : snapshot_(snapshot), root_child_index_(root_child_index) {
   1053   }
   1054   ~AggregatedRetainerTreeAllocator() { }
   1055 
   1056   HeapEntry* AllocateEntry(
   1057       HeapThing ptr, int children_count, int retainers_count) {
   1058     HeapObject* obj = reinterpret_cast<HeapObject*>(ptr);
   1059     JSObjectsCluster cluster = HeapObjectAsCluster(obj);
   1060     const char* name = cluster.GetSpecialCaseName();
   1061     if (name == NULL) {
   1062       name = snapshot_->collection()->names()->GetFunctionName(
   1063           cluster.constructor());
   1064     }
   1065     return AddEntryFromAggregatedSnapshot(
   1066         snapshot_, root_child_index_, HeapEntry::kObject, name,
   1067         0, 0, children_count, retainers_count);
   1068   }
   1069 
   1070  private:
   1071   HeapSnapshot* snapshot_;
   1072   int* root_child_index_;
   1073 };
   1074 
   1075 
   1076 template<class Iterator>
   1077 void AggregatedHeapSnapshotGenerator::IterateRetainers(
   1078     HeapEntriesAllocator* allocator, HeapEntriesMap* entries_map) {
   1079   RetainerHeapProfile* p = agg_snapshot_->js_retainer_profile();
   1080   AggregatingRetainerTreeIterator<Iterator> agg_ret_iter_1(
   1081       p->coarser(), allocator, entries_map);
   1082   p->retainers_tree()->ForEach(&agg_ret_iter_1);
   1083   AggregatingRetainerTreeIterator<Iterator> agg_ret_iter_2(
   1084       NULL, allocator, entries_map);
   1085   p->aggregator()->output_tree().ForEach(&agg_ret_iter_2);
   1086 }
   1087 
   1088 
   1089 void AggregatedHeapSnapshotGenerator::FillHeapSnapshot(HeapSnapshot* snapshot) {
   1090   // Count the number of entities.
   1091   int histogram_entities_count = 0;
   1092   int histogram_children_count = 0;
   1093   int histogram_retainers_count = 0;
   1094   for (int i = FIRST_NONSTRING_TYPE; i <= kAllStringsType; ++i) {
   1095     if (agg_snapshot_->info()[i].bytes() > 0) {
   1096       ++histogram_entities_count;
   1097     }
   1098   }
   1099   CountingConstructorHeapProfileIterator counting_cons_iter;
   1100   agg_snapshot_->js_cons_profile()->ForEach(&counting_cons_iter);
   1101   histogram_entities_count += counting_cons_iter.entities_count();
   1102   HeapEntriesMap entries_map;
   1103   int root_child_index = 0;
   1104   AggregatedRetainerTreeAllocator allocator(snapshot, &root_child_index);
   1105   IterateRetainers<CountingRetainersIterator>(&allocator, &entries_map);
   1106   histogram_entities_count += entries_map.entries_count();
   1107   histogram_children_count += entries_map.total_children_count();
   1108   histogram_retainers_count += entries_map.total_retainers_count();
   1109 
   1110   // Root entry references all other entries.
   1111   histogram_children_count += histogram_entities_count;
   1112   int root_children_count = histogram_entities_count;
   1113   ++histogram_entities_count;
   1114 
   1115   // Allocate and fill entries in the snapshot, allocate references.
   1116   snapshot->AllocateEntries(histogram_entities_count,
   1117                             histogram_children_count,
   1118                             histogram_retainers_count);
   1119   snapshot->AddRootEntry(root_children_count);
   1120   for (int i = FIRST_NONSTRING_TYPE; i <= kAllStringsType; ++i) {
   1121     if (agg_snapshot_->info()[i].bytes() > 0) {
   1122       AddEntryFromAggregatedSnapshot(snapshot,
   1123                                      &root_child_index,
   1124                                      HeapEntry::kHidden,
   1125                                      agg_snapshot_->info()[i].name(),
   1126                                      agg_snapshot_->info()[i].number(),
   1127                                      agg_snapshot_->info()[i].bytes(),
   1128                                      0,
   1129                                      0);
   1130     }
   1131   }
   1132   AllocatingConstructorHeapProfileIterator alloc_cons_iter(
   1133       snapshot, &root_child_index);
   1134   agg_snapshot_->js_cons_profile()->ForEach(&alloc_cons_iter);
   1135   entries_map.AllocateEntries();
   1136 
   1137   // Fill up references.
   1138   IterateRetainers<AllocatingRetainersIterator>(&allocator, &entries_map);
   1139 
   1140   snapshot->SetDominatorsToSelf();
   1141 }
   1142 
   1143 
   1144 void ProducerHeapProfile::Setup() {
   1145   can_log_ = true;
   1146 }
   1147 
   1148 void ProducerHeapProfile::DoRecordJSObjectAllocation(Object* obj) {
   1149   ASSERT(FLAG_log_producers);
   1150   if (!can_log_) return;
   1151   int framesCount = 0;
   1152   for (JavaScriptFrameIterator it; !it.done(); it.Advance()) {
   1153     ++framesCount;
   1154   }
   1155   if (framesCount == 0) return;
   1156   ++framesCount;  // Reserve place for the terminator item.
   1157   Vector<Address> stack(NewArray<Address>(framesCount), framesCount);
   1158   int i = 0;
   1159   for (JavaScriptFrameIterator it; !it.done(); it.Advance()) {
   1160     stack[i++] = it.frame()->pc();
   1161   }
   1162   stack[i] = NULL;
   1163   Handle<Object> handle = isolate_->global_handles()->Create(obj);
   1164   isolate_->global_handles()->MakeWeak(handle.location(),
   1165                                        static_cast<void*>(stack.start()),
   1166                                        StackWeakReferenceCallback);
   1167 }
   1168 
   1169 
   1170 #endif  // ENABLE_LOGGING_AND_PROFILING
   1171 
   1172 
   1173 } }  // namespace v8::internal
   1174