Home | History | Annotate | Download | only in heap
      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 
     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.
     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 
     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 
     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 
     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 
     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 
    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 
    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 
    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 
    238 Isolate* ObjectStats::isolate() { return heap()->isolate(); }
    239 
    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:
    274   explicit CompilationCacheTableVisitor(ObjectStatsCollector* parent)
    275       : parent_(parent) {}
    276 
    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 
    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 
    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 
    340 static bool IsCowArray(Heap* heap, FixedArrayBase* array) {
    341   return array->map() == heap->fixed_cow_array_map();
    342 }
    343 
    344 static bool SameLiveness(HeapObject* obj1, HeapObject* obj2) {
    345   return obj1 == nullptr || obj2 == nullptr ||
    346          ObjectMarking::Color(obj1) == ObjectMarking::Color(obj2);
    347 }
    348 
    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 
    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>
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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