// Copyright 2015 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/heap/object-stats.h" #include "src/counters.h" #include "src/heap/heap-inl.h" #include "src/isolate.h" #include "src/utils.h" namespace v8 { namespace internal { static base::LazyMutex object_stats_mutex = LAZY_MUTEX_INITIALIZER; void ObjectStats::ClearObjectStats(bool clear_last_time_stats) { memset(object_counts_, 0, sizeof(object_counts_)); memset(object_sizes_, 0, sizeof(object_sizes_)); if (clear_last_time_stats) { memset(object_counts_last_time_, 0, sizeof(object_counts_last_time_)); memset(object_sizes_last_time_, 0, sizeof(object_sizes_last_time_)); } } void ObjectStats::TraceObjectStat(const char* name, int count, int size, double time) { int ms_count = heap()->ms_count(); PrintIsolate(isolate(), "heap:%p, time:%f, gc:%d, type:%s, count:%d, size:%d\n", static_cast(heap()), time, ms_count, name, count, size); } void ObjectStats::TraceObjectStats() { base::LockGuard lock_guard(object_stats_mutex.Pointer()); int index; int count; int size; int total_size = 0; double time = isolate()->time_millis_since_init(); #define TRACE_OBJECT_COUNT(name) \ count = static_cast(object_counts_[name]); \ size = static_cast(object_sizes_[name]) / KB; \ total_size += size; \ TraceObjectStat(#name, count, size, time); INSTANCE_TYPE_LIST(TRACE_OBJECT_COUNT) #undef TRACE_OBJECT_COUNT #define TRACE_OBJECT_COUNT(name) \ index = FIRST_CODE_KIND_SUB_TYPE + Code::name; \ count = static_cast(object_counts_[index]); \ size = static_cast(object_sizes_[index]) / KB; \ TraceObjectStat("*CODE_" #name, count, size, time); CODE_KIND_LIST(TRACE_OBJECT_COUNT) #undef TRACE_OBJECT_COUNT #define TRACE_OBJECT_COUNT(name) \ index = FIRST_FIXED_ARRAY_SUB_TYPE + name; \ count = static_cast(object_counts_[index]); \ size = static_cast(object_sizes_[index]) / KB; \ TraceObjectStat("*FIXED_ARRAY_" #name, count, size, time); FIXED_ARRAY_SUB_INSTANCE_TYPE_LIST(TRACE_OBJECT_COUNT) #undef TRACE_OBJECT_COUNT #define TRACE_OBJECT_COUNT(name) \ index = \ FIRST_CODE_AGE_SUB_TYPE + Code::k##name##CodeAge - Code::kFirstCodeAge; \ count = static_cast(object_counts_[index]); \ size = static_cast(object_sizes_[index]) / KB; \ TraceObjectStat("*CODE_AGE_" #name, count, size, time); CODE_AGE_LIST_COMPLETE(TRACE_OBJECT_COUNT) #undef TRACE_OBJECT_COUNT } void ObjectStats::CheckpointObjectStats() { base::LockGuard lock_guard(object_stats_mutex.Pointer()); Counters* counters = isolate()->counters(); #define ADJUST_LAST_TIME_OBJECT_COUNT(name) \ counters->count_of_##name()->Increment( \ static_cast(object_counts_[name])); \ counters->count_of_##name()->Decrement( \ static_cast(object_counts_last_time_[name])); \ counters->size_of_##name()->Increment( \ static_cast(object_sizes_[name])); \ counters->size_of_##name()->Decrement( \ static_cast(object_sizes_last_time_[name])); INSTANCE_TYPE_LIST(ADJUST_LAST_TIME_OBJECT_COUNT) #undef ADJUST_LAST_TIME_OBJECT_COUNT int index; #define ADJUST_LAST_TIME_OBJECT_COUNT(name) \ index = FIRST_CODE_KIND_SUB_TYPE + Code::name; \ counters->count_of_CODE_TYPE_##name()->Increment( \ static_cast(object_counts_[index])); \ counters->count_of_CODE_TYPE_##name()->Decrement( \ static_cast(object_counts_last_time_[index])); \ counters->size_of_CODE_TYPE_##name()->Increment( \ static_cast(object_sizes_[index])); \ counters->size_of_CODE_TYPE_##name()->Decrement( \ static_cast(object_sizes_last_time_[index])); CODE_KIND_LIST(ADJUST_LAST_TIME_OBJECT_COUNT) #undef ADJUST_LAST_TIME_OBJECT_COUNT #define ADJUST_LAST_TIME_OBJECT_COUNT(name) \ index = FIRST_FIXED_ARRAY_SUB_TYPE + name; \ counters->count_of_FIXED_ARRAY_##name()->Increment( \ static_cast(object_counts_[index])); \ counters->count_of_FIXED_ARRAY_##name()->Decrement( \ static_cast(object_counts_last_time_[index])); \ counters->size_of_FIXED_ARRAY_##name()->Increment( \ static_cast(object_sizes_[index])); \ counters->size_of_FIXED_ARRAY_##name()->Decrement( \ static_cast(object_sizes_last_time_[index])); FIXED_ARRAY_SUB_INSTANCE_TYPE_LIST(ADJUST_LAST_TIME_OBJECT_COUNT) #undef ADJUST_LAST_TIME_OBJECT_COUNT #define ADJUST_LAST_TIME_OBJECT_COUNT(name) \ index = \ FIRST_CODE_AGE_SUB_TYPE + Code::k##name##CodeAge - Code::kFirstCodeAge; \ counters->count_of_CODE_AGE_##name()->Increment( \ static_cast(object_counts_[index])); \ counters->count_of_CODE_AGE_##name()->Decrement( \ static_cast(object_counts_last_time_[index])); \ counters->size_of_CODE_AGE_##name()->Increment( \ static_cast(object_sizes_[index])); \ counters->size_of_CODE_AGE_##name()->Decrement( \ static_cast(object_sizes_last_time_[index])); CODE_AGE_LIST_COMPLETE(ADJUST_LAST_TIME_OBJECT_COUNT) #undef ADJUST_LAST_TIME_OBJECT_COUNT MemCopy(object_counts_last_time_, object_counts_, sizeof(object_counts_)); MemCopy(object_sizes_last_time_, object_sizes_, sizeof(object_sizes_)); ClearObjectStats(); } Isolate* ObjectStats::isolate() { return heap()->isolate(); } void ObjectStatsVisitor::CountFixedArray( FixedArrayBase* fixed_array, FixedArraySubInstanceType fast_type, FixedArraySubInstanceType dictionary_type) { Heap* heap = fixed_array->map()->GetHeap(); if (fixed_array->map() != heap->fixed_cow_array_map() && fixed_array->map() != heap->fixed_double_array_map() && fixed_array != heap->empty_fixed_array()) { if (fixed_array->IsDictionary()) { heap->object_stats_->RecordFixedArraySubTypeStats(dictionary_type, fixed_array->Size()); } else { heap->object_stats_->RecordFixedArraySubTypeStats(fast_type, fixed_array->Size()); } } } void ObjectStatsVisitor::VisitBase(VisitorId id, Map* map, HeapObject* obj) { Heap* heap = map->GetHeap(); int object_size = obj->Size(); heap->object_stats_->RecordObjectStats(map->instance_type(), object_size); table_.GetVisitorById(id)(map, obj); if (obj->IsJSObject()) { JSObject* object = JSObject::cast(obj); CountFixedArray(object->elements(), DICTIONARY_ELEMENTS_SUB_TYPE, FAST_ELEMENTS_SUB_TYPE); CountFixedArray(object->properties(), DICTIONARY_PROPERTIES_SUB_TYPE, FAST_PROPERTIES_SUB_TYPE); } } template void ObjectStatsVisitor::Visit(Map* map, HeapObject* obj) { VisitBase(id, map, obj); } template <> void ObjectStatsVisitor::Visit(Map* map, HeapObject* obj) { Heap* heap = map->GetHeap(); Map* map_obj = Map::cast(obj); DCHECK(map->instance_type() == MAP_TYPE); DescriptorArray* array = map_obj->instance_descriptors(); if (map_obj->owns_descriptors() && array != heap->empty_descriptor_array()) { int fixed_array_size = array->Size(); heap->object_stats_->RecordFixedArraySubTypeStats(DESCRIPTOR_ARRAY_SUB_TYPE, fixed_array_size); } if (map_obj->has_code_cache()) { CodeCache* cache = CodeCache::cast(map_obj->code_cache()); heap->object_stats_->RecordFixedArraySubTypeStats( MAP_CODE_CACHE_SUB_TYPE, cache->default_cache()->Size()); if (!cache->normal_type_cache()->IsUndefined()) { heap->object_stats_->RecordFixedArraySubTypeStats( MAP_CODE_CACHE_SUB_TYPE, FixedArray::cast(cache->normal_type_cache())->Size()); } } VisitBase(kVisitMap, map, obj); } template <> void ObjectStatsVisitor::Visit( Map* map, HeapObject* obj) { Heap* heap = map->GetHeap(); int object_size = obj->Size(); DCHECK(map->instance_type() == CODE_TYPE); Code* code_obj = Code::cast(obj); heap->object_stats_->RecordCodeSubTypeStats(code_obj->kind(), code_obj->GetAge(), object_size); VisitBase(kVisitCode, map, obj); } template <> void ObjectStatsVisitor::Visit( Map* map, HeapObject* obj) { Heap* heap = map->GetHeap(); SharedFunctionInfo* sfi = SharedFunctionInfo::cast(obj); if (sfi->scope_info() != heap->empty_fixed_array()) { heap->object_stats_->RecordFixedArraySubTypeStats( SCOPE_INFO_SUB_TYPE, FixedArray::cast(sfi->scope_info())->Size()); } VisitBase(kVisitSharedFunctionInfo, map, obj); } template <> void ObjectStatsVisitor::Visit( Map* map, HeapObject* obj) { Heap* heap = map->GetHeap(); FixedArray* fixed_array = FixedArray::cast(obj); if (fixed_array == heap->string_table()) { heap->object_stats_->RecordFixedArraySubTypeStats(STRING_TABLE_SUB_TYPE, fixed_array->Size()); } VisitBase(kVisitFixedArray, map, obj); } void ObjectStatsVisitor::Initialize(VisitorDispatchTable* original) { // Copy the original visitor table to make call-through possible. After we // preserved a copy locally, we patch the original table to call us. table_.CopyFrom(original); #define COUNT_FUNCTION(id) original->Register(kVisit##id, Visit); VISITOR_ID_LIST(COUNT_FUNCTION) #undef COUNT_FUNCTION } } // namespace internal } // namespace v8