1 // Copyright 2015 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/heap/object-stats.h"
6 
7 #include "src/compilation-cache.h"
8 #include "src/counters.h"
9 #include "src/heap/heap-inl.h"
10 #include "src/isolate.h"
11 #include "src/macro-assembler.h"
12 #include "src/utils.h"
13 
14 namespace v8 {
15 namespace internal {
16 
17 static base::LazyMutex object_stats_mutex = LAZY_MUTEX_INITIALIZER;
18 
19 
ClearObjectStats(bool clear_last_time_stats)20 void ObjectStats::ClearObjectStats(bool clear_last_time_stats) {
21   memset(object_counts_, 0, sizeof(object_counts_));
22   memset(object_sizes_, 0, sizeof(object_sizes_));
23   memset(over_allocated_, 0, sizeof(over_allocated_));
24   memset(size_histogram_, 0, sizeof(size_histogram_));
25   memset(over_allocated_histogram_, 0, sizeof(over_allocated_histogram_));
26   if (clear_last_time_stats) {
27     memset(object_counts_last_time_, 0, sizeof(object_counts_last_time_));
28     memset(object_sizes_last_time_, 0, sizeof(object_sizes_last_time_));
29   }
30   visited_fixed_array_sub_types_.clear();
31 }
32 
33 // Tell the compiler to never inline this: occasionally, the optimizer will
34 // decide to inline this and unroll the loop, making the compiled code more than
35 // 100KB larger.
PrintJSONArray(size_t * array,const int len)36 V8_NOINLINE static void PrintJSONArray(size_t* array, const int len) {
37   PrintF("[ ");
38   for (int i = 0; i < len; i++) {
39     PrintF("%zu", array[i]);
40     if (i != (len - 1)) PrintF(", ");
41   }
42   PrintF(" ]");
43 }
44 
DumpJSONArray(std::stringstream & stream,size_t * array,const int len)45 V8_NOINLINE static void DumpJSONArray(std::stringstream& stream, size_t* array,
46                                       const int len) {
47   stream << "[";
48   for (int i = 0; i < len; i++) {
49     stream << array[i];
50     if (i != (len - 1)) stream << ",";
51   }
52   stream << "]";
53 }
54 
PrintKeyAndId(const char * key,int gc_count)55 void ObjectStats::PrintKeyAndId(const char* key, int gc_count) {
56   PrintF("\"isolate\": \"%p\", \"id\": %d, \"key\": \"%s\", ",
57          reinterpret_cast<void*>(isolate()), gc_count, key);
58 }
59 
PrintInstanceTypeJSON(const char * key,int gc_count,const char * name,int index)60 void ObjectStats::PrintInstanceTypeJSON(const char* key, int gc_count,
61                                         const char* name, int index) {
62   PrintF("{ ");
63   PrintKeyAndId(key, gc_count);
64   PrintF("\"type\": \"instance_type_data\", ");
65   PrintF("\"instance_type\": %d, ", index);
66   PrintF("\"instance_type_name\": \"%s\", ", name);
67   PrintF("\"overall\": %zu, ", object_sizes_[index]);
68   PrintF("\"count\": %zu, ", object_counts_[index]);
69   PrintF("\"over_allocated\": %zu, ", over_allocated_[index]);
70   PrintF("\"histogram\": ");
71   PrintJSONArray(size_histogram_[index], kNumberOfBuckets);
72   PrintF(",");
73   PrintF("\"over_allocated_histogram\": ");
74   PrintJSONArray(over_allocated_histogram_[index], kNumberOfBuckets);
75   PrintF(" }\n");
76 }
77 
PrintJSON(const char * key)78 void ObjectStats::PrintJSON(const char* key) {
79   double time = isolate()->time_millis_since_init();
80   int gc_count = heap()->gc_count();
81 
82   // gc_descriptor
83   PrintF("{ ");
84   PrintKeyAndId(key, gc_count);
85   PrintF("\"type\": \"gc_descriptor\", \"time\": %f }\n", time);
86   // bucket_sizes
87   PrintF("{ ");
88   PrintKeyAndId(key, gc_count);
89   PrintF("\"type\": \"bucket_sizes\", \"sizes\": [ ");
90   for (int i = 0; i < kNumberOfBuckets; i++) {
91     PrintF("%d", 1 << (kFirstBucketShift + i));
92     if (i != (kNumberOfBuckets - 1)) PrintF(", ");
93   }
94   PrintF(" ] }\n");
95 
96 #define INSTANCE_TYPE_WRAPPER(name) \
97   PrintInstanceTypeJSON(key, gc_count, #name, name);
98 #define CODE_KIND_WRAPPER(name)                        \
99   PrintInstanceTypeJSON(key, gc_count, "*CODE_" #name, \
100                         FIRST_CODE_KIND_SUB_TYPE + Code::name);
101 #define FIXED_ARRAY_SUB_INSTANCE_TYPE_WRAPPER(name)           \
102   PrintInstanceTypeJSON(key, gc_count, "*FIXED_ARRAY_" #name, \
103                         FIRST_FIXED_ARRAY_SUB_TYPE + name);
104 #define CODE_AGE_WRAPPER(name)           \
105   PrintInstanceTypeJSON(                 \
106       key, gc_count, "*CODE_AGE_" #name, \
107       FIRST_CODE_AGE_SUB_TYPE + Code::k##name##CodeAge - Code::kFirstCodeAge);
108 
109   INSTANCE_TYPE_LIST(INSTANCE_TYPE_WRAPPER)
110   CODE_KIND_LIST(CODE_KIND_WRAPPER)
111   FIXED_ARRAY_SUB_INSTANCE_TYPE_LIST(FIXED_ARRAY_SUB_INSTANCE_TYPE_WRAPPER)
112   CODE_AGE_LIST_COMPLETE(CODE_AGE_WRAPPER)
113 
114 #undef INSTANCE_TYPE_WRAPPER
115 #undef CODE_KIND_WRAPPER
116 #undef FIXED_ARRAY_SUB_INSTANCE_TYPE_WRAPPER
117 #undef CODE_AGE_WRAPPER
118 #undef PRINT_INSTANCE_TYPE_DATA
119 #undef PRINT_KEY_AND_ID
120 }
121 
DumpInstanceTypeData(std::stringstream & stream,const char * name,int index)122 void ObjectStats::DumpInstanceTypeData(std::stringstream& stream,
123                                        const char* name, int index) {
124   stream << "\"" << name << "\":{";
125   stream << "\"type\":" << static_cast<int>(index) << ",";
126   stream << "\"overall\":" << object_sizes_[index] << ",";
127   stream << "\"count\":" << object_counts_[index] << ",";
128   stream << "\"over_allocated\":" << over_allocated_[index] << ",";
129   stream << "\"histogram\":";
130   DumpJSONArray(stream, size_histogram_[index], kNumberOfBuckets);
131   stream << ",\"over_allocated_histogram\":";
132   DumpJSONArray(stream, over_allocated_histogram_[index], kNumberOfBuckets);
133   stream << "},";
134 }
135 
Dump(std::stringstream & stream)136 void ObjectStats::Dump(std::stringstream& stream) {
137   double time = isolate()->time_millis_since_init();
138   int gc_count = heap()->gc_count();
139 
140   stream << "{";
141   stream << "\"isolate\":\"" << reinterpret_cast<void*>(isolate()) << "\",";
142   stream << "\"id\":" << gc_count << ",";
143   stream << "\"time\":" << time << ",";
144   stream << "\"bucket_sizes\":[";
145   for (int i = 0; i < kNumberOfBuckets; i++) {
146     stream << (1 << (kFirstBucketShift + i));
147     if (i != (kNumberOfBuckets - 1)) stream << ",";
148   }
149   stream << "],";
150   stream << "\"type_data\":{";
151 
152 #define INSTANCE_TYPE_WRAPPER(name) DumpInstanceTypeData(stream, #name, name);
153 #define CODE_KIND_WRAPPER(name)                \
154   DumpInstanceTypeData(stream, "*CODE_" #name, \
155                        FIRST_CODE_KIND_SUB_TYPE + Code::name);
156 
157 #define FIXED_ARRAY_SUB_INSTANCE_TYPE_WRAPPER(name)   \
158   DumpInstanceTypeData(stream, "*FIXED_ARRAY_" #name, \
159                        FIRST_FIXED_ARRAY_SUB_TYPE + name);
160 
161 #define CODE_AGE_WRAPPER(name)    \
162   DumpInstanceTypeData(           \
163       stream, "*CODE_AGE_" #name, \
164       FIRST_CODE_AGE_SUB_TYPE + Code::k##name##CodeAge - Code::kFirstCodeAge);
165 
166   INSTANCE_TYPE_LIST(INSTANCE_TYPE_WRAPPER);
167   CODE_KIND_LIST(CODE_KIND_WRAPPER);
168   FIXED_ARRAY_SUB_INSTANCE_TYPE_LIST(FIXED_ARRAY_SUB_INSTANCE_TYPE_WRAPPER);
169   CODE_AGE_LIST_COMPLETE(CODE_AGE_WRAPPER);
170   stream << "\"END\":{}}}";
171 
172 #undef INSTANCE_TYPE_WRAPPER
173 #undef CODE_KIND_WRAPPER
174 #undef FIXED_ARRAY_SUB_INSTANCE_TYPE_WRAPPER
175 #undef CODE_AGE_WRAPPER
176 #undef PRINT_INSTANCE_TYPE_DATA
177 }
178 
CheckpointObjectStats()179 void ObjectStats::CheckpointObjectStats() {
180   base::LockGuard<base::Mutex> lock_guard(object_stats_mutex.Pointer());
181   Counters* counters = isolate()->counters();
182 #define ADJUST_LAST_TIME_OBJECT_COUNT(name)              \
183   counters->count_of_##name()->Increment(                \
184       static_cast<int>(object_counts_[name]));           \
185   counters->count_of_##name()->Decrement(                \
186       static_cast<int>(object_counts_last_time_[name])); \
187   counters->size_of_##name()->Increment(                 \
188       static_cast<int>(object_sizes_[name]));            \
189   counters->size_of_##name()->Decrement(                 \
190       static_cast<int>(object_sizes_last_time_[name]));
191   INSTANCE_TYPE_LIST(ADJUST_LAST_TIME_OBJECT_COUNT)
192 #undef ADJUST_LAST_TIME_OBJECT_COUNT
193   int index;
194 #define ADJUST_LAST_TIME_OBJECT_COUNT(name)               \
195   index = FIRST_CODE_KIND_SUB_TYPE + Code::name;          \
196   counters->count_of_CODE_TYPE_##name()->Increment(       \
197       static_cast<int>(object_counts_[index]));           \
198   counters->count_of_CODE_TYPE_##name()->Decrement(       \
199       static_cast<int>(object_counts_last_time_[index])); \
200   counters->size_of_CODE_TYPE_##name()->Increment(        \
201       static_cast<int>(object_sizes_[index]));            \
202   counters->size_of_CODE_TYPE_##name()->Decrement(        \
203       static_cast<int>(object_sizes_last_time_[index]));
204   CODE_KIND_LIST(ADJUST_LAST_TIME_OBJECT_COUNT)
205 #undef ADJUST_LAST_TIME_OBJECT_COUNT
206 #define ADJUST_LAST_TIME_OBJECT_COUNT(name)               \
207   index = FIRST_FIXED_ARRAY_SUB_TYPE + name;              \
208   counters->count_of_FIXED_ARRAY_##name()->Increment(     \
209       static_cast<int>(object_counts_[index]));           \
210   counters->count_of_FIXED_ARRAY_##name()->Decrement(     \
211       static_cast<int>(object_counts_last_time_[index])); \
212   counters->size_of_FIXED_ARRAY_##name()->Increment(      \
213       static_cast<int>(object_sizes_[index]));            \
214   counters->size_of_FIXED_ARRAY_##name()->Decrement(      \
215       static_cast<int>(object_sizes_last_time_[index]));
216   FIXED_ARRAY_SUB_INSTANCE_TYPE_LIST(ADJUST_LAST_TIME_OBJECT_COUNT)
217 #undef ADJUST_LAST_TIME_OBJECT_COUNT
218 #define ADJUST_LAST_TIME_OBJECT_COUNT(name)                                   \
219   index =                                                                     \
220       FIRST_CODE_AGE_SUB_TYPE + Code::k##name##CodeAge - Code::kFirstCodeAge; \
221   counters->count_of_CODE_AGE_##name()->Increment(                            \
222       static_cast<int>(object_counts_[index]));                               \
223   counters->count_of_CODE_AGE_##name()->Decrement(                            \
224       static_cast<int>(object_counts_last_time_[index]));                     \
225   counters->size_of_CODE_AGE_##name()->Increment(                             \
226       static_cast<int>(object_sizes_[index]));                                \
227   counters->size_of_CODE_AGE_##name()->Decrement(                             \
228       static_cast<int>(object_sizes_last_time_[index]));
229   CODE_AGE_LIST_COMPLETE(ADJUST_LAST_TIME_OBJECT_COUNT)
230 #undef ADJUST_LAST_TIME_OBJECT_COUNT
231 
232   MemCopy(object_counts_last_time_, object_counts_, sizeof(object_counts_));
233   MemCopy(object_sizes_last_time_, object_sizes_, sizeof(object_sizes_));
234   ClearObjectStats();
235 }
236 
237 
isolate()238 Isolate* ObjectStats::isolate() { return heap()->isolate(); }
239 
CollectStatistics(HeapObject * obj)240 void ObjectStatsCollector::CollectStatistics(HeapObject* obj) {
241   Map* map = obj->map();
242 
243   // Record for the InstanceType.
244   int object_size = obj->Size();
245   stats_->RecordObjectStats(map->instance_type(), object_size);
246 
247   // Record specific sub types where possible.
248   if (obj->IsMap()) RecordMapDetails(Map::cast(obj));
249   if (obj->IsObjectTemplateInfo() || obj->IsFunctionTemplateInfo()) {
250     RecordTemplateInfoDetails(TemplateInfo::cast(obj));
251   }
252   if (obj->IsBytecodeArray()) {
253     RecordBytecodeArrayDetails(BytecodeArray::cast(obj));
254   }
255   if (obj->IsCode()) RecordCodeDetails(Code::cast(obj));
256   if (obj->IsSharedFunctionInfo()) {
257     RecordSharedFunctionInfoDetails(SharedFunctionInfo::cast(obj));
258   }
259   if (obj->IsFixedArray()) RecordFixedArrayDetails(FixedArray::cast(obj));
260   if (obj->IsJSObject()) RecordJSObjectDetails(JSObject::cast(obj));
261   if (obj->IsJSWeakCollection()) {
262     RecordJSWeakCollectionDetails(JSWeakCollection::cast(obj));
263   }
264   if (obj->IsJSCollection()) {
265     RecordJSCollectionDetails(JSObject::cast(obj));
266   }
267   if (obj->IsJSFunction()) RecordJSFunctionDetails(JSFunction::cast(obj));
268   if (obj->IsScript()) RecordScriptDetails(Script::cast(obj));
269 }
270 
271 class ObjectStatsCollector::CompilationCacheTableVisitor
272     : public ObjectVisitor {
273  public:
CompilationCacheTableVisitor(ObjectStatsCollector * parent)274   explicit CompilationCacheTableVisitor(ObjectStatsCollector* parent)
275       : parent_(parent) {}
276 
VisitPointers(Object ** start,Object ** end)277   void VisitPointers(Object** start, Object** end) override {
278     for (Object** current = start; current < end; current++) {
279       HeapObject* obj = HeapObject::cast(*current);
280       if (obj->IsUndefined(parent_->heap_->isolate())) continue;
281       CHECK(obj->IsCompilationCacheTable());
282       parent_->RecordHashTableHelper(nullptr, CompilationCacheTable::cast(obj),
283                                      COMPILATION_CACHE_TABLE_SUB_TYPE);
284     }
285   }
286 
287  private:
288   ObjectStatsCollector* parent_;
289 };
290 
CollectGlobalStatistics()291 void ObjectStatsCollector::CollectGlobalStatistics() {
292   // Global FixedArrays.
293   RecordFixedArrayHelper(nullptr, heap_->weak_new_space_object_to_code_list(),
294                          WEAK_NEW_SPACE_OBJECT_TO_CODE_SUB_TYPE, 0);
295   RecordFixedArrayHelper(nullptr, heap_->serialized_templates(),
296                          SERIALIZED_TEMPLATES_SUB_TYPE, 0);
297   RecordFixedArrayHelper(nullptr, heap_->number_string_cache(),
298                          NUMBER_STRING_CACHE_SUB_TYPE, 0);
299   RecordFixedArrayHelper(nullptr, heap_->single_character_string_cache(),
300                          SINGLE_CHARACTER_STRING_CACHE_SUB_TYPE, 0);
301   RecordFixedArrayHelper(nullptr, heap_->string_split_cache(),
302                          STRING_SPLIT_CACHE_SUB_TYPE, 0);
303   RecordFixedArrayHelper(nullptr, heap_->regexp_multiple_cache(),
304                          REGEXP_MULTIPLE_CACHE_SUB_TYPE, 0);
305   RecordFixedArrayHelper(nullptr, heap_->retained_maps(),
306                          RETAINED_MAPS_SUB_TYPE, 0);
307 
308   // Global weak FixedArrays.
309   RecordFixedArrayHelper(
310       nullptr, WeakFixedArray::cast(heap_->noscript_shared_function_infos()),
311       NOSCRIPT_SHARED_FUNCTION_INFOS_SUB_TYPE, 0);
312   RecordFixedArrayHelper(nullptr, WeakFixedArray::cast(heap_->script_list()),
313                          SCRIPT_LIST_SUB_TYPE, 0);
314 
315   // Global hash tables.
316   RecordHashTableHelper(nullptr, heap_->string_table(), STRING_TABLE_SUB_TYPE);
317   RecordHashTableHelper(nullptr, heap_->weak_object_to_code_table(),
318                         OBJECT_TO_CODE_SUB_TYPE);
319   RecordHashTableHelper(nullptr, heap_->code_stubs(),
320                         CODE_STUBS_TABLE_SUB_TYPE);
321   RecordHashTableHelper(nullptr, heap_->empty_properties_dictionary(),
322                         EMPTY_PROPERTIES_DICTIONARY_SUB_TYPE);
323   CompilationCache* compilation_cache = heap_->isolate()->compilation_cache();
324   CompilationCacheTableVisitor v(this);
325   compilation_cache->Iterate(&v);
326 }
327 
CanRecordFixedArray(Heap * heap,FixedArrayBase * array)328 static bool CanRecordFixedArray(Heap* heap, FixedArrayBase* array) {
329   return array->map()->instance_type() == FIXED_ARRAY_TYPE &&
330          array->map() != heap->fixed_double_array_map() &&
331          array != heap->empty_fixed_array() &&
332          array != heap->empty_byte_array() &&
333          array != heap->empty_literals_array() &&
334          array != heap->empty_sloppy_arguments_elements() &&
335          array != heap->empty_slow_element_dictionary() &&
336          array != heap->empty_descriptor_array() &&
337          array != heap->empty_properties_dictionary();
338 }
339 
IsCowArray(Heap * heap,FixedArrayBase * array)340 static bool IsCowArray(Heap* heap, FixedArrayBase* array) {
341   return array->map() == heap->fixed_cow_array_map();
342 }
343 
SameLiveness(HeapObject * obj1,HeapObject * obj2)344 static bool SameLiveness(HeapObject* obj1, HeapObject* obj2) {
345   return obj1 == nullptr || obj2 == nullptr ||
346          ObjectMarking::Color(obj1) == ObjectMarking::Color(obj2);
347 }
348 
RecordFixedArrayHelper(HeapObject * parent,FixedArray * array,int subtype,size_t overhead)349 bool ObjectStatsCollector::RecordFixedArrayHelper(HeapObject* parent,
350                                                   FixedArray* array,
351                                                   int subtype,
352                                                   size_t overhead) {
353   if (SameLiveness(parent, array) && CanRecordFixedArray(heap_, array) &&
354       !IsCowArray(heap_, array)) {
355     return stats_->RecordFixedArraySubTypeStats(array, subtype, array->Size(),
356                                                 overhead);
357   }
358   return false;
359 }
360 
RecursivelyRecordFixedArrayHelper(HeapObject * parent,FixedArray * array,int subtype)361 void ObjectStatsCollector::RecursivelyRecordFixedArrayHelper(HeapObject* parent,
362                                                              FixedArray* array,
363                                                              int subtype) {
364   if (RecordFixedArrayHelper(parent, array, subtype, 0)) {
365     for (int i = 0; i < array->length(); i++) {
366       if (array->get(i)->IsFixedArray()) {
367         RecursivelyRecordFixedArrayHelper(
368             parent, FixedArray::cast(array->get(i)), subtype);
369       }
370     }
371   }
372 }
373 
374 template <class HashTable>
RecordHashTableHelper(HeapObject * parent,HashTable * array,int subtype)375 void ObjectStatsCollector::RecordHashTableHelper(HeapObject* parent,
376                                                  HashTable* array,
377                                                  int subtype) {
378   int used = array->NumberOfElements() * HashTable::kEntrySize * kPointerSize;
379   CHECK_GE(array->Size(), used);
380   size_t overhead = array->Size() - used -
381                     HashTable::kElementsStartIndex * kPointerSize -
382                     FixedArray::kHeaderSize;
383   RecordFixedArrayHelper(parent, array, subtype, overhead);
384 }
385 
RecordJSObjectDetails(JSObject * object)386 void ObjectStatsCollector::RecordJSObjectDetails(JSObject* object) {
387   size_t overhead = 0;
388   FixedArrayBase* elements = object->elements();
389   if (CanRecordFixedArray(heap_, elements) && !IsCowArray(heap_, elements)) {
390     if (elements->IsDictionary() && SameLiveness(object, elements)) {
391       SeededNumberDictionary* dict = SeededNumberDictionary::cast(elements);
392       RecordHashTableHelper(object, dict, DICTIONARY_ELEMENTS_SUB_TYPE);
393     } else {
394       if (IsFastHoleyElementsKind(object->GetElementsKind())) {
395         int used = object->GetFastElementsUsage() * kPointerSize;
396         if (object->GetElementsKind() == FAST_HOLEY_DOUBLE_ELEMENTS) used *= 2;
397         CHECK_GE(elements->Size(), used);
398         overhead = elements->Size() - used - FixedArray::kHeaderSize;
399       }
400       stats_->RecordFixedArraySubTypeStats(elements, FAST_ELEMENTS_SUB_TYPE,
401                                            elements->Size(), overhead);
402     }
403   }
404 
405   overhead = 0;
406   FixedArrayBase* properties = object->properties();
407   if (CanRecordFixedArray(heap_, properties) &&
408       SameLiveness(object, properties) && !IsCowArray(heap_, properties)) {
409     if (properties->IsDictionary()) {
410       NameDictionary* dict = NameDictionary::cast(properties);
411       RecordHashTableHelper(object, dict, DICTIONARY_PROPERTIES_SUB_TYPE);
412     } else {
413       stats_->RecordFixedArraySubTypeStats(properties, FAST_PROPERTIES_SUB_TYPE,
414                                            properties->Size(), overhead);
415     }
416   }
417 }
418 
RecordJSWeakCollectionDetails(JSWeakCollection * obj)419 void ObjectStatsCollector::RecordJSWeakCollectionDetails(
420     JSWeakCollection* obj) {
421   if (obj->table()->IsHashTable()) {
422     ObjectHashTable* table = ObjectHashTable::cast(obj->table());
423     int used = table->NumberOfElements() * ObjectHashTable::kEntrySize;
424     size_t overhead = table->Size() - used;
425     RecordFixedArrayHelper(obj, table, JS_WEAK_COLLECTION_SUB_TYPE, overhead);
426   }
427 }
428 
RecordJSCollectionDetails(JSObject * obj)429 void ObjectStatsCollector::RecordJSCollectionDetails(JSObject* obj) {
430   // The JS versions use a different HashTable implementation that cannot use
431   // the regular helper. Since overall impact is usually small just record
432   // without overhead.
433   if (obj->IsJSMap()) {
434     RecordFixedArrayHelper(nullptr, FixedArray::cast(JSMap::cast(obj)->table()),
435                            JS_COLLECTION_SUB_TYPE, 0);
436   }
437   if (obj->IsJSSet()) {
438     RecordFixedArrayHelper(nullptr, FixedArray::cast(JSSet::cast(obj)->table()),
439                            JS_COLLECTION_SUB_TYPE, 0);
440   }
441 }
442 
RecordScriptDetails(Script * obj)443 void ObjectStatsCollector::RecordScriptDetails(Script* obj) {
444   Object* infos = WeakFixedArray::cast(obj->shared_function_infos());
445   if (infos->IsWeakFixedArray())
446     RecordFixedArrayHelper(obj, WeakFixedArray::cast(infos),
447                            SHARED_FUNCTION_INFOS_SUB_TYPE, 0);
448 }
449 
RecordMapDetails(Map * map_obj)450 void ObjectStatsCollector::RecordMapDetails(Map* map_obj) {
451   DescriptorArray* array = map_obj->instance_descriptors();
452   if (map_obj->owns_descriptors() && array != heap_->empty_descriptor_array() &&
453       SameLiveness(map_obj, array)) {
454     RecordFixedArrayHelper(map_obj, array, DESCRIPTOR_ARRAY_SUB_TYPE, 0);
455     if (array->HasEnumCache()) {
456       RecordFixedArrayHelper(array, array->GetEnumCache(), ENUM_CACHE_SUB_TYPE,
457                              0);
458     }
459     if (array->HasEnumIndicesCache()) {
460       RecordFixedArrayHelper(array, array->GetEnumIndicesCache(),
461                              ENUM_INDICES_CACHE_SUB_TYPE, 0);
462     }
463   }
464 
465   if (map_obj->has_code_cache()) {
466     FixedArray* code_cache = map_obj->code_cache();
467     if (code_cache->IsCodeCacheHashTable()) {
468       RecordHashTableHelper(map_obj, CodeCacheHashTable::cast(code_cache),
469                             MAP_CODE_CACHE_SUB_TYPE);
470     } else {
471       RecordFixedArrayHelper(map_obj, code_cache, MAP_CODE_CACHE_SUB_TYPE, 0);
472     }
473   }
474 
475   for (DependentCode* cur_dependent_code = map_obj->dependent_code();
476        cur_dependent_code != heap_->empty_fixed_array();
477        cur_dependent_code = DependentCode::cast(
478            cur_dependent_code->get(DependentCode::kNextLinkIndex))) {
479     RecordFixedArrayHelper(map_obj, cur_dependent_code, DEPENDENT_CODE_SUB_TYPE,
480                            0);
481   }
482 
483   if (map_obj->is_prototype_map()) {
484     if (map_obj->prototype_info()->IsPrototypeInfo()) {
485       PrototypeInfo* info = PrototypeInfo::cast(map_obj->prototype_info());
486       Object* users = info->prototype_users();
487       if (users->IsWeakFixedArray()) {
488         RecordFixedArrayHelper(map_obj, WeakFixedArray::cast(users),
489                                PROTOTYPE_USERS_SUB_TYPE, 0);
490       }
491     }
492   }
493 }
494 
RecordTemplateInfoDetails(TemplateInfo * obj)495 void ObjectStatsCollector::RecordTemplateInfoDetails(TemplateInfo* obj) {
496   if (obj->property_accessors()->IsFixedArray()) {
497     RecordFixedArrayHelper(obj, FixedArray::cast(obj->property_accessors()),
498                            TEMPLATE_INFO_SUB_TYPE, 0);
499   }
500   if (obj->property_list()->IsFixedArray()) {
501     RecordFixedArrayHelper(obj, FixedArray::cast(obj->property_list()),
502                            TEMPLATE_INFO_SUB_TYPE, 0);
503   }
504 }
505 
RecordBytecodeArrayDetails(BytecodeArray * obj)506 void ObjectStatsCollector::RecordBytecodeArrayDetails(BytecodeArray* obj) {
507   RecordFixedArrayHelper(obj, obj->constant_pool(),
508                          BYTECODE_ARRAY_CONSTANT_POOL_SUB_TYPE, 0);
509   RecordFixedArrayHelper(obj, obj->handler_table(),
510                          BYTECODE_ARRAY_HANDLER_TABLE_SUB_TYPE, 0);
511 }
512 
RecordCodeDetails(Code * code)513 void ObjectStatsCollector::RecordCodeDetails(Code* code) {
514   stats_->RecordCodeSubTypeStats(code->kind(), code->GetAge(), code->Size());
515   RecordFixedArrayHelper(code, code->deoptimization_data(),
516                          DEOPTIMIZATION_DATA_SUB_TYPE, 0);
517   if (code->kind() == Code::Kind::OPTIMIZED_FUNCTION) {
518     DeoptimizationInputData* input_data =
519         DeoptimizationInputData::cast(code->deoptimization_data());
520     if (input_data->length() > 0) {
521       RecordFixedArrayHelper(code->deoptimization_data(),
522                              input_data->LiteralArray(),
523                              OPTIMIZED_CODE_LITERALS_SUB_TYPE, 0);
524     }
525   }
526   RecordFixedArrayHelper(code, code->handler_table(), HANDLER_TABLE_SUB_TYPE,
527                          0);
528   int const mode_mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT);
529   for (RelocIterator it(code, mode_mask); !it.done(); it.next()) {
530     RelocInfo::Mode mode = it.rinfo()->rmode();
531     if (mode == RelocInfo::EMBEDDED_OBJECT) {
532       Object* target = it.rinfo()->target_object();
533       if (target->IsFixedArray()) {
534         RecursivelyRecordFixedArrayHelper(code, FixedArray::cast(target),
535                                           EMBEDDED_OBJECT_SUB_TYPE);
536       }
537     }
538   }
539 }
540 
RecordSharedFunctionInfoDetails(SharedFunctionInfo * sfi)541 void ObjectStatsCollector::RecordSharedFunctionInfoDetails(
542     SharedFunctionInfo* sfi) {
543   FixedArray* scope_info = sfi->scope_info();
544   RecordFixedArrayHelper(sfi, scope_info, SCOPE_INFO_SUB_TYPE, 0);
545   TypeFeedbackMetadata* feedback_metadata = sfi->feedback_metadata();
546   if (!feedback_metadata->is_empty()) {
547     RecordFixedArrayHelper(sfi, feedback_metadata,
548                            TYPE_FEEDBACK_METADATA_SUB_TYPE, 0);
549     Object* names =
550         feedback_metadata->get(TypeFeedbackMetadata::kNamesTableIndex);
551     if (!names->IsSmi()) {
552       UnseededNumberDictionary* names = UnseededNumberDictionary::cast(
553           feedback_metadata->get(TypeFeedbackMetadata::kNamesTableIndex));
554       RecordHashTableHelper(sfi, names, TYPE_FEEDBACK_METADATA_SUB_TYPE);
555     }
556   }
557 
558   if (!sfi->OptimizedCodeMapIsCleared()) {
559     FixedArray* optimized_code_map = sfi->optimized_code_map();
560     RecordFixedArrayHelper(sfi, optimized_code_map, OPTIMIZED_CODE_MAP_SUB_TYPE,
561                            0);
562     // Optimized code map should be small, so skip accounting.
563     int len = optimized_code_map->length();
564     for (int i = SharedFunctionInfo::kEntriesStart; i < len;
565          i += SharedFunctionInfo::kEntryLength) {
566       Object* slot =
567           optimized_code_map->get(i + SharedFunctionInfo::kLiteralsOffset);
568       LiteralsArray* literals = nullptr;
569       if (slot->IsWeakCell()) {
570         WeakCell* cell = WeakCell::cast(slot);
571         if (!cell->cleared()) {
572           literals = LiteralsArray::cast(cell->value());
573         }
574       } else {
575         literals = LiteralsArray::cast(slot);
576       }
577       if (literals != nullptr) {
578         RecordFixedArrayHelper(sfi, literals, LITERALS_ARRAY_SUB_TYPE, 0);
579         RecordFixedArrayHelper(sfi, literals->feedback_vector(),
580                                TYPE_FEEDBACK_VECTOR_SUB_TYPE, 0);
581       }
582     }
583   }
584 }
585 
RecordJSFunctionDetails(JSFunction * function)586 void ObjectStatsCollector::RecordJSFunctionDetails(JSFunction* function) {
587   LiteralsArray* literals = function->literals();
588   RecordFixedArrayHelper(function, literals, LITERALS_ARRAY_SUB_TYPE, 0);
589   RecordFixedArrayHelper(function, literals->feedback_vector(),
590                          TYPE_FEEDBACK_VECTOR_SUB_TYPE, 0);
591 }
592 
RecordFixedArrayDetails(FixedArray * array)593 void ObjectStatsCollector::RecordFixedArrayDetails(FixedArray* array) {
594   if (array->IsContext()) {
595     RecordFixedArrayHelper(nullptr, array, CONTEXT_SUB_TYPE, 0);
596   }
597   if (IsCowArray(heap_, array) && CanRecordFixedArray(heap_, array)) {
598     stats_->RecordFixedArraySubTypeStats(array, COPY_ON_WRITE_SUB_TYPE,
599                                          array->Size(), 0);
600   }
601   if (array->IsNativeContext()) {
602     Context* native_ctx = Context::cast(array);
603     RecordHashTableHelper(array,
604                           native_ctx->slow_template_instantiations_cache(),
605                           SLOW_TEMPLATE_INSTANTIATIONS_CACHE_SUB_TYPE);
606     FixedArray* fast_cache = native_ctx->fast_template_instantiations_cache();
607     stats_->RecordFixedArraySubTypeStats(
608         fast_cache, FAST_TEMPLATE_INSTANTIATIONS_CACHE_SUB_TYPE,
609         fast_cache->Size(), 0);
610   }
611 }
612 
613 }  // namespace internal
614 }  // namespace v8
615