• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 the V8 project authors. All rights reserved.
2 //
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 
6 #include "src/heap/object-stats.h"
7 
8 #include <unordered_set>
9 
10 #include "src/assembler-inl.h"
11 #include "src/base/bits.h"
12 #include "src/compilation-cache.h"
13 #include "src/counters.h"
14 #include "src/globals.h"
15 #include "src/heap/heap-inl.h"
16 #include "src/heap/mark-compact.h"
17 #include "src/isolate.h"
18 #include "src/objects/compilation-cache-inl.h"
19 #include "src/objects/js-collection-inl.h"
20 #include "src/objects/literal-objects-inl.h"
21 #include "src/objects/templates.h"
22 #include "src/utils.h"
23 
24 namespace v8 {
25 namespace internal {
26 
27 static base::LazyMutex object_stats_mutex = LAZY_MUTEX_INITIALIZER;
28 
29 class FieldStatsCollector : public ObjectVisitor {
30  public:
FieldStatsCollector(size_t * tagged_fields_count,size_t * embedder_fields_count,size_t * unboxed_double_fields_count,size_t * raw_fields_count)31   FieldStatsCollector(size_t* tagged_fields_count,
32                       size_t* embedder_fields_count,
33                       size_t* unboxed_double_fields_count,
34                       size_t* raw_fields_count)
35       : tagged_fields_count_(tagged_fields_count),
36         embedder_fields_count_(embedder_fields_count),
37         unboxed_double_fields_count_(unboxed_double_fields_count),
38         raw_fields_count_(raw_fields_count) {}
39 
RecordStats(HeapObject * host)40   void RecordStats(HeapObject* host) {
41     size_t old_pointer_fields_count = *tagged_fields_count_;
42     host->Iterate(this);
43     size_t tagged_fields_count_in_object =
44         *tagged_fields_count_ - old_pointer_fields_count;
45 
46     int object_size_in_words = host->Size() / kPointerSize;
47     DCHECK_LE(tagged_fields_count_in_object, object_size_in_words);
48     size_t raw_fields_count_in_object =
49         object_size_in_words - tagged_fields_count_in_object;
50 
51     if (host->IsJSObject()) {
52       JSObjectFieldStats field_stats = GetInobjectFieldStats(host->map());
53       // Embedder fields are already included into pointer words.
54       DCHECK_LE(field_stats.embedded_fields_count_,
55                 tagged_fields_count_in_object);
56       tagged_fields_count_in_object -= field_stats.embedded_fields_count_;
57       *tagged_fields_count_ -= field_stats.embedded_fields_count_;
58       *embedder_fields_count_ += field_stats.embedded_fields_count_;
59 
60       // The rest are data words.
61       DCHECK_LE(field_stats.unboxed_double_fields_count_,
62                 raw_fields_count_in_object);
63       raw_fields_count_in_object -= field_stats.unboxed_double_fields_count_;
64       *unboxed_double_fields_count_ += field_stats.unboxed_double_fields_count_;
65     }
66     *raw_fields_count_ += raw_fields_count_in_object;
67   }
68 
VisitPointers(HeapObject * host,Object ** start,Object ** end)69   void VisitPointers(HeapObject* host, Object** start, Object** end) override {
70     *tagged_fields_count_ += (end - start);
71   }
VisitPointers(HeapObject * host,MaybeObject ** start,MaybeObject ** end)72   void VisitPointers(HeapObject* host, MaybeObject** start,
73                      MaybeObject** end) override {
74     *tagged_fields_count_ += (end - start);
75   }
76 
77  private:
78   struct JSObjectFieldStats {
JSObjectFieldStatsv8::internal::FieldStatsCollector::JSObjectFieldStats79     JSObjectFieldStats()
80         : embedded_fields_count_(0), unboxed_double_fields_count_(0) {}
81 
82     unsigned embedded_fields_count_ : kDescriptorIndexBitCount;
83     unsigned unboxed_double_fields_count_ : kDescriptorIndexBitCount;
84   };
85   std::unordered_map<Map*, JSObjectFieldStats> field_stats_cache_;
86 
87   JSObjectFieldStats GetInobjectFieldStats(Map* map);
88 
89   size_t* const tagged_fields_count_;
90   size_t* const embedder_fields_count_;
91   size_t* const unboxed_double_fields_count_;
92   size_t* const raw_fields_count_;
93 };
94 
95 FieldStatsCollector::JSObjectFieldStats
GetInobjectFieldStats(Map * map)96 FieldStatsCollector::GetInobjectFieldStats(Map* map) {
97   auto iter = field_stats_cache_.find(map);
98   if (iter != field_stats_cache_.end()) {
99     return iter->second;
100   }
101   // Iterate descriptor array and calculate stats.
102   JSObjectFieldStats stats;
103   stats.embedded_fields_count_ = JSObject::GetEmbedderFieldCount(map);
104   if (!map->is_dictionary_map()) {
105     int nof = map->NumberOfOwnDescriptors();
106     DescriptorArray* descriptors = map->instance_descriptors();
107     for (int descriptor = 0; descriptor < nof; descriptor++) {
108       PropertyDetails details = descriptors->GetDetails(descriptor);
109       if (details.location() == kField) {
110         FieldIndex index = FieldIndex::ForDescriptor(map, descriptor);
111         // Stop on first out-of-object field.
112         if (!index.is_inobject()) break;
113         if (details.representation().IsDouble() &&
114             map->IsUnboxedDoubleField(index)) {
115           ++stats.unboxed_double_fields_count_;
116         }
117       }
118     }
119   }
120   field_stats_cache_.insert(std::make_pair(map, stats));
121   return stats;
122 }
123 
ClearObjectStats(bool clear_last_time_stats)124 void ObjectStats::ClearObjectStats(bool clear_last_time_stats) {
125   memset(object_counts_, 0, sizeof(object_counts_));
126   memset(object_sizes_, 0, sizeof(object_sizes_));
127   memset(over_allocated_, 0, sizeof(over_allocated_));
128   memset(size_histogram_, 0, sizeof(size_histogram_));
129   memset(over_allocated_histogram_, 0, sizeof(over_allocated_histogram_));
130   if (clear_last_time_stats) {
131     memset(object_counts_last_time_, 0, sizeof(object_counts_last_time_));
132     memset(object_sizes_last_time_, 0, sizeof(object_sizes_last_time_));
133   }
134   tagged_fields_count_ = 0;
135   embedder_fields_count_ = 0;
136   unboxed_double_fields_count_ = 0;
137   raw_fields_count_ = 0;
138 }
139 
140 // Tell the compiler to never inline this: occasionally, the optimizer will
141 // decide to inline this and unroll the loop, making the compiled code more than
142 // 100KB larger.
PrintJSONArray(size_t * array,const int len)143 V8_NOINLINE static void PrintJSONArray(size_t* array, const int len) {
144   PrintF("[ ");
145   for (int i = 0; i < len; i++) {
146     PrintF("%zu", array[i]);
147     if (i != (len - 1)) PrintF(", ");
148   }
149   PrintF(" ]");
150 }
151 
DumpJSONArray(std::stringstream & stream,size_t * array,const int len)152 V8_NOINLINE static void DumpJSONArray(std::stringstream& stream, size_t* array,
153                                       const int len) {
154   stream << PrintCollection(Vector<size_t>(array, len));
155 }
156 
PrintKeyAndId(const char * key,int gc_count)157 void ObjectStats::PrintKeyAndId(const char* key, int gc_count) {
158   PrintF("\"isolate\": \"%p\", \"id\": %d, \"key\": \"%s\", ",
159          reinterpret_cast<void*>(isolate()), gc_count, key);
160 }
161 
PrintInstanceTypeJSON(const char * key,int gc_count,const char * name,int index)162 void ObjectStats::PrintInstanceTypeJSON(const char* key, int gc_count,
163                                         const char* name, int index) {
164   PrintF("{ ");
165   PrintKeyAndId(key, gc_count);
166   PrintF("\"type\": \"instance_type_data\", ");
167   PrintF("\"instance_type\": %d, ", index);
168   PrintF("\"instance_type_name\": \"%s\", ", name);
169   PrintF("\"overall\": %zu, ", object_sizes_[index]);
170   PrintF("\"count\": %zu, ", object_counts_[index]);
171   PrintF("\"over_allocated\": %zu, ", over_allocated_[index]);
172   PrintF("\"histogram\": ");
173   PrintJSONArray(size_histogram_[index], kNumberOfBuckets);
174   PrintF(",");
175   PrintF("\"over_allocated_histogram\": ");
176   PrintJSONArray(over_allocated_histogram_[index], kNumberOfBuckets);
177   PrintF(" }\n");
178 }
179 
PrintJSON(const char * key)180 void ObjectStats::PrintJSON(const char* key) {
181   double time = isolate()->time_millis_since_init();
182   int gc_count = heap()->gc_count();
183 
184   // gc_descriptor
185   PrintF("{ ");
186   PrintKeyAndId(key, gc_count);
187   PrintF("\"type\": \"gc_descriptor\", \"time\": %f }\n", time);
188   // field_data
189   PrintF("{ ");
190   PrintKeyAndId(key, gc_count);
191   PrintF("\"type\": \"field_data\"");
192   PrintF(", \"tagged_fields\": %zu", tagged_fields_count_ * kPointerSize);
193   PrintF(", \"embedder_fields\": %zu", embedder_fields_count_ * kPointerSize);
194   PrintF(", \"unboxed_double_fields\": %zu",
195          unboxed_double_fields_count_ * kDoubleSize);
196   PrintF(", \"other_raw_fields\": %zu", raw_fields_count_ * kPointerSize);
197   PrintF(" }\n");
198   // bucket_sizes
199   PrintF("{ ");
200   PrintKeyAndId(key, gc_count);
201   PrintF("\"type\": \"bucket_sizes\", \"sizes\": [ ");
202   for (int i = 0; i < kNumberOfBuckets; i++) {
203     PrintF("%d", 1 << (kFirstBucketShift + i));
204     if (i != (kNumberOfBuckets - 1)) PrintF(", ");
205   }
206   PrintF(" ] }\n");
207 
208 #define INSTANCE_TYPE_WRAPPER(name) \
209   PrintInstanceTypeJSON(key, gc_count, #name, name);
210 
211 #define VIRTUAL_INSTANCE_TYPE_WRAPPER(name) \
212   PrintInstanceTypeJSON(key, gc_count, #name, FIRST_VIRTUAL_TYPE + name);
213 
214   INSTANCE_TYPE_LIST(INSTANCE_TYPE_WRAPPER)
215   VIRTUAL_INSTANCE_TYPE_LIST(VIRTUAL_INSTANCE_TYPE_WRAPPER)
216 
217 #undef INSTANCE_TYPE_WRAPPER
218 #undef VIRTUAL_INSTANCE_TYPE_WRAPPER
219 }
220 
DumpInstanceTypeData(std::stringstream & stream,const char * name,int index)221 void ObjectStats::DumpInstanceTypeData(std::stringstream& stream,
222                                        const char* name, int index) {
223   stream << "\"" << name << "\":{";
224   stream << "\"type\":" << static_cast<int>(index) << ",";
225   stream << "\"overall\":" << object_sizes_[index] << ",";
226   stream << "\"count\":" << object_counts_[index] << ",";
227   stream << "\"over_allocated\":" << over_allocated_[index] << ",";
228   stream << "\"histogram\":";
229   DumpJSONArray(stream, size_histogram_[index], kNumberOfBuckets);
230   stream << ",\"over_allocated_histogram\":";
231   DumpJSONArray(stream, over_allocated_histogram_[index], kNumberOfBuckets);
232   stream << "},";
233 }
234 
Dump(std::stringstream & stream)235 void ObjectStats::Dump(std::stringstream& stream) {
236   double time = isolate()->time_millis_since_init();
237   int gc_count = heap()->gc_count();
238 
239   stream << "{";
240   stream << "\"isolate\":\"" << reinterpret_cast<void*>(isolate()) << "\",";
241   stream << "\"id\":" << gc_count << ",";
242   stream << "\"time\":" << time << ",";
243 
244   // field_data
245   stream << "\"field_data\":{";
246   stream << "\"tagged_fields\":" << (tagged_fields_count_ * kPointerSize);
247   stream << ",\"embedder_fields\":" << (embedder_fields_count_ * kPointerSize);
248   stream << ",\"unboxed_double_fields\": "
249          << (unboxed_double_fields_count_ * kDoubleSize);
250   stream << ",\"other_raw_fields\":" << (raw_fields_count_ * kPointerSize);
251   stream << "}, ";
252 
253   stream << "\"bucket_sizes\":[";
254   for (int i = 0; i < kNumberOfBuckets; i++) {
255     stream << (1 << (kFirstBucketShift + i));
256     if (i != (kNumberOfBuckets - 1)) stream << ",";
257   }
258   stream << "],";
259   stream << "\"type_data\":{";
260 
261 #define INSTANCE_TYPE_WRAPPER(name) DumpInstanceTypeData(stream, #name, name);
262 
263 #define VIRTUAL_INSTANCE_TYPE_WRAPPER(name) \
264   DumpInstanceTypeData(stream, #name, FIRST_VIRTUAL_TYPE + name);
265 
266   INSTANCE_TYPE_LIST(INSTANCE_TYPE_WRAPPER);
267   VIRTUAL_INSTANCE_TYPE_LIST(VIRTUAL_INSTANCE_TYPE_WRAPPER)
268   stream << "\"END\":{}}}";
269 
270 #undef INSTANCE_TYPE_WRAPPER
271 #undef VIRTUAL_INSTANCE_TYPE_WRAPPER
272 }
273 
CheckpointObjectStats()274 void ObjectStats::CheckpointObjectStats() {
275   base::LockGuard<base::Mutex> lock_guard(object_stats_mutex.Pointer());
276   MemCopy(object_counts_last_time_, object_counts_, sizeof(object_counts_));
277   MemCopy(object_sizes_last_time_, object_sizes_, sizeof(object_sizes_));
278   ClearObjectStats();
279 }
280 
281 namespace {
282 
Log2ForSize(size_t size)283 int Log2ForSize(size_t size) {
284   DCHECK_GT(size, 0);
285   return kSizetSize * 8 - 1 - base::bits::CountLeadingZeros(size);
286 }
287 
288 }  // namespace
289 
HistogramIndexFromSize(size_t size)290 int ObjectStats::HistogramIndexFromSize(size_t size) {
291   if (size == 0) return 0;
292   return Min(Max(Log2ForSize(size) + 1 - kFirstBucketShift, 0),
293              kLastValueBucketIndex);
294 }
295 
RecordObjectStats(InstanceType type,size_t size)296 void ObjectStats::RecordObjectStats(InstanceType type, size_t size) {
297   DCHECK_LE(type, LAST_TYPE);
298   object_counts_[type]++;
299   object_sizes_[type] += size;
300   size_histogram_[type][HistogramIndexFromSize(size)]++;
301 }
302 
RecordVirtualObjectStats(VirtualInstanceType type,size_t size,size_t over_allocated)303 void ObjectStats::RecordVirtualObjectStats(VirtualInstanceType type,
304                                            size_t size, size_t over_allocated) {
305   DCHECK_LE(type, LAST_VIRTUAL_TYPE);
306   object_counts_[FIRST_VIRTUAL_TYPE + type]++;
307   object_sizes_[FIRST_VIRTUAL_TYPE + type] += size;
308   size_histogram_[FIRST_VIRTUAL_TYPE + type][HistogramIndexFromSize(size)]++;
309   over_allocated_[FIRST_VIRTUAL_TYPE + type] += over_allocated;
310   over_allocated_histogram_[FIRST_VIRTUAL_TYPE + type]
311                            [HistogramIndexFromSize(size)]++;
312 }
313 
isolate()314 Isolate* ObjectStats::isolate() { return heap()->isolate(); }
315 
316 class ObjectStatsCollectorImpl {
317  public:
318   enum Phase {
319     kPhase1,
320     kPhase2,
321   };
322   static const int kNumberOfPhases = kPhase2 + 1;
323 
324   ObjectStatsCollectorImpl(Heap* heap, ObjectStats* stats);
325 
326   void CollectGlobalStatistics();
327 
328   enum class CollectFieldStats { kNo, kYes };
329   void CollectStatistics(HeapObject* obj, Phase phase,
330                          CollectFieldStats collect_field_stats);
331 
332  private:
333   enum CowMode {
334     kCheckCow,
335     kIgnoreCow,
336   };
337 
isolate()338   Isolate* isolate() { return heap_->isolate(); }
339 
340   bool RecordVirtualObjectStats(HeapObject* parent, HeapObject* obj,
341                                 ObjectStats::VirtualInstanceType type,
342                                 size_t size, size_t over_allocated,
343                                 CowMode check_cow_array = kCheckCow);
344   void RecordExternalResourceStats(Address resource,
345                                    ObjectStats::VirtualInstanceType type,
346                                    size_t size);
347   // Gets size from |ob| and assumes no over allocating.
348   bool RecordSimpleVirtualObjectStats(HeapObject* parent, HeapObject* obj,
349                                       ObjectStats::VirtualInstanceType type);
350   // For HashTable it is possible to compute over allocated memory.
351   void RecordHashTableVirtualObjectStats(HeapObject* parent,
352                                          FixedArray* hash_table,
353                                          ObjectStats::VirtualInstanceType type);
354 
355   bool SameLiveness(HeapObject* obj1, HeapObject* obj2);
356   bool CanRecordFixedArray(FixedArrayBase* array);
357   bool IsCowArray(FixedArrayBase* array);
358 
359   // Blacklist for objects that should not be recorded using
360   // VirtualObjectStats and RecordSimpleVirtualObjectStats. For recording those
361   // objects dispatch to the low level ObjectStats::RecordObjectStats manually.
362   bool ShouldRecordObject(HeapObject* object, CowMode check_cow_array);
363 
364   void RecordObjectStats(HeapObject* obj, InstanceType type, size_t size);
365 
366   // Specific recursion into constant pool or embedded code objects. Records
367   // FixedArrays and Tuple2.
368   void RecordVirtualObjectsForConstantPoolOrEmbeddedObjects(
369       HeapObject* parent, HeapObject* object,
370       ObjectStats::VirtualInstanceType type);
371 
372   // Details.
373   void RecordVirtualAllocationSiteDetails(AllocationSite* site);
374   void RecordVirtualBytecodeArrayDetails(BytecodeArray* bytecode);
375   void RecordVirtualCodeDetails(Code* code);
376   void RecordVirtualContext(Context* context);
377   void RecordVirtualFeedbackVectorDetails(FeedbackVector* vector);
378   void RecordVirtualFixedArrayDetails(FixedArray* array);
379   void RecordVirtualFunctionTemplateInfoDetails(FunctionTemplateInfo* fti);
380   void RecordVirtualJSGlobalObjectDetails(JSGlobalObject* object);
381   void RecordVirtualJSCollectionDetails(JSObject* object);
382   void RecordVirtualJSObjectDetails(JSObject* object);
383   void RecordVirtualMapDetails(Map* map);
384   void RecordVirtualScriptDetails(Script* script);
385   void RecordVirtualExternalStringDetails(ExternalString* script);
386   void RecordVirtualSharedFunctionInfoDetails(SharedFunctionInfo* info);
387   void RecordVirtualJSFunctionDetails(JSFunction* function);
388 
389   void RecordVirtualArrayBoilerplateDescription(
390       ArrayBoilerplateDescription* description);
391   Heap* heap_;
392   ObjectStats* stats_;
393   MarkCompactCollector::NonAtomicMarkingState* marking_state_;
394   std::unordered_set<HeapObject*> virtual_objects_;
395   std::unordered_set<Address> external_resources_;
396   FieldStatsCollector field_stats_collector_;
397 };
398 
ObjectStatsCollectorImpl(Heap * heap,ObjectStats * stats)399 ObjectStatsCollectorImpl::ObjectStatsCollectorImpl(Heap* heap,
400                                                    ObjectStats* stats)
401     : heap_(heap),
402       stats_(stats),
403       marking_state_(
404           heap->mark_compact_collector()->non_atomic_marking_state()),
405       field_stats_collector_(
406           &stats->tagged_fields_count_, &stats->embedder_fields_count_,
407           &stats->unboxed_double_fields_count_, &stats->raw_fields_count_) {}
408 
ShouldRecordObject(HeapObject * obj,CowMode check_cow_array)409 bool ObjectStatsCollectorImpl::ShouldRecordObject(HeapObject* obj,
410                                                   CowMode check_cow_array) {
411   if (obj->IsFixedArrayExact()) {
412     FixedArray* fixed_array = FixedArray::cast(obj);
413     bool cow_check = check_cow_array == kIgnoreCow || !IsCowArray(fixed_array);
414     return CanRecordFixedArray(fixed_array) && cow_check;
415   }
416   if (obj == ReadOnlyRoots(heap_).empty_property_array()) return false;
417   return true;
418 }
419 
RecordHashTableVirtualObjectStats(HeapObject * parent,FixedArray * hash_table,ObjectStats::VirtualInstanceType type)420 void ObjectStatsCollectorImpl::RecordHashTableVirtualObjectStats(
421     HeapObject* parent, FixedArray* hash_table,
422     ObjectStats::VirtualInstanceType type) {
423   CHECK(hash_table->IsHashTable());
424   // TODO(mlippautz): Implement over allocation for hash tables.
425   RecordVirtualObjectStats(parent, hash_table, type, hash_table->Size(),
426                            ObjectStats::kNoOverAllocation);
427 }
428 
RecordSimpleVirtualObjectStats(HeapObject * parent,HeapObject * obj,ObjectStats::VirtualInstanceType type)429 bool ObjectStatsCollectorImpl::RecordSimpleVirtualObjectStats(
430     HeapObject* parent, HeapObject* obj,
431     ObjectStats::VirtualInstanceType type) {
432   return RecordVirtualObjectStats(parent, obj, type, obj->Size(),
433                                   ObjectStats::kNoOverAllocation, kCheckCow);
434 }
435 
RecordVirtualObjectStats(HeapObject * parent,HeapObject * obj,ObjectStats::VirtualInstanceType type,size_t size,size_t over_allocated,CowMode check_cow_array)436 bool ObjectStatsCollectorImpl::RecordVirtualObjectStats(
437     HeapObject* parent, HeapObject* obj, ObjectStats::VirtualInstanceType type,
438     size_t size, size_t over_allocated, CowMode check_cow_array) {
439   if (!SameLiveness(parent, obj) || !ShouldRecordObject(obj, check_cow_array)) {
440     return false;
441   }
442 
443   if (virtual_objects_.find(obj) == virtual_objects_.end()) {
444     virtual_objects_.insert(obj);
445     stats_->RecordVirtualObjectStats(type, size, over_allocated);
446     return true;
447   }
448   return false;
449 }
450 
RecordExternalResourceStats(Address resource,ObjectStats::VirtualInstanceType type,size_t size)451 void ObjectStatsCollectorImpl::RecordExternalResourceStats(
452     Address resource, ObjectStats::VirtualInstanceType type, size_t size) {
453   if (external_resources_.find(resource) == external_resources_.end()) {
454     external_resources_.insert(resource);
455     stats_->RecordVirtualObjectStats(type, size, 0);
456   }
457 }
458 
RecordVirtualAllocationSiteDetails(AllocationSite * site)459 void ObjectStatsCollectorImpl::RecordVirtualAllocationSiteDetails(
460     AllocationSite* site) {
461   if (!site->PointsToLiteral()) return;
462   JSObject* boilerplate = site->boilerplate();
463   if (boilerplate->IsJSArray()) {
464     RecordSimpleVirtualObjectStats(site, boilerplate,
465                                    ObjectStats::JS_ARRAY_BOILERPLATE_TYPE);
466     // Array boilerplates cannot have properties.
467   } else {
468     RecordVirtualObjectStats(
469         site, boilerplate, ObjectStats::JS_OBJECT_BOILERPLATE_TYPE,
470         boilerplate->Size(), ObjectStats::kNoOverAllocation);
471     if (boilerplate->HasFastProperties()) {
472       // We'll mis-classify the empty_property_array here. Given that there is a
473       // single instance, this is negligible.
474       PropertyArray* properties = boilerplate->property_array();
475       RecordSimpleVirtualObjectStats(
476           site, properties, ObjectStats::BOILERPLATE_PROPERTY_ARRAY_TYPE);
477     } else {
478       NameDictionary* properties = boilerplate->property_dictionary();
479       RecordSimpleVirtualObjectStats(
480           site, properties, ObjectStats::BOILERPLATE_PROPERTY_DICTIONARY_TYPE);
481     }
482   }
483   FixedArrayBase* elements = boilerplate->elements();
484   RecordSimpleVirtualObjectStats(site, elements,
485                                  ObjectStats::BOILERPLATE_ELEMENTS_TYPE);
486 }
487 
RecordVirtualFunctionTemplateInfoDetails(FunctionTemplateInfo * fti)488 void ObjectStatsCollectorImpl::RecordVirtualFunctionTemplateInfoDetails(
489     FunctionTemplateInfo* fti) {
490   // named_property_handler and indexed_property_handler are recorded as
491   // INTERCEPTOR_INFO_TYPE.
492   if (!fti->call_code()->IsUndefined(isolate())) {
493     RecordSimpleVirtualObjectStats(
494         fti, CallHandlerInfo::cast(fti->call_code()),
495         ObjectStats::FUNCTION_TEMPLATE_INFO_ENTRIES_TYPE);
496   }
497   if (!fti->instance_call_handler()->IsUndefined(isolate())) {
498     RecordSimpleVirtualObjectStats(
499         fti, CallHandlerInfo::cast(fti->instance_call_handler()),
500         ObjectStats::FUNCTION_TEMPLATE_INFO_ENTRIES_TYPE);
501   }
502 }
503 
RecordVirtualJSGlobalObjectDetails(JSGlobalObject * object)504 void ObjectStatsCollectorImpl::RecordVirtualJSGlobalObjectDetails(
505     JSGlobalObject* object) {
506   // Properties.
507   GlobalDictionary* properties = object->global_dictionary();
508   RecordHashTableVirtualObjectStats(object, properties,
509                                     ObjectStats::GLOBAL_PROPERTIES_TYPE);
510   // Elements.
511   FixedArrayBase* elements = object->elements();
512   RecordSimpleVirtualObjectStats(object, elements,
513                                  ObjectStats::GLOBAL_ELEMENTS_TYPE);
514 }
515 
RecordVirtualJSCollectionDetails(JSObject * object)516 void ObjectStatsCollectorImpl::RecordVirtualJSCollectionDetails(
517     JSObject* object) {
518   if (object->IsJSMap()) {
519     RecordSimpleVirtualObjectStats(
520         object, FixedArray::cast(JSMap::cast(object)->table()),
521         ObjectStats::JS_COLLETION_TABLE_TYPE);
522   }
523   if (object->IsJSSet()) {
524     RecordSimpleVirtualObjectStats(
525         object, FixedArray::cast(JSSet::cast(object)->table()),
526         ObjectStats::JS_COLLETION_TABLE_TYPE);
527   }
528 }
529 
RecordVirtualJSObjectDetails(JSObject * object)530 void ObjectStatsCollectorImpl::RecordVirtualJSObjectDetails(JSObject* object) {
531   // JSGlobalObject is recorded separately.
532   if (object->IsJSGlobalObject()) return;
533 
534   // Properties.
535   if (object->HasFastProperties()) {
536     PropertyArray* properties = object->property_array();
537     CHECK_EQ(PROPERTY_ARRAY_TYPE, properties->map()->instance_type());
538   } else {
539     NameDictionary* properties = object->property_dictionary();
540     RecordHashTableVirtualObjectStats(
541         object, properties, ObjectStats::OBJECT_PROPERTY_DICTIONARY_TYPE);
542   }
543   // Elements.
544   FixedArrayBase* elements = object->elements();
545   RecordSimpleVirtualObjectStats(object, elements, ObjectStats::ELEMENTS_TYPE);
546 }
547 
GetFeedbackSlotType(MaybeObject * maybe_obj,FeedbackSlotKind kind,Isolate * isolate)548 static ObjectStats::VirtualInstanceType GetFeedbackSlotType(
549     MaybeObject* maybe_obj, FeedbackSlotKind kind, Isolate* isolate) {
550   if (maybe_obj->IsClearedWeakHeapObject())
551     return ObjectStats::FEEDBACK_VECTOR_SLOT_OTHER_TYPE;
552   Object* obj = maybe_obj->GetHeapObjectOrSmi();
553   switch (kind) {
554     case FeedbackSlotKind::kCall:
555       if (obj == *isolate->factory()->uninitialized_symbol() ||
556           obj == *isolate->factory()->premonomorphic_symbol()) {
557         return ObjectStats::FEEDBACK_VECTOR_SLOT_CALL_UNUSED_TYPE;
558       }
559       return ObjectStats::FEEDBACK_VECTOR_SLOT_CALL_TYPE;
560 
561     case FeedbackSlotKind::kLoadProperty:
562     case FeedbackSlotKind::kLoadGlobalInsideTypeof:
563     case FeedbackSlotKind::kLoadGlobalNotInsideTypeof:
564     case FeedbackSlotKind::kLoadKeyed:
565       if (obj == *isolate->factory()->uninitialized_symbol() ||
566           obj == *isolate->factory()->premonomorphic_symbol()) {
567         return ObjectStats::FEEDBACK_VECTOR_SLOT_LOAD_UNUSED_TYPE;
568       }
569       return ObjectStats::FEEDBACK_VECTOR_SLOT_LOAD_TYPE;
570 
571     case FeedbackSlotKind::kStoreNamedSloppy:
572     case FeedbackSlotKind::kStoreNamedStrict:
573     case FeedbackSlotKind::kStoreOwnNamed:
574     case FeedbackSlotKind::kStoreGlobalSloppy:
575     case FeedbackSlotKind::kStoreGlobalStrict:
576     case FeedbackSlotKind::kStoreKeyedSloppy:
577     case FeedbackSlotKind::kStoreKeyedStrict:
578       if (obj == *isolate->factory()->uninitialized_symbol() ||
579           obj == *isolate->factory()->premonomorphic_symbol()) {
580         return ObjectStats::FEEDBACK_VECTOR_SLOT_STORE_UNUSED_TYPE;
581       }
582       return ObjectStats::FEEDBACK_VECTOR_SLOT_STORE_TYPE;
583 
584     case FeedbackSlotKind::kBinaryOp:
585     case FeedbackSlotKind::kCompareOp:
586       return ObjectStats::FEEDBACK_VECTOR_SLOT_ENUM_TYPE;
587 
588     default:
589       return ObjectStats::FEEDBACK_VECTOR_SLOT_OTHER_TYPE;
590   }
591 }
592 
RecordVirtualFeedbackVectorDetails(FeedbackVector * vector)593 void ObjectStatsCollectorImpl::RecordVirtualFeedbackVectorDetails(
594     FeedbackVector* vector) {
595   if (virtual_objects_.find(vector) == virtual_objects_.end()) {
596     // Manually insert the feedback vector into the virtual object list, since
597     // we're logging its component parts separately.
598     virtual_objects_.insert(vector);
599 
600     size_t calculated_size = 0;
601 
602     // Log the feedback vector's header (fixed fields).
603     size_t header_size =
604         reinterpret_cast<Address>(vector->slots_start()) - vector->address();
605     stats_->RecordVirtualObjectStats(ObjectStats::FEEDBACK_VECTOR_HEADER_TYPE,
606                                      header_size,
607                                      ObjectStats::kNoOverAllocation);
608     calculated_size += header_size;
609 
610     // Iterate over the feedback slots and log each one.
611     if (!vector->shared_function_info()->HasFeedbackMetadata()) return;
612 
613     FeedbackMetadataIterator it(vector->metadata());
614     while (it.HasNext()) {
615       FeedbackSlot slot = it.Next();
616       // Log the entry (or entries) taken up by this slot.
617       size_t slot_size = it.entry_size() * kPointerSize;
618       stats_->RecordVirtualObjectStats(
619           GetFeedbackSlotType(vector->Get(slot), it.kind(), heap_->isolate()),
620           slot_size, ObjectStats::kNoOverAllocation);
621       calculated_size += slot_size;
622 
623       // Log the monomorphic/polymorphic helper objects that this slot owns.
624       for (int i = 0; i < it.entry_size(); i++) {
625         MaybeObject* raw_object = vector->get(slot.ToInt() + i);
626         if (!raw_object->IsStrongOrWeakHeapObject()) continue;
627         HeapObject* object = raw_object->GetHeapObject();
628         if (object->IsCell() || object->IsWeakFixedArray()) {
629           RecordSimpleVirtualObjectStats(
630               vector, object, ObjectStats::FEEDBACK_VECTOR_ENTRY_TYPE);
631         }
632       }
633     }
634 
635     CHECK_EQ(calculated_size, vector->Size());
636   }
637 }
638 
RecordVirtualFixedArrayDetails(FixedArray * array)639 void ObjectStatsCollectorImpl::RecordVirtualFixedArrayDetails(
640     FixedArray* array) {
641   if (IsCowArray(array)) {
642     RecordVirtualObjectStats(nullptr, array, ObjectStats::COW_ARRAY_TYPE,
643                              array->Size(), ObjectStats::kNoOverAllocation,
644                              kIgnoreCow);
645   }
646 }
647 
CollectStatistics(HeapObject * obj,Phase phase,CollectFieldStats collect_field_stats)648 void ObjectStatsCollectorImpl::CollectStatistics(
649     HeapObject* obj, Phase phase, CollectFieldStats collect_field_stats) {
650   Map* map = obj->map();
651   switch (phase) {
652     case kPhase1:
653       if (obj->IsFeedbackVector()) {
654         RecordVirtualFeedbackVectorDetails(FeedbackVector::cast(obj));
655       } else if (obj->IsMap()) {
656         RecordVirtualMapDetails(Map::cast(obj));
657       } else if (obj->IsBytecodeArray()) {
658         RecordVirtualBytecodeArrayDetails(BytecodeArray::cast(obj));
659       } else if (obj->IsCode()) {
660         RecordVirtualCodeDetails(Code::cast(obj));
661       } else if (obj->IsFunctionTemplateInfo()) {
662         RecordVirtualFunctionTemplateInfoDetails(
663             FunctionTemplateInfo::cast(obj));
664       } else if (obj->IsJSFunction()) {
665         RecordVirtualJSFunctionDetails(JSFunction::cast(obj));
666       } else if (obj->IsJSGlobalObject()) {
667         RecordVirtualJSGlobalObjectDetails(JSGlobalObject::cast(obj));
668       } else if (obj->IsJSObject()) {
669         // This phase needs to come after RecordVirtualAllocationSiteDetails
670         // to properly split among boilerplates.
671         RecordVirtualJSObjectDetails(JSObject::cast(obj));
672       } else if (obj->IsJSCollection()) {
673         RecordVirtualJSCollectionDetails(JSObject::cast(obj));
674       } else if (obj->IsSharedFunctionInfo()) {
675         RecordVirtualSharedFunctionInfoDetails(SharedFunctionInfo::cast(obj));
676       } else if (obj->IsContext()) {
677         RecordVirtualContext(Context::cast(obj));
678       } else if (obj->IsScript()) {
679         RecordVirtualScriptDetails(Script::cast(obj));
680       } else if (obj->IsExternalString()) {
681         RecordVirtualExternalStringDetails(ExternalString::cast(obj));
682       } else if (obj->IsArrayBoilerplateDescription()) {
683         RecordVirtualArrayBoilerplateDescription(
684             ArrayBoilerplateDescription::cast(obj));
685       } else if (obj->IsFixedArrayExact()) {
686         // Has to go last as it triggers too eagerly.
687         RecordVirtualFixedArrayDetails(FixedArray::cast(obj));
688       }
689       break;
690     case kPhase2:
691       RecordObjectStats(obj, map->instance_type(), obj->Size());
692       if (collect_field_stats == CollectFieldStats::kYes) {
693         field_stats_collector_.RecordStats(obj);
694       }
695       break;
696   }
697 }
698 
CollectGlobalStatistics()699 void ObjectStatsCollectorImpl::CollectGlobalStatistics() {
700   // Iterate boilerplates first to disambiguate them from regular JS objects.
701   Object* list = heap_->allocation_sites_list();
702   while (list->IsAllocationSite()) {
703     AllocationSite* site = AllocationSite::cast(list);
704     RecordVirtualAllocationSiteDetails(site);
705     list = site->weak_next();
706   }
707 
708   // FixedArray.
709   RecordSimpleVirtualObjectStats(nullptr, heap_->serialized_objects(),
710                                  ObjectStats::SERIALIZED_OBJECTS_TYPE);
711   RecordSimpleVirtualObjectStats(nullptr, heap_->number_string_cache(),
712                                  ObjectStats::NUMBER_STRING_CACHE_TYPE);
713   RecordSimpleVirtualObjectStats(
714       nullptr, heap_->single_character_string_cache(),
715       ObjectStats::SINGLE_CHARACTER_STRING_CACHE_TYPE);
716   RecordSimpleVirtualObjectStats(nullptr, heap_->string_split_cache(),
717                                  ObjectStats::STRING_SPLIT_CACHE_TYPE);
718   RecordSimpleVirtualObjectStats(nullptr, heap_->regexp_multiple_cache(),
719                                  ObjectStats::REGEXP_MULTIPLE_CACHE_TYPE);
720   RecordSimpleVirtualObjectStats(nullptr, heap_->retained_maps(),
721                                  ObjectStats::RETAINED_MAPS_TYPE);
722 
723   // WeakArrayList.
724   RecordSimpleVirtualObjectStats(
725       nullptr, WeakArrayList::cast(heap_->noscript_shared_function_infos()),
726       ObjectStats::NOSCRIPT_SHARED_FUNCTION_INFOS_TYPE);
727   RecordSimpleVirtualObjectStats(nullptr,
728                                  WeakArrayList::cast(heap_->script_list()),
729                                  ObjectStats::SCRIPT_LIST_TYPE);
730 
731   // HashTable.
732   RecordHashTableVirtualObjectStats(nullptr, heap_->code_stubs(),
733                                     ObjectStats::CODE_STUBS_TABLE_TYPE);
734 }
735 
RecordObjectStats(HeapObject * obj,InstanceType type,size_t size)736 void ObjectStatsCollectorImpl::RecordObjectStats(HeapObject* obj,
737                                                  InstanceType type,
738                                                  size_t size) {
739   if (virtual_objects_.find(obj) == virtual_objects_.end()) {
740     stats_->RecordObjectStats(type, size);
741   }
742 }
743 
CanRecordFixedArray(FixedArrayBase * array)744 bool ObjectStatsCollectorImpl::CanRecordFixedArray(FixedArrayBase* array) {
745   ReadOnlyRoots roots(heap_);
746   return array != roots.empty_fixed_array() &&
747          array != roots.empty_sloppy_arguments_elements() &&
748          array != roots.empty_slow_element_dictionary() &&
749          array != heap_->empty_property_dictionary();
750 }
751 
IsCowArray(FixedArrayBase * array)752 bool ObjectStatsCollectorImpl::IsCowArray(FixedArrayBase* array) {
753   return array->map() == ReadOnlyRoots(heap_).fixed_cow_array_map();
754 }
755 
SameLiveness(HeapObject * obj1,HeapObject * obj2)756 bool ObjectStatsCollectorImpl::SameLiveness(HeapObject* obj1,
757                                             HeapObject* obj2) {
758   return obj1 == nullptr || obj2 == nullptr ||
759          marking_state_->Color(obj1) == marking_state_->Color(obj2);
760 }
761 
RecordVirtualMapDetails(Map * map)762 void ObjectStatsCollectorImpl::RecordVirtualMapDetails(Map* map) {
763   // TODO(mlippautz): map->dependent_code(): DEPENDENT_CODE_TYPE.
764 
765   DescriptorArray* array = map->instance_descriptors();
766   if (map->owns_descriptors() &&
767       array != ReadOnlyRoots(heap_).empty_descriptor_array()) {
768     // DescriptorArray has its own instance type.
769     EnumCache* enum_cache = array->GetEnumCache();
770     RecordSimpleVirtualObjectStats(array, enum_cache->keys(),
771                                    ObjectStats::ENUM_CACHE_TYPE);
772     RecordSimpleVirtualObjectStats(array, enum_cache->indices(),
773                                    ObjectStats::ENUM_INDICES_CACHE_TYPE);
774   }
775 
776   if (map->is_prototype_map()) {
777     if (map->prototype_info()->IsPrototypeInfo()) {
778       PrototypeInfo* info = PrototypeInfo::cast(map->prototype_info());
779       Object* users = info->prototype_users();
780       if (users->IsWeakFixedArray()) {
781         RecordSimpleVirtualObjectStats(map, WeakArrayList::cast(users),
782                                        ObjectStats::PROTOTYPE_USERS_TYPE);
783       }
784     }
785   }
786 }
787 
RecordVirtualScriptDetails(Script * script)788 void ObjectStatsCollectorImpl::RecordVirtualScriptDetails(Script* script) {
789   RecordSimpleVirtualObjectStats(
790       script, script->shared_function_infos(),
791       ObjectStats::SCRIPT_SHARED_FUNCTION_INFOS_TYPE);
792 
793   // Log the size of external source code.
794   Object* raw_source = script->source();
795   if (raw_source->IsExternalString()) {
796     // The contents of external strings aren't on the heap, so we have to record
797     // them manually. The on-heap String object is recorded indepentendely in
798     // the normal pass.
799     ExternalString* string = ExternalString::cast(raw_source);
800     Address resource = string->resource_as_address();
801     size_t off_heap_size = string->ExternalPayloadSize();
802     RecordExternalResourceStats(
803         resource,
804         string->IsOneByteRepresentation()
805             ? ObjectStats::SCRIPT_SOURCE_EXTERNAL_ONE_BYTE_TYPE
806             : ObjectStats::SCRIPT_SOURCE_EXTERNAL_TWO_BYTE_TYPE,
807         off_heap_size);
808   } else if (raw_source->IsString()) {
809     String* source = String::cast(raw_source);
810     RecordSimpleVirtualObjectStats(
811         script, HeapObject::cast(raw_source),
812         source->IsOneByteRepresentation()
813             ? ObjectStats::SCRIPT_SOURCE_NON_EXTERNAL_ONE_BYTE_TYPE
814             : ObjectStats::SCRIPT_SOURCE_NON_EXTERNAL_TWO_BYTE_TYPE);
815   }
816 }
817 
RecordVirtualExternalStringDetails(ExternalString * string)818 void ObjectStatsCollectorImpl::RecordVirtualExternalStringDetails(
819     ExternalString* string) {
820   // Track the external string resource size in a separate category.
821 
822   Address resource = string->resource_as_address();
823   size_t off_heap_size = string->ExternalPayloadSize();
824   RecordExternalResourceStats(
825       resource,
826       string->IsOneByteRepresentation()
827           ? ObjectStats::STRING_EXTERNAL_RESOURCE_ONE_BYTE_TYPE
828           : ObjectStats::STRING_EXTERNAL_RESOURCE_TWO_BYTE_TYPE,
829       off_heap_size);
830 }
831 
RecordVirtualSharedFunctionInfoDetails(SharedFunctionInfo * info)832 void ObjectStatsCollectorImpl::RecordVirtualSharedFunctionInfoDetails(
833     SharedFunctionInfo* info) {
834   // Uncompiled SharedFunctionInfo gets its own category.
835   if (!info->is_compiled()) {
836     RecordSimpleVirtualObjectStats(
837         nullptr, info, ObjectStats::UNCOMPILED_SHARED_FUNCTION_INFO_TYPE);
838   }
839 }
840 
RecordVirtualJSFunctionDetails(JSFunction * function)841 void ObjectStatsCollectorImpl::RecordVirtualJSFunctionDetails(
842     JSFunction* function) {
843   // Uncompiled JSFunctions get their own category.
844   if (!function->is_compiled()) {
845     RecordSimpleVirtualObjectStats(nullptr, function,
846                                    ObjectStats::UNCOMPILED_JS_FUNCTION_TYPE);
847   }
848 }
RecordVirtualArrayBoilerplateDescription(ArrayBoilerplateDescription * description)849 void ObjectStatsCollectorImpl::RecordVirtualArrayBoilerplateDescription(
850     ArrayBoilerplateDescription* description) {
851   RecordVirtualObjectsForConstantPoolOrEmbeddedObjects(
852       description, description->constant_elements(),
853       ObjectStats::ARRAY_BOILERPLATE_DESCRIPTION_ELEMENTS_TYPE);
854 }
855 
856 void ObjectStatsCollectorImpl::
RecordVirtualObjectsForConstantPoolOrEmbeddedObjects(HeapObject * parent,HeapObject * object,ObjectStats::VirtualInstanceType type)857     RecordVirtualObjectsForConstantPoolOrEmbeddedObjects(
858         HeapObject* parent, HeapObject* object,
859         ObjectStats::VirtualInstanceType type) {
860   if (!RecordSimpleVirtualObjectStats(parent, object, type)) return;
861   if (object->IsFixedArrayExact()) {
862     FixedArray* array = FixedArray::cast(object);
863     for (int i = 0; i < array->length(); i++) {
864       Object* entry = array->get(i);
865       if (!entry->IsHeapObject()) continue;
866       RecordVirtualObjectsForConstantPoolOrEmbeddedObjects(
867           array, HeapObject::cast(entry), type);
868     }
869   }
870 }
871 
RecordVirtualBytecodeArrayDetails(BytecodeArray * bytecode)872 void ObjectStatsCollectorImpl::RecordVirtualBytecodeArrayDetails(
873     BytecodeArray* bytecode) {
874   RecordSimpleVirtualObjectStats(
875       bytecode, bytecode->constant_pool(),
876       ObjectStats::BYTECODE_ARRAY_CONSTANT_POOL_TYPE);
877   // FixedArrays on constant pool are used for holding descriptor information.
878   // They are shared with optimized code.
879   FixedArray* constant_pool = FixedArray::cast(bytecode->constant_pool());
880   for (int i = 0; i < constant_pool->length(); i++) {
881     Object* entry = constant_pool->get(i);
882     if (entry->IsFixedArrayExact()) {
883       RecordVirtualObjectsForConstantPoolOrEmbeddedObjects(
884           constant_pool, HeapObject::cast(entry),
885           ObjectStats::EMBEDDED_OBJECT_TYPE);
886     }
887   }
888   RecordSimpleVirtualObjectStats(
889       bytecode, bytecode->handler_table(),
890       ObjectStats::BYTECODE_ARRAY_HANDLER_TABLE_TYPE);
891 }
892 
893 namespace {
894 
CodeKindToVirtualInstanceType(Code::Kind kind)895 ObjectStats::VirtualInstanceType CodeKindToVirtualInstanceType(
896     Code::Kind kind) {
897   switch (kind) {
898 #define CODE_KIND_CASE(type) \
899   case Code::type:           \
900     return ObjectStats::type;
901     CODE_KIND_LIST(CODE_KIND_CASE)
902 #undef CODE_KIND_CASE
903     default:
904       UNREACHABLE();
905   }
906   UNREACHABLE();
907 }
908 
909 }  // namespace
910 
RecordVirtualCodeDetails(Code * code)911 void ObjectStatsCollectorImpl::RecordVirtualCodeDetails(Code* code) {
912   RecordSimpleVirtualObjectStats(nullptr, code,
913                                  CodeKindToVirtualInstanceType(code->kind()));
914   RecordSimpleVirtualObjectStats(code, code->deoptimization_data(),
915                                  ObjectStats::DEOPTIMIZATION_DATA_TYPE);
916   RecordSimpleVirtualObjectStats(code, code->relocation_info(),
917                                  ObjectStats::RELOC_INFO_TYPE);
918   Object* source_position_table = code->source_position_table();
919   if (source_position_table->IsSourcePositionTableWithFrameCache()) {
920     RecordSimpleVirtualObjectStats(
921         code,
922         SourcePositionTableWithFrameCache::cast(source_position_table)
923             ->source_position_table(),
924         ObjectStats::SOURCE_POSITION_TABLE_TYPE);
925   } else if (source_position_table->IsHeapObject()) {
926     RecordSimpleVirtualObjectStats(code,
927                                    HeapObject::cast(source_position_table),
928                                    ObjectStats::SOURCE_POSITION_TABLE_TYPE);
929   }
930   if (code->kind() == Code::Kind::OPTIMIZED_FUNCTION) {
931     DeoptimizationData* input_data =
932         DeoptimizationData::cast(code->deoptimization_data());
933     if (input_data->length() > 0) {
934       RecordSimpleVirtualObjectStats(code->deoptimization_data(),
935                                      input_data->LiteralArray(),
936                                      ObjectStats::OPTIMIZED_CODE_LITERALS_TYPE);
937     }
938   }
939   int const mode_mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT);
940   for (RelocIterator it(code, mode_mask); !it.done(); it.next()) {
941     RelocInfo::Mode mode = it.rinfo()->rmode();
942     if (mode == RelocInfo::EMBEDDED_OBJECT) {
943       Object* target = it.rinfo()->target_object();
944       if (target->IsFixedArrayExact()) {
945         RecordVirtualObjectsForConstantPoolOrEmbeddedObjects(
946             code, HeapObject::cast(target), ObjectStats::EMBEDDED_OBJECT_TYPE);
947       }
948     }
949   }
950 }
951 
RecordVirtualContext(Context * context)952 void ObjectStatsCollectorImpl::RecordVirtualContext(Context* context) {
953   if (context->IsNativeContext()) {
954     RecordObjectStats(context, NATIVE_CONTEXT_TYPE, context->Size());
955   } else if (context->IsFunctionContext()) {
956     RecordObjectStats(context, FUNCTION_CONTEXT_TYPE, context->Size());
957   } else {
958     RecordSimpleVirtualObjectStats(nullptr, context,
959                                    ObjectStats::OTHER_CONTEXT_TYPE);
960   }
961 }
962 
963 class ObjectStatsVisitor {
964  public:
ObjectStatsVisitor(Heap * heap,ObjectStatsCollectorImpl * live_collector,ObjectStatsCollectorImpl * dead_collector,ObjectStatsCollectorImpl::Phase phase)965   ObjectStatsVisitor(Heap* heap, ObjectStatsCollectorImpl* live_collector,
966                      ObjectStatsCollectorImpl* dead_collector,
967                      ObjectStatsCollectorImpl::Phase phase)
968       : live_collector_(live_collector),
969         dead_collector_(dead_collector),
970         marking_state_(
971             heap->mark_compact_collector()->non_atomic_marking_state()),
972         phase_(phase) {}
973 
Visit(HeapObject * obj,int size)974   bool Visit(HeapObject* obj, int size) {
975     if (marking_state_->IsBlack(obj)) {
976       live_collector_->CollectStatistics(
977           obj, phase_, ObjectStatsCollectorImpl::CollectFieldStats::kYes);
978     } else {
979       DCHECK(!marking_state_->IsGrey(obj));
980       dead_collector_->CollectStatistics(
981           obj, phase_, ObjectStatsCollectorImpl::CollectFieldStats::kNo);
982     }
983     return true;
984   }
985 
986  private:
987   ObjectStatsCollectorImpl* live_collector_;
988   ObjectStatsCollectorImpl* dead_collector_;
989   MarkCompactCollector::NonAtomicMarkingState* marking_state_;
990   ObjectStatsCollectorImpl::Phase phase_;
991 };
992 
993 namespace {
994 
IterateHeap(Heap * heap,ObjectStatsVisitor * visitor)995 void IterateHeap(Heap* heap, ObjectStatsVisitor* visitor) {
996   SpaceIterator space_it(heap);
997   HeapObject* obj = nullptr;
998   while (space_it.has_next()) {
999     std::unique_ptr<ObjectIterator> it(space_it.next()->GetObjectIterator());
1000     ObjectIterator* obj_it = it.get();
1001     while ((obj = obj_it->Next()) != nullptr) {
1002       visitor->Visit(obj, obj->Size());
1003     }
1004   }
1005 }
1006 
1007 }  // namespace
1008 
Collect()1009 void ObjectStatsCollector::Collect() {
1010   ObjectStatsCollectorImpl live_collector(heap_, live_);
1011   ObjectStatsCollectorImpl dead_collector(heap_, dead_);
1012   live_collector.CollectGlobalStatistics();
1013   for (int i = 0; i < ObjectStatsCollectorImpl::kNumberOfPhases; i++) {
1014     ObjectStatsVisitor visitor(heap_, &live_collector, &dead_collector,
1015                                static_cast<ObjectStatsCollectorImpl::Phase>(i));
1016     IterateHeap(heap_, &visitor);
1017   }
1018 }
1019 
1020 }  // namespace internal
1021 }  // namespace v8
1022