• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
Clusterize(HeapObject * obj)46   static JSObjectsCluster Clusterize(HeapObject* obj) {
47     return Clusterize(obj, true);
48   }
49   static void InsertIntoTree(JSObjectsClusterTree* tree,
50                              HeapObject* obj, bool fine_grain);
InsertReferenceIntoTree(JSObjectsClusterTree * tree,const JSObjectsCluster & cluster)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);
GetObjectSize(HeapObject * 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 
Clusterize(HeapObject * obj,bool fine_grain)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 
InsertIntoTree(JSObjectsClusterTree * tree,HeapObject * obj,bool fine_grain)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 
InsertIntoTree(JSObjectsClusterTree * tree,const JSObjectsCluster & cluster,int size)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 
CalculateNetworkSize(JSObject * obj)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:
ReferencesExtractor(const JSObjectsCluster & cluster,RetainerHeapProfile * profile)136   ReferencesExtractor(const JSObjectsCluster& cluster,
137                       RetainerHeapProfile* profile)
138       : cluster_(cluster),
139         profile_(profile),
140         inside_array_(false) {
141   }
142 
VisitPointer(Object ** o)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 
VisitPointers(Object ** start,Object ** end)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:
PrintRetainers(const JSObjectsCluster & cluster,const StringStream & retainers)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:
ClusterTreePrinter(StringStream * stream)184   explicit ClusterTreePrinter(StringStream* stream) : stream_(stream) {}
Call(const JSObjectsCluster & cluster,const NumberAndSizeInfo & number_and_size)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 
Print(StringStream * stream,const JSObjectsCluster & cluster,const NumberAndSizeInfo & number_and_size)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:
SimpleRetainerTreePrinter(RetainerHeapProfile::Printer * printer)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 
Call(const JSObjectsCluster & cluster,JSObjectsClusterTree * tree)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:
RetainersAggregator(ClustersCoarser * coarser,JSObjectsClusterTree * dest_tree)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 
Call(const JSObjectsCluster & cluster,const NumberAndSizeInfo & number_and_size)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:
AggregatingRetainerTreePrinter(ClustersCoarser * coarser,RetainerHeapProfile::Printer * printer)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 
Call(const JSObjectsCluster & cluster,JSObjectsClusterTree * tree)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:
RetainerTreeAggregator(ClustersCoarser * coarser)287   explicit RetainerTreeAggregator(ClustersCoarser* coarser)
288       : coarser_(coarser) {}
Process(JSObjectsRetainerTree * input_tree)289   void Process(JSObjectsRetainerTree* input_tree) {
290     input_tree->ForEach(this);
291   }
292   void Call(const JSObjectsCluster& cluster, JSObjectsClusterTree* tree);
output_tree()293   JSObjectsRetainerTree& output_tree() { return output_tree_; }
294 
295  private:
296   ClustersCoarser* coarser_;
297   JSObjectsRetainerTree output_tree_;
298 };
299 
300 
Call(const JSObjectsCluster & cluster,JSObjectsClusterTree * tree)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 
ConstructorHeapProfile()320 ConstructorHeapProfile::ConstructorHeapProfile()
321     : zscope_(DELETE_ON_EXIT) {
322 }
323 
324 
Call(const JSObjectsCluster & cluster,const NumberAndSizeInfo & number_and_size)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 
CollectStats(HeapObject * obj)336 void ConstructorHeapProfile::CollectStats(HeapObject* obj) {
337   Clusterizer::InsertIntoTree(&js_objects_info_tree_, obj, false);
338 }
339 
340 
PrintStats()341 void ConstructorHeapProfile::PrintStats() {
342   js_objects_info_tree_.ForEach(this);
343 }
344 
345 
GetConstructorName(const char * name)346 static const char* GetConstructorName(const char* name) {
347   return name[0] != '\0' ? name : "(anonymous)";
348 }
349 
350 
Print(StringStream * accumulator) const351 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 
DebugPrint(StringStream * accumulator) const372 void JSObjectsCluster::DebugPrint(StringStream* accumulator) const {
373   if (!is_null()) {
374     Print(accumulator);
375   } else {
376     accumulator->Add("(null cluster)");
377   }
378 }
379 
380 
ClusterBackRefs(const JSObjectsCluster & cluster_)381 inline ClustersCoarser::ClusterBackRefs::ClusterBackRefs(
382     const JSObjectsCluster& cluster_)
383     : cluster(cluster_), refs(kInitialBackrefsListCapacity) {
384 }
385 
386 
ClusterBackRefs(const ClustersCoarser::ClusterBackRefs & src)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&
operator =(const ClustersCoarser::ClusterBackRefs & src)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 
Compare(const ClustersCoarser::ClusterBackRefs & a,const ClustersCoarser::ClusterBackRefs & b)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 
ClustersCoarser()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 
Call(const JSObjectsCluster & cluster,JSObjectsClusterTree * tree)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 
Call(const JSObjectsCluster & cluster,const NumberAndSizeInfo & number_and_size)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 
Process(JSObjectsRetainerTree * tree)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 
DoProcess(JSObjectsRetainerTree * tree)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 
GetCoarseEquivalent(const JSObjectsCluster & cluster)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 
HasAnEquivalent(const JSObjectsCluster & cluster)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 
FillEqualityTree()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 
RetainerHeapProfile()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 
StoreReference(const JSObjectsCluster & cluster,HeapObject * ref)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 
CollectStats(HeapObject * obj)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 
DebugPrintStats(RetainerHeapProfile::Printer * printer)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 
PrintStats()579 void RetainerHeapProfile::PrintStats() {
580   RetainersPrinter printer;
581   DebugPrintStats(&printer);
582 }
583 
584 
585 //
586 // HeapProfiler class implementation.
587 //
CollectStats(HeapObject * obj,HistogramInfo * info)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 
StackWeakReferenceCallback(Persistent<Value> object,void * trace)598 static void StackWeakReferenceCallback(Persistent<Value> object,
599                                        void* trace) {
600   DeleteArray(static_cast<Address*>(trace));
601   object.Dispose();
602 }
603 
604 
PrintProducerStackTrace(Object * obj,void * trace)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 
WriteSample()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 
Setup()665 void ProducerHeapProfile::Setup() {
666   can_log_ = true;
667 }
668 
DoRecordJSObjectAllocation(Object * obj)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