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/assembler-inl.h"
8#include "src/compilation-cache.h"
9#include "src/counters.h"
10#include "src/heap/heap-inl.h"
11#include "src/isolate.h"
12#include "src/macro-assembler.h"
13#include "src/utils.h"
14
15namespace v8 {
16namespace internal {
17
18static base::LazyMutex object_stats_mutex = LAZY_MUTEX_INITIALIZER;
19
20
21void ObjectStats::ClearObjectStats(bool clear_last_time_stats) {
22  memset(object_counts_, 0, sizeof(object_counts_));
23  memset(object_sizes_, 0, sizeof(object_sizes_));
24  memset(over_allocated_, 0, sizeof(over_allocated_));
25  memset(size_histogram_, 0, sizeof(size_histogram_));
26  memset(over_allocated_histogram_, 0, sizeof(over_allocated_histogram_));
27  if (clear_last_time_stats) {
28    memset(object_counts_last_time_, 0, sizeof(object_counts_last_time_));
29    memset(object_sizes_last_time_, 0, sizeof(object_sizes_last_time_));
30  }
31  visited_fixed_array_sub_types_.clear();
32}
33
34// Tell the compiler to never inline this: occasionally, the optimizer will
35// decide to inline this and unroll the loop, making the compiled code more than
36// 100KB larger.
37V8_NOINLINE static void PrintJSONArray(size_t* array, const int len) {
38  PrintF("[ ");
39  for (int i = 0; i < len; i++) {
40    PrintF("%zu", array[i]);
41    if (i != (len - 1)) PrintF(", ");
42  }
43  PrintF(" ]");
44}
45
46V8_NOINLINE static void DumpJSONArray(std::stringstream& stream, size_t* array,
47                                      const int len) {
48  stream << "[";
49  for (int i = 0; i < len; i++) {
50    stream << array[i];
51    if (i != (len - 1)) stream << ",";
52  }
53  stream << "]";
54}
55
56void ObjectStats::PrintKeyAndId(const char* key, int gc_count) {
57  PrintF("\"isolate\": \"%p\", \"id\": %d, \"key\": \"%s\", ",
58         reinterpret_cast<void*>(isolate()), gc_count, key);
59}
60
61void ObjectStats::PrintInstanceTypeJSON(const char* key, int gc_count,
62                                        const char* name, int index) {
63  PrintF("{ ");
64  PrintKeyAndId(key, gc_count);
65  PrintF("\"type\": \"instance_type_data\", ");
66  PrintF("\"instance_type\": %d, ", index);
67  PrintF("\"instance_type_name\": \"%s\", ", name);
68  PrintF("\"overall\": %zu, ", object_sizes_[index]);
69  PrintF("\"count\": %zu, ", object_counts_[index]);
70  PrintF("\"over_allocated\": %zu, ", over_allocated_[index]);
71  PrintF("\"histogram\": ");
72  PrintJSONArray(size_histogram_[index], kNumberOfBuckets);
73  PrintF(",");
74  PrintF("\"over_allocated_histogram\": ");
75  PrintJSONArray(over_allocated_histogram_[index], kNumberOfBuckets);
76  PrintF(" }\n");
77}
78
79void ObjectStats::PrintJSON(const char* key) {
80  double time = isolate()->time_millis_since_init();
81  int gc_count = heap()->gc_count();
82
83  // gc_descriptor
84  PrintF("{ ");
85  PrintKeyAndId(key, gc_count);
86  PrintF("\"type\": \"gc_descriptor\", \"time\": %f }\n", time);
87  // bucket_sizes
88  PrintF("{ ");
89  PrintKeyAndId(key, gc_count);
90  PrintF("\"type\": \"bucket_sizes\", \"sizes\": [ ");
91  for (int i = 0; i < kNumberOfBuckets; i++) {
92    PrintF("%d", 1 << (kFirstBucketShift + i));
93    if (i != (kNumberOfBuckets - 1)) PrintF(", ");
94  }
95  PrintF(" ] }\n");
96
97#define INSTANCE_TYPE_WRAPPER(name) \
98  PrintInstanceTypeJSON(key, gc_count, #name, name);
99#define CODE_KIND_WRAPPER(name)                        \
100  PrintInstanceTypeJSON(key, gc_count, "*CODE_" #name, \
101                        FIRST_CODE_KIND_SUB_TYPE + Code::name);
102#define FIXED_ARRAY_SUB_INSTANCE_TYPE_WRAPPER(name)           \
103  PrintInstanceTypeJSON(key, gc_count, "*FIXED_ARRAY_" #name, \
104                        FIRST_FIXED_ARRAY_SUB_TYPE + name);
105#define CODE_AGE_WRAPPER(name)           \
106  PrintInstanceTypeJSON(                 \
107      key, gc_count, "*CODE_AGE_" #name, \
108      FIRST_CODE_AGE_SUB_TYPE + Code::k##name##CodeAge - Code::kFirstCodeAge);
109
110  INSTANCE_TYPE_LIST(INSTANCE_TYPE_WRAPPER)
111  CODE_KIND_LIST(CODE_KIND_WRAPPER)
112  FIXED_ARRAY_SUB_INSTANCE_TYPE_LIST(FIXED_ARRAY_SUB_INSTANCE_TYPE_WRAPPER)
113  CODE_AGE_LIST_COMPLETE(CODE_AGE_WRAPPER)
114
115#undef INSTANCE_TYPE_WRAPPER
116#undef CODE_KIND_WRAPPER
117#undef FIXED_ARRAY_SUB_INSTANCE_TYPE_WRAPPER
118#undef CODE_AGE_WRAPPER
119#undef PRINT_INSTANCE_TYPE_DATA
120#undef PRINT_KEY_AND_ID
121}
122
123void ObjectStats::DumpInstanceTypeData(std::stringstream& stream,
124                                       const char* name, int index) {
125  stream << "\"" << name << "\":{";
126  stream << "\"type\":" << static_cast<int>(index) << ",";
127  stream << "\"overall\":" << object_sizes_[index] << ",";
128  stream << "\"count\":" << object_counts_[index] << ",";
129  stream << "\"over_allocated\":" << over_allocated_[index] << ",";
130  stream << "\"histogram\":";
131  DumpJSONArray(stream, size_histogram_[index], kNumberOfBuckets);
132  stream << ",\"over_allocated_histogram\":";
133  DumpJSONArray(stream, over_allocated_histogram_[index], kNumberOfBuckets);
134  stream << "},";
135}
136
137void ObjectStats::Dump(std::stringstream& stream) {
138  double time = isolate()->time_millis_since_init();
139  int gc_count = heap()->gc_count();
140
141  stream << "{";
142  stream << "\"isolate\":\"" << reinterpret_cast<void*>(isolate()) << "\",";
143  stream << "\"id\":" << gc_count << ",";
144  stream << "\"time\":" << time << ",";
145  stream << "\"bucket_sizes\":[";
146  for (int i = 0; i < kNumberOfBuckets; i++) {
147    stream << (1 << (kFirstBucketShift + i));
148    if (i != (kNumberOfBuckets - 1)) stream << ",";
149  }
150  stream << "],";
151  stream << "\"type_data\":{";
152
153#define INSTANCE_TYPE_WRAPPER(name) DumpInstanceTypeData(stream, #name, name);
154#define CODE_KIND_WRAPPER(name)                \
155  DumpInstanceTypeData(stream, "*CODE_" #name, \
156                       FIRST_CODE_KIND_SUB_TYPE + Code::name);
157
158#define FIXED_ARRAY_SUB_INSTANCE_TYPE_WRAPPER(name)   \
159  DumpInstanceTypeData(stream, "*FIXED_ARRAY_" #name, \
160                       FIRST_FIXED_ARRAY_SUB_TYPE + name);
161
162#define CODE_AGE_WRAPPER(name)    \
163  DumpInstanceTypeData(           \
164      stream, "*CODE_AGE_" #name, \
165      FIRST_CODE_AGE_SUB_TYPE + Code::k##name##CodeAge - Code::kFirstCodeAge);
166
167  INSTANCE_TYPE_LIST(INSTANCE_TYPE_WRAPPER);
168  CODE_KIND_LIST(CODE_KIND_WRAPPER);
169  FIXED_ARRAY_SUB_INSTANCE_TYPE_LIST(FIXED_ARRAY_SUB_INSTANCE_TYPE_WRAPPER);
170  CODE_AGE_LIST_COMPLETE(CODE_AGE_WRAPPER);
171  stream << "\"END\":{}}}";
172
173#undef INSTANCE_TYPE_WRAPPER
174#undef CODE_KIND_WRAPPER
175#undef FIXED_ARRAY_SUB_INSTANCE_TYPE_WRAPPER
176#undef CODE_AGE_WRAPPER
177#undef PRINT_INSTANCE_TYPE_DATA
178}
179
180void ObjectStats::CheckpointObjectStats() {
181  base::LockGuard<base::Mutex> lock_guard(object_stats_mutex.Pointer());
182  Counters* counters = isolate()->counters();
183#define ADJUST_LAST_TIME_OBJECT_COUNT(name)              \
184  counters->count_of_##name()->Increment(                \
185      static_cast<int>(object_counts_[name]));           \
186  counters->count_of_##name()->Decrement(                \
187      static_cast<int>(object_counts_last_time_[name])); \
188  counters->size_of_##name()->Increment(                 \
189      static_cast<int>(object_sizes_[name]));            \
190  counters->size_of_##name()->Decrement(                 \
191      static_cast<int>(object_sizes_last_time_[name]));
192  INSTANCE_TYPE_LIST(ADJUST_LAST_TIME_OBJECT_COUNT)
193#undef ADJUST_LAST_TIME_OBJECT_COUNT
194  int index;
195#define ADJUST_LAST_TIME_OBJECT_COUNT(name)               \
196  index = FIRST_CODE_KIND_SUB_TYPE + Code::name;          \
197  counters->count_of_CODE_TYPE_##name()->Increment(       \
198      static_cast<int>(object_counts_[index]));           \
199  counters->count_of_CODE_TYPE_##name()->Decrement(       \
200      static_cast<int>(object_counts_last_time_[index])); \
201  counters->size_of_CODE_TYPE_##name()->Increment(        \
202      static_cast<int>(object_sizes_[index]));            \
203  counters->size_of_CODE_TYPE_##name()->Decrement(        \
204      static_cast<int>(object_sizes_last_time_[index]));
205  CODE_KIND_LIST(ADJUST_LAST_TIME_OBJECT_COUNT)
206#undef ADJUST_LAST_TIME_OBJECT_COUNT
207#define ADJUST_LAST_TIME_OBJECT_COUNT(name)               \
208  index = FIRST_FIXED_ARRAY_SUB_TYPE + name;              \
209  counters->count_of_FIXED_ARRAY_##name()->Increment(     \
210      static_cast<int>(object_counts_[index]));           \
211  counters->count_of_FIXED_ARRAY_##name()->Decrement(     \
212      static_cast<int>(object_counts_last_time_[index])); \
213  counters->size_of_FIXED_ARRAY_##name()->Increment(      \
214      static_cast<int>(object_sizes_[index]));            \
215  counters->size_of_FIXED_ARRAY_##name()->Decrement(      \
216      static_cast<int>(object_sizes_last_time_[index]));
217  FIXED_ARRAY_SUB_INSTANCE_TYPE_LIST(ADJUST_LAST_TIME_OBJECT_COUNT)
218#undef ADJUST_LAST_TIME_OBJECT_COUNT
219#define ADJUST_LAST_TIME_OBJECT_COUNT(name)                                   \
220  index =                                                                     \
221      FIRST_CODE_AGE_SUB_TYPE + Code::k##name##CodeAge - Code::kFirstCodeAge; \
222  counters->count_of_CODE_AGE_##name()->Increment(                            \
223      static_cast<int>(object_counts_[index]));                               \
224  counters->count_of_CODE_AGE_##name()->Decrement(                            \
225      static_cast<int>(object_counts_last_time_[index]));                     \
226  counters->size_of_CODE_AGE_##name()->Increment(                             \
227      static_cast<int>(object_sizes_[index]));                                \
228  counters->size_of_CODE_AGE_##name()->Decrement(                             \
229      static_cast<int>(object_sizes_last_time_[index]));
230  CODE_AGE_LIST_COMPLETE(ADJUST_LAST_TIME_OBJECT_COUNT)
231#undef ADJUST_LAST_TIME_OBJECT_COUNT
232
233  MemCopy(object_counts_last_time_, object_counts_, sizeof(object_counts_));
234  MemCopy(object_sizes_last_time_, object_sizes_, sizeof(object_sizes_));
235  ClearObjectStats();
236}
237
238
239Isolate* ObjectStats::isolate() { return heap()->isolate(); }
240
241void ObjectStatsCollector::CollectStatistics(HeapObject* obj) {
242  Map* map = obj->map();
243
244  // Record for the InstanceType.
245  int object_size = obj->Size();
246  stats_->RecordObjectStats(map->instance_type(), object_size);
247
248  // Record specific sub types where possible.
249  if (obj->IsMap()) RecordMapDetails(Map::cast(obj));
250  if (obj->IsObjectTemplateInfo() || obj->IsFunctionTemplateInfo()) {
251    RecordTemplateInfoDetails(TemplateInfo::cast(obj));
252  }
253  if (obj->IsBytecodeArray()) {
254    RecordBytecodeArrayDetails(BytecodeArray::cast(obj));
255  }
256  if (obj->IsCode()) RecordCodeDetails(Code::cast(obj));
257  if (obj->IsSharedFunctionInfo()) {
258    RecordSharedFunctionInfoDetails(SharedFunctionInfo::cast(obj));
259  }
260  if (obj->IsFixedArray()) RecordFixedArrayDetails(FixedArray::cast(obj));
261  if (obj->IsJSObject()) RecordJSObjectDetails(JSObject::cast(obj));
262  if (obj->IsJSWeakCollection()) {
263    RecordJSWeakCollectionDetails(JSWeakCollection::cast(obj));
264  }
265  if (obj->IsJSCollection()) {
266    RecordJSCollectionDetails(JSObject::cast(obj));
267  }
268  if (obj->IsJSFunction()) RecordJSFunctionDetails(JSFunction::cast(obj));
269  if (obj->IsScript()) RecordScriptDetails(Script::cast(obj));
270}
271
272class ObjectStatsCollector::CompilationCacheTableVisitor
273    : public ObjectVisitor {
274 public:
275  explicit CompilationCacheTableVisitor(ObjectStatsCollector* parent)
276      : parent_(parent) {}
277
278  void VisitPointers(Object** start, Object** end) override {
279    for (Object** current = start; current < end; current++) {
280      HeapObject* obj = HeapObject::cast(*current);
281      if (obj->IsUndefined(parent_->heap_->isolate())) continue;
282      CHECK(obj->IsCompilationCacheTable());
283      parent_->RecordHashTableHelper(nullptr, CompilationCacheTable::cast(obj),
284                                     COMPILATION_CACHE_TABLE_SUB_TYPE);
285    }
286  }
287
288 private:
289  ObjectStatsCollector* parent_;
290};
291
292void ObjectStatsCollector::CollectGlobalStatistics() {
293  // Global FixedArrays.
294  RecordFixedArrayHelper(nullptr, heap_->weak_new_space_object_to_code_list(),
295                         WEAK_NEW_SPACE_OBJECT_TO_CODE_SUB_TYPE, 0);
296  RecordFixedArrayHelper(nullptr, heap_->serialized_templates(),
297                         SERIALIZED_TEMPLATES_SUB_TYPE, 0);
298  RecordFixedArrayHelper(nullptr, heap_->number_string_cache(),
299                         NUMBER_STRING_CACHE_SUB_TYPE, 0);
300  RecordFixedArrayHelper(nullptr, heap_->single_character_string_cache(),
301                         SINGLE_CHARACTER_STRING_CACHE_SUB_TYPE, 0);
302  RecordFixedArrayHelper(nullptr, heap_->string_split_cache(),
303                         STRING_SPLIT_CACHE_SUB_TYPE, 0);
304  RecordFixedArrayHelper(nullptr, heap_->regexp_multiple_cache(),
305                         REGEXP_MULTIPLE_CACHE_SUB_TYPE, 0);
306  RecordFixedArrayHelper(nullptr, heap_->retained_maps(),
307                         RETAINED_MAPS_SUB_TYPE, 0);
308
309  // Global weak FixedArrays.
310  RecordFixedArrayHelper(
311      nullptr, WeakFixedArray::cast(heap_->noscript_shared_function_infos()),
312      NOSCRIPT_SHARED_FUNCTION_INFOS_SUB_TYPE, 0);
313  RecordFixedArrayHelper(nullptr, WeakFixedArray::cast(heap_->script_list()),
314                         SCRIPT_LIST_SUB_TYPE, 0);
315
316  // Global hash tables.
317  RecordHashTableHelper(nullptr, heap_->string_table(), STRING_TABLE_SUB_TYPE);
318  RecordHashTableHelper(nullptr, heap_->weak_object_to_code_table(),
319                        OBJECT_TO_CODE_SUB_TYPE);
320  RecordHashTableHelper(nullptr, heap_->code_stubs(),
321                        CODE_STUBS_TABLE_SUB_TYPE);
322  RecordHashTableHelper(nullptr, heap_->empty_properties_dictionary(),
323                        EMPTY_PROPERTIES_DICTIONARY_SUB_TYPE);
324  CompilationCache* compilation_cache = heap_->isolate()->compilation_cache();
325  CompilationCacheTableVisitor v(this);
326  compilation_cache->Iterate(&v);
327}
328
329static bool CanRecordFixedArray(Heap* heap, FixedArrayBase* array) {
330  return array->map()->instance_type() == FIXED_ARRAY_TYPE &&
331         array->map() != heap->fixed_double_array_map() &&
332         array != heap->empty_fixed_array() &&
333         array != heap->empty_byte_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
340static bool IsCowArray(Heap* heap, FixedArrayBase* array) {
341  return array->map() == heap->fixed_cow_array_map();
342}
343
344static bool SameLiveness(HeapObject* obj1, HeapObject* obj2) {
345  return obj1 == nullptr || obj2 == nullptr ||
346         ObjectMarking::Color(obj1) == ObjectMarking::Color(obj2);
347}
348
349bool 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
361void 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
374template <class HashTable>
375void 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
386void 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
419void 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
429void 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
443void ObjectStatsCollector::RecordScriptDetails(Script* obj) {
444  FixedArray* infos = FixedArray::cast(obj->shared_function_infos());
445  RecordFixedArrayHelper(obj, infos, SHARED_FUNCTION_INFOS_SUB_TYPE, 0);
446}
447
448void ObjectStatsCollector::RecordMapDetails(Map* map_obj) {
449  DescriptorArray* array = map_obj->instance_descriptors();
450  if (map_obj->owns_descriptors() && array != heap_->empty_descriptor_array() &&
451      SameLiveness(map_obj, array)) {
452    RecordFixedArrayHelper(map_obj, array, DESCRIPTOR_ARRAY_SUB_TYPE, 0);
453    if (array->HasEnumCache()) {
454      RecordFixedArrayHelper(array, array->GetEnumCache(), ENUM_CACHE_SUB_TYPE,
455                             0);
456    }
457    if (array->HasEnumIndicesCache()) {
458      RecordFixedArrayHelper(array, array->GetEnumIndicesCache(),
459                             ENUM_INDICES_CACHE_SUB_TYPE, 0);
460    }
461  }
462
463  if (map_obj->has_code_cache()) {
464    FixedArray* code_cache = map_obj->code_cache();
465    if (code_cache->IsCodeCacheHashTable()) {
466      RecordHashTableHelper(map_obj, CodeCacheHashTable::cast(code_cache),
467                            MAP_CODE_CACHE_SUB_TYPE);
468    } else {
469      RecordFixedArrayHelper(map_obj, code_cache, MAP_CODE_CACHE_SUB_TYPE, 0);
470    }
471  }
472
473  for (DependentCode* cur_dependent_code = map_obj->dependent_code();
474       cur_dependent_code != heap_->empty_fixed_array();
475       cur_dependent_code = DependentCode::cast(
476           cur_dependent_code->get(DependentCode::kNextLinkIndex))) {
477    RecordFixedArrayHelper(map_obj, cur_dependent_code, DEPENDENT_CODE_SUB_TYPE,
478                           0);
479  }
480
481  if (map_obj->is_prototype_map()) {
482    if (map_obj->prototype_info()->IsPrototypeInfo()) {
483      PrototypeInfo* info = PrototypeInfo::cast(map_obj->prototype_info());
484      Object* users = info->prototype_users();
485      if (users->IsWeakFixedArray()) {
486        RecordFixedArrayHelper(map_obj, WeakFixedArray::cast(users),
487                               PROTOTYPE_USERS_SUB_TYPE, 0);
488      }
489    }
490  }
491}
492
493void ObjectStatsCollector::RecordTemplateInfoDetails(TemplateInfo* obj) {
494  if (obj->property_accessors()->IsFixedArray()) {
495    RecordFixedArrayHelper(obj, FixedArray::cast(obj->property_accessors()),
496                           TEMPLATE_INFO_SUB_TYPE, 0);
497  }
498  if (obj->property_list()->IsFixedArray()) {
499    RecordFixedArrayHelper(obj, FixedArray::cast(obj->property_list()),
500                           TEMPLATE_INFO_SUB_TYPE, 0);
501  }
502}
503
504void ObjectStatsCollector::RecordBytecodeArrayDetails(BytecodeArray* obj) {
505  RecordFixedArrayHelper(obj, obj->constant_pool(),
506                         BYTECODE_ARRAY_CONSTANT_POOL_SUB_TYPE, 0);
507  RecordFixedArrayHelper(obj, obj->handler_table(),
508                         BYTECODE_ARRAY_HANDLER_TABLE_SUB_TYPE, 0);
509}
510
511void ObjectStatsCollector::RecordCodeDetails(Code* code) {
512  stats_->RecordCodeSubTypeStats(code->kind(), code->GetAge(), code->Size());
513  RecordFixedArrayHelper(code, code->deoptimization_data(),
514                         DEOPTIMIZATION_DATA_SUB_TYPE, 0);
515  if (code->kind() == Code::Kind::OPTIMIZED_FUNCTION) {
516    DeoptimizationInputData* input_data =
517        DeoptimizationInputData::cast(code->deoptimization_data());
518    if (input_data->length() > 0) {
519      RecordFixedArrayHelper(code->deoptimization_data(),
520                             input_data->LiteralArray(),
521                             OPTIMIZED_CODE_LITERALS_SUB_TYPE, 0);
522    }
523  }
524  RecordFixedArrayHelper(code, code->handler_table(), HANDLER_TABLE_SUB_TYPE,
525                         0);
526  int const mode_mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT);
527  for (RelocIterator it(code, mode_mask); !it.done(); it.next()) {
528    RelocInfo::Mode mode = it.rinfo()->rmode();
529    if (mode == RelocInfo::EMBEDDED_OBJECT) {
530      Object* target = it.rinfo()->target_object();
531      if (target->IsFixedArray()) {
532        RecursivelyRecordFixedArrayHelper(code, FixedArray::cast(target),
533                                          EMBEDDED_OBJECT_SUB_TYPE);
534      }
535    }
536  }
537}
538
539void ObjectStatsCollector::RecordSharedFunctionInfoDetails(
540    SharedFunctionInfo* sfi) {
541  FixedArray* scope_info = sfi->scope_info();
542  RecordFixedArrayHelper(sfi, scope_info, SCOPE_INFO_SUB_TYPE, 0);
543  FeedbackMetadata* feedback_metadata = sfi->feedback_metadata();
544  if (!feedback_metadata->is_empty()) {
545    RecordFixedArrayHelper(sfi, feedback_metadata, FEEDBACK_METADATA_SUB_TYPE,
546                           0);
547  }
548
549  if (!sfi->OptimizedCodeMapIsCleared()) {
550    FixedArray* optimized_code_map = sfi->optimized_code_map();
551    RecordFixedArrayHelper(sfi, optimized_code_map, OPTIMIZED_CODE_MAP_SUB_TYPE,
552                           0);
553    // Optimized code map should be small, so skip accounting.
554  }
555}
556
557void ObjectStatsCollector::RecordJSFunctionDetails(JSFunction* function) {
558  if (function->feedback_vector_cell()->value()->IsFeedbackVector()) {
559    FeedbackVector* feedback_vector = function->feedback_vector();
560    RecordFixedArrayHelper(function, feedback_vector, FEEDBACK_VECTOR_SUB_TYPE,
561                           0);
562  }
563}
564
565void ObjectStatsCollector::RecordFixedArrayDetails(FixedArray* array) {
566  if (array->IsContext()) {
567    RecordFixedArrayHelper(nullptr, array, CONTEXT_SUB_TYPE, 0);
568  }
569  if (IsCowArray(heap_, array) && CanRecordFixedArray(heap_, array)) {
570    stats_->RecordFixedArraySubTypeStats(array, COPY_ON_WRITE_SUB_TYPE,
571                                         array->Size(), 0);
572  }
573  if (array->IsNativeContext()) {
574    Context* native_ctx = Context::cast(array);
575    RecordHashTableHelper(array,
576                          native_ctx->slow_template_instantiations_cache(),
577                          SLOW_TEMPLATE_INSTANTIATIONS_CACHE_SUB_TYPE);
578    FixedArray* fast_cache = native_ctx->fast_template_instantiations_cache();
579    stats_->RecordFixedArraySubTypeStats(
580        fast_cache, FAST_TEMPLATE_INSTANTIATIONS_CACHE_SUB_TYPE,
581        fast_cache->Size(), 0);
582  }
583}
584
585}  // namespace internal
586}  // namespace v8
587