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