| OLD | NEW |
| 1 // Copyright 2015 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "src/heap/object-stats.h" | 5 #include "src/heap/object-stats.h" |
| 6 | 6 |
| 7 #include "src/counters.h" | 7 #include "src/counters.h" |
| 8 #include "src/heap/heap-inl.h" | 8 #include "src/heap/heap-inl.h" |
| 9 #include "src/isolate.h" | 9 #include "src/isolate.h" |
| 10 #include "src/utils.h" | 10 #include "src/utils.h" |
| 11 | 11 |
| 12 namespace v8 { | 12 namespace v8 { |
| 13 namespace internal { | 13 namespace internal { |
| 14 | 14 |
| 15 static base::LazyMutex object_stats_mutex = LAZY_MUTEX_INITIALIZER; | 15 static base::LazyMutex object_stats_mutex = LAZY_MUTEX_INITIALIZER; |
| 16 | 16 |
| 17 | 17 |
| 18 void ObjectStats::ClearObjectStats(bool clear_last_time_stats) { | 18 void ObjectStats::ClearObjectStats(bool clear_last_time_stats) { |
| 19 memset(object_counts_, 0, sizeof(object_counts_)); | 19 memset(object_counts_, 0, sizeof(object_counts_)); |
| 20 memset(object_sizes_, 0, sizeof(object_sizes_)); | 20 memset(object_sizes_, 0, sizeof(object_sizes_)); |
| 21 memset(over_allocated_, 0, sizeof(over_allocated_)); | 21 memset(over_allocated_, 0, sizeof(over_allocated_)); |
| 22 memset(size_histogram_, 0, sizeof(size_histogram_)); | 22 memset(size_histogram_, 0, sizeof(size_histogram_)); |
| 23 memset(over_allocated_histogram_, 0, sizeof(over_allocated_histogram_)); | 23 memset(over_allocated_histogram_, 0, sizeof(over_allocated_histogram_)); |
| 24 if (clear_last_time_stats) { | 24 if (clear_last_time_stats) { |
| 25 memset(object_counts_last_time_, 0, sizeof(object_counts_last_time_)); | 25 memset(object_counts_last_time_, 0, sizeof(object_counts_last_time_)); |
| 26 memset(object_sizes_last_time_, 0, sizeof(object_sizes_last_time_)); | 26 memset(object_sizes_last_time_, 0, sizeof(object_sizes_last_time_)); |
| 27 } | 27 } |
| 28 visited_fixed_array_sub_types_.clear(); |
| 28 } | 29 } |
| 29 | 30 |
| 30 static void PrintJSONArray(size_t* array, const int len) { | 31 static void PrintJSONArray(size_t* array, const int len) { |
| 31 PrintF("[ "); | 32 PrintF("[ "); |
| 32 for (int i = 0; i < len; i++) { | 33 for (int i = 0; i < len; i++) { |
| 33 PrintF("%zu", array[i]); | 34 PrintF("%zu", array[i]); |
| 34 if (i != (len - 1)) PrintF(", "); | 35 if (i != (len - 1)) PrintF(", "); |
| 35 } | 36 } |
| 36 PrintF(" ]"); | 37 PrintF(" ]"); |
| 37 } | 38 } |
| (...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 183 } | 184 } |
| 184 if (obj->IsJSObject()) { | 185 if (obj->IsJSObject()) { |
| 185 RecordJSObjectDetails(stats, heap, JSObject::cast(obj)); | 186 RecordJSObjectDetails(stats, heap, JSObject::cast(obj)); |
| 186 } | 187 } |
| 187 if (obj->IsJSWeakCollection()) { | 188 if (obj->IsJSWeakCollection()) { |
| 188 RecordJSWeakCollectionDetails(stats, heap, JSWeakCollection::cast(obj)); | 189 RecordJSWeakCollectionDetails(stats, heap, JSWeakCollection::cast(obj)); |
| 189 } | 190 } |
| 190 } | 191 } |
| 191 | 192 |
| 192 static bool CanRecordFixedArray(Heap* heap, FixedArrayBase* array) { | 193 static bool CanRecordFixedArray(Heap* heap, FixedArrayBase* array) { |
| 193 return array->map() != heap->fixed_cow_array_map() && | 194 return array->map()->instance_type() == FIXED_ARRAY_TYPE && |
| 195 array->map() != heap->fixed_cow_array_map() && |
| 194 array->map() != heap->fixed_double_array_map() && | 196 array->map() != heap->fixed_double_array_map() && |
| 195 array != heap->empty_fixed_array(); | 197 array != heap->empty_fixed_array() && |
| 198 array != heap->empty_byte_array() && |
| 199 array != heap->empty_literals_array() && |
| 200 array != heap->empty_sloppy_arguments_elements() && |
| 201 array != heap->empty_slow_element_dictionary() && |
| 202 array != heap->empty_descriptor_array() && |
| 203 array != heap->empty_properties_dictionary(); |
| 204 } |
| 205 |
| 206 static bool SameLiveness(HeapObject* obj1, HeapObject* obj2) { |
| 207 return ObjectMarking::Color(obj1) == ObjectMarking::Color(obj2); |
| 208 } |
| 209 |
| 210 void ObjectStatsCollector::RecordFixedArrayHelper( |
| 211 ObjectStats* stats, Heap* heap, HeapObject* parent, FixedArray* array, |
| 212 int subtype, size_t overhead) { |
| 213 if (SameLiveness(parent, array) && CanRecordFixedArray(heap, array)) { |
| 214 stats->RecordFixedArraySubTypeStats(array, subtype, array->Size(), |
| 215 overhead); |
| 216 } |
| 196 } | 217 } |
| 197 | 218 |
| 198 void ObjectStatsCollector::RecordJSObjectDetails(ObjectStats* stats, Heap* heap, | 219 void ObjectStatsCollector::RecordJSObjectDetails(ObjectStats* stats, Heap* heap, |
| 199 JSObject* object) { | 220 JSObject* object) { |
| 200 DCHECK(object->IsJSObject()); | 221 DCHECK(object->IsJSObject()); |
| 201 | 222 |
| 202 size_t overhead = 0; | 223 size_t overhead = 0; |
| 203 FixedArrayBase* elements = object->elements(); | 224 FixedArrayBase* elements = object->elements(); |
| 204 if (CanRecordFixedArray(heap, elements)) { | 225 if (CanRecordFixedArray(heap, elements)) { |
| 205 if (elements->IsDictionary()) { | 226 if (elements->IsDictionary() && SameLiveness(object, elements)) { |
| 206 SeededNumberDictionary* dict = object->element_dictionary(); | 227 SeededNumberDictionary* dict = SeededNumberDictionary::cast(elements); |
| 207 int used = dict->NumberOfElements() * SeededNumberDictionary::kEntrySize; | 228 int used = dict->NumberOfElements() * SeededNumberDictionary::kEntrySize; |
| 208 CHECK_GE(elements->Size(), used); | 229 CHECK_GE(elements->Size(), used); |
| 209 overhead = elements->Size() - used; | 230 overhead = elements->Size() - used; |
| 210 stats->RecordFixedArraySubTypeStats(DICTIONARY_ELEMENTS_SUB_TYPE, | 231 stats->RecordFixedArraySubTypeStats( |
| 211 elements->Size(), overhead); | 232 elements, DICTIONARY_ELEMENTS_SUB_TYPE, elements->Size(), overhead); |
| 212 } else { | 233 } else { |
| 213 if (IsFastHoleyElementsKind(object->GetElementsKind())) { | 234 if (IsFastHoleyElementsKind(object->GetElementsKind())) { |
| 214 int used = object->GetFastElementsUsage() * kPointerSize; | 235 int used = object->GetFastElementsUsage() * kPointerSize; |
| 215 if (object->GetElementsKind() == FAST_HOLEY_DOUBLE_ELEMENTS) used *= 2; | 236 if (object->GetElementsKind() == FAST_HOLEY_DOUBLE_ELEMENTS) used *= 2; |
| 216 CHECK_GE(elements->Size(), used); | 237 CHECK_GE(elements->Size(), used); |
| 217 overhead = elements->Size() - used; | 238 overhead = elements->Size() - used; |
| 218 } | 239 } |
| 219 stats->RecordFixedArraySubTypeStats(FAST_ELEMENTS_SUB_TYPE, | 240 stats->RecordFixedArraySubTypeStats(elements, FAST_ELEMENTS_SUB_TYPE, |
| 220 elements->Size(), overhead); | 241 elements->Size(), overhead); |
| 221 } | 242 } |
| 222 } | 243 } |
| 223 | 244 |
| 224 overhead = 0; | 245 overhead = 0; |
| 225 FixedArrayBase* properties = object->properties(); | 246 FixedArrayBase* properties = object->properties(); |
| 226 if (CanRecordFixedArray(heap, properties)) { | 247 if (CanRecordFixedArray(heap, properties) && |
| 248 SameLiveness(object, properties)) { |
| 227 if (properties->IsDictionary()) { | 249 if (properties->IsDictionary()) { |
| 228 NameDictionary* dict = object->property_dictionary(); | 250 NameDictionary* dict = NameDictionary::cast(properties); |
| 229 int used = dict->NumberOfElements() * NameDictionary::kEntrySize; | 251 int used = dict->NumberOfElements() * NameDictionary::kEntrySize; |
| 230 CHECK_GE(properties->Size(), used); | 252 CHECK_GE(properties->Size(), used); |
| 231 overhead = properties->Size() - used; | 253 overhead = properties->Size() - used; |
| 232 stats->RecordFixedArraySubTypeStats(DICTIONARY_PROPERTIES_SUB_TYPE, | 254 stats->RecordFixedArraySubTypeStats(properties, |
| 255 DICTIONARY_PROPERTIES_SUB_TYPE, |
| 233 properties->Size(), overhead); | 256 properties->Size(), overhead); |
| 234 } else { | 257 } else { |
| 235 stats->RecordFixedArraySubTypeStats(FAST_PROPERTIES_SUB_TYPE, | 258 stats->RecordFixedArraySubTypeStats(properties, FAST_PROPERTIES_SUB_TYPE, |
| 236 properties->Size(), overhead); | 259 properties->Size(), overhead); |
| 237 } | 260 } |
| 238 } | 261 } |
| 239 } | 262 } |
| 240 | 263 |
| 241 void ObjectStatsCollector::RecordJSWeakCollectionDetails( | 264 void ObjectStatsCollector::RecordJSWeakCollectionDetails( |
| 242 ObjectStats* stats, Heap* heap, JSWeakCollection* obj) { | 265 ObjectStats* stats, Heap* heap, JSWeakCollection* obj) { |
| 243 if (obj->table()->IsHashTable()) { | 266 if (obj->table()->IsHashTable()) { |
| 244 ObjectHashTable* table = ObjectHashTable::cast(obj->table()); | 267 ObjectHashTable* table = ObjectHashTable::cast(obj->table()); |
| 245 int used = table->NumberOfElements() * ObjectHashTable::kEntrySize; | 268 int used = table->NumberOfElements() * ObjectHashTable::kEntrySize; |
| 246 size_t overhead = table->Size() - used; | 269 size_t overhead = table->Size() - used; |
| 247 stats->RecordFixedArraySubTypeStats(WEAK_COLLECTION_SUB_TYPE, table->Size(), | 270 RecordFixedArrayHelper(stats, heap, obj, table, WEAK_COLLECTION_SUB_TYPE, |
| 248 overhead); | 271 overhead); |
| 249 } | 272 } |
| 250 } | 273 } |
| 251 | 274 |
| 252 void ObjectStatsCollector::RecordMapDetails(ObjectStats* stats, Heap* heap, | 275 void ObjectStatsCollector::RecordMapDetails(ObjectStats* stats, Heap* heap, |
| 253 HeapObject* obj) { | 276 HeapObject* obj) { |
| 254 Map* map_obj = Map::cast(obj); | 277 Map* map_obj = Map::cast(obj); |
| 255 DCHECK(obj->map()->instance_type() == MAP_TYPE); | 278 DCHECK(obj->map()->instance_type() == MAP_TYPE); |
| 256 DescriptorArray* array = map_obj->instance_descriptors(); | 279 DescriptorArray* array = map_obj->instance_descriptors(); |
| 257 if (map_obj->owns_descriptors() && array != heap->empty_descriptor_array()) { | 280 if (map_obj->owns_descriptors() && array != heap->empty_descriptor_array() && |
| 258 int fixed_array_size = array->Size(); | 281 SameLiveness(map_obj, array)) { |
| 259 stats->RecordFixedArraySubTypeStats(DESCRIPTOR_ARRAY_SUB_TYPE, | 282 RecordFixedArrayHelper(stats, heap, map_obj, array, |
| 260 fixed_array_size, 0); | 283 DESCRIPTOR_ARRAY_SUB_TYPE, 0); |
| 261 if (array->HasEnumCache()) { | 284 if (array->HasEnumCache()) { |
| 262 stats->RecordFixedArraySubTypeStats(ENUM_CACHE_SUB_TYPE, | 285 RecordFixedArrayHelper(stats, heap, array, array->GetEnumCache(), |
| 263 array->GetEnumCache()->Size(), 0); | 286 ENUM_CACHE_SUB_TYPE, 0); |
| 264 } | 287 } |
| 265 if (array->HasEnumIndicesCache()) { | 288 if (array->HasEnumIndicesCache()) { |
| 266 stats->RecordFixedArraySubTypeStats( | 289 RecordFixedArrayHelper(stats, heap, array, array->GetEnumIndicesCache(), |
| 267 ENUM_INDICES_CACHE_SUB_TYPE, array->GetEnumIndicesCache()->Size(), 0); | 290 ENUM_INDICES_CACHE_SUB_TYPE, 0); |
| 268 } | 291 } |
| 269 } | 292 } |
| 270 | 293 |
| 271 if (map_obj->has_code_cache()) { | 294 if (map_obj->has_code_cache()) { |
| 272 FixedArray* cache = map_obj->code_cache(); | 295 RecordFixedArrayHelper(stats, heap, map_obj, map_obj->code_cache(), |
| 273 stats->RecordFixedArraySubTypeStats(MAP_CODE_CACHE_SUB_TYPE, cache->Size(), | 296 MAP_CODE_CACHE_SUB_TYPE, 0); |
| 274 0); | |
| 275 } | 297 } |
| 276 } | 298 } |
| 277 | 299 |
| 278 void ObjectStatsCollector::RecordCodeDetails(ObjectStats* stats, Heap* heap, | 300 void ObjectStatsCollector::RecordCodeDetails(ObjectStats* stats, Heap* heap, |
| 279 HeapObject* obj) { | 301 HeapObject* obj) { |
| 280 int object_size = obj->Size(); | 302 int object_size = obj->Size(); |
| 281 DCHECK(obj->map()->instance_type() == CODE_TYPE); | 303 DCHECK(obj->map()->instance_type() == CODE_TYPE); |
| 282 Code* code_obj = Code::cast(obj); | 304 Code* code_obj = Code::cast(obj); |
| 283 stats->RecordCodeSubTypeStats(code_obj->kind(), code_obj->GetAge(), | 305 stats->RecordCodeSubTypeStats(code_obj->kind(), code_obj->GetAge(), |
| 284 object_size); | 306 object_size); |
| 285 Code* code = Code::cast(obj); | 307 Code* code = Code::cast(obj); |
| 286 if (code->deoptimization_data() != heap->empty_fixed_array()) { | 308 RecordFixedArrayHelper(stats, heap, code, code->deoptimization_data(), |
| 287 stats->RecordFixedArraySubTypeStats(DEOPTIMIZATION_DATA_SUB_TYPE, | 309 DEOPTIMIZATION_DATA_SUB_TYPE, 0); |
| 288 code->deoptimization_data()->Size(), 0); | |
| 289 } | |
| 290 FixedArrayBase* reloc_info = | |
| 291 reinterpret_cast<FixedArrayBase*>(code->unchecked_relocation_info()); | |
| 292 if (reloc_info != heap->empty_fixed_array()) { | |
| 293 stats->RecordFixedArraySubTypeStats(RELOC_INFO_SUB_TYPE, | |
| 294 code->relocation_info()->Size(), 0); | |
| 295 } | |
| 296 FixedArrayBase* source_pos_table = | |
| 297 reinterpret_cast<FixedArrayBase*>(code->source_position_table()); | |
| 298 if (source_pos_table != heap->empty_fixed_array()) { | |
| 299 stats->RecordFixedArraySubTypeStats(SOURCE_POS_SUB_TYPE, | |
| 300 source_pos_table->Size(), 0); | |
| 301 } | |
| 302 } | 310 } |
| 303 | 311 |
| 304 void ObjectStatsCollector::RecordSharedFunctionInfoDetails(ObjectStats* stats, | 312 void ObjectStatsCollector::RecordSharedFunctionInfoDetails(ObjectStats* stats, |
| 305 Heap* heap, | 313 Heap* heap, |
| 306 HeapObject* obj) { | 314 HeapObject* obj) { |
| 307 SharedFunctionInfo* sfi = SharedFunctionInfo::cast(obj); | 315 SharedFunctionInfo* sfi = SharedFunctionInfo::cast(obj); |
| 308 if (sfi->scope_info() != heap->empty_fixed_array()) { | 316 FixedArray* scope_info = sfi->scope_info(); |
| 309 stats->RecordFixedArraySubTypeStats(SCOPE_INFO_SUB_TYPE, | 317 RecordFixedArrayHelper(stats, heap, sfi, scope_info, SCOPE_INFO_SUB_TYPE, 0); |
| 310 sfi->scope_info()->Size(), 0); | 318 FixedArray* feedback_metadata = sfi->feedback_metadata(); |
| 311 } | 319 RecordFixedArrayHelper(stats, heap, sfi, feedback_metadata, |
| 312 if (sfi->feedback_metadata() != heap->empty_fixed_array()) { | 320 TYPE_FEEDBACK_METADATA_SUB_TYPE, 0); |
| 313 stats->RecordFixedArraySubTypeStats(TYPE_FEEDBACK_METADATA_SUB_TYPE, | 321 |
| 314 sfi->feedback_metadata()->Size(), 0); | |
| 315 } | |
| 316 if (!sfi->OptimizedCodeMapIsCleared()) { | 322 if (!sfi->OptimizedCodeMapIsCleared()) { |
| 317 FixedArray* optimized_code_map = sfi->optimized_code_map(); | 323 FixedArray* optimized_code_map = sfi->optimized_code_map(); |
| 318 // Optimized code map should be small, so skip accounting. | 324 // Optimized code map should be small, so skip accounting. |
| 319 int len = optimized_code_map->length(); | 325 int len = optimized_code_map->length(); |
| 320 for (int i = SharedFunctionInfo::kEntriesStart; i < len; | 326 for (int i = SharedFunctionInfo::kEntriesStart; i < len; |
| 321 i += SharedFunctionInfo::kEntryLength) { | 327 i += SharedFunctionInfo::kEntryLength) { |
| 322 Object* slot = | 328 Object* slot = |
| 323 optimized_code_map->get(i + SharedFunctionInfo::kLiteralsOffset); | 329 optimized_code_map->get(i + SharedFunctionInfo::kLiteralsOffset); |
| 324 LiteralsArray* literals = nullptr; | 330 LiteralsArray* literals = nullptr; |
| 325 if (slot->IsWeakCell()) { | 331 if (slot->IsWeakCell()) { |
| 326 WeakCell* cell = WeakCell::cast(slot); | 332 WeakCell* cell = WeakCell::cast(slot); |
| 327 if (!cell->cleared()) { | 333 if (!cell->cleared()) { |
| 328 literals = LiteralsArray::cast(cell->value()); | 334 literals = LiteralsArray::cast(cell->value()); |
| 329 } | 335 } |
| 330 } else { | 336 } else { |
| 331 literals = LiteralsArray::cast(slot); | 337 literals = LiteralsArray::cast(slot); |
| 332 } | 338 } |
| 333 if (literals != nullptr) { | 339 if (literals != nullptr) { |
| 334 stats->RecordFixedArraySubTypeStats(LITERALS_ARRAY_SUB_TYPE, | 340 RecordFixedArrayHelper(stats, heap, sfi, literals, |
| 335 literals->Size(), 0); | 341 LITERALS_ARRAY_SUB_TYPE, 0); |
| 336 TypeFeedbackVector* tfv = literals->feedback_vector(); | 342 RecordFixedArrayHelper(stats, heap, sfi, literals->feedback_vector(), |
| 337 | 343 TYPE_FEEDBACK_VECTOR_SUB_TYPE, 0); |
| 338 stats->RecordFixedArraySubTypeStats(TYPE_FEEDBACK_VECTOR_SUB_TYPE, | |
| 339 tfv->Size(), 0); | |
| 340 } | 344 } |
| 341 } | 345 } |
| 342 } | 346 } |
| 343 } | 347 } |
| 344 | 348 |
| 345 void ObjectStatsCollector::RecordFixedArrayDetails(ObjectStats* stats, | 349 void ObjectStatsCollector::RecordFixedArrayDetails(ObjectStats* stats, |
| 346 Heap* heap, | 350 Heap* heap, |
| 347 HeapObject* obj) { | 351 HeapObject* obj) { |
| 348 FixedArray* fixed_array = FixedArray::cast(obj); | 352 FixedArray* fixed_array = FixedArray::cast(obj); |
| 349 if (fixed_array == heap->string_table()) { | |
| 350 stats->RecordFixedArraySubTypeStats(STRING_TABLE_SUB_TYPE, | |
| 351 fixed_array->Size(), 0); | |
| 352 } | |
| 353 if (fixed_array == heap->weak_object_to_code_table()) { | 353 if (fixed_array == heap->weak_object_to_code_table()) { |
| 354 WeakHashTable* table = reinterpret_cast<WeakHashTable*>(fixed_array); | 354 WeakHashTable* table = reinterpret_cast<WeakHashTable*>(fixed_array); |
| 355 int used = table->NumberOfElements() * WeakHashTable::kEntrySize; | 355 int used = table->NumberOfElements() * WeakHashTable::kEntrySize; |
| 356 CHECK_GE(fixed_array->Size(), used); | 356 CHECK_GE(fixed_array->Size(), used); |
| 357 size_t overhead = fixed_array->Size() - used; | 357 size_t overhead = fixed_array->Size() - used; |
| 358 stats->RecordFixedArraySubTypeStats(OBJECT_TO_CODE_SUB_TYPE, | 358 stats->RecordFixedArraySubTypeStats(table, OBJECT_TO_CODE_SUB_TYPE, |
| 359 fixed_array->Size(), overhead); | 359 fixed_array->Size(), overhead); |
| 360 } | 360 } |
| 361 if (obj->IsContext()) { | 361 |
| 362 stats->RecordFixedArraySubTypeStats(CONTEXT_SUB_TYPE, fixed_array->Size(), | 362 int subtype = -1; |
| 363 0); | 363 if (fixed_array == heap->string_table()) subtype = STRING_TABLE_SUB_TYPE; |
| 364 } | 364 if (fixed_array->IsContext()) subtype = CONTEXT_SUB_TYPE; |
| 365 if (fixed_array->map() == heap->fixed_cow_array_map()) { | 365 if (fixed_array->map() == heap->fixed_cow_array_map()) |
| 366 stats->RecordFixedArraySubTypeStats(COPY_ON_WRITE_SUB_TYPE, | 366 subtype = COPY_ON_WRITE_SUB_TYPE; |
| 367 if (subtype != -1) { |
| 368 stats->RecordFixedArraySubTypeStats(fixed_array, subtype, |
| 367 fixed_array->Size(), 0); | 369 fixed_array->Size(), 0); |
| 368 } | 370 } |
| 369 } | 371 } |
| 370 | 372 |
| 371 } // namespace internal | 373 } // namespace internal |
| 372 } // namespace v8 | 374 } // namespace v8 |
| OLD | NEW |