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
15 namespace v8 {
16 namespace internal {
17
18 static base::LazyMutex object_stats_mutex = LAZY_MUTEX_INITIALIZER;
19
20
ClearObjectStats(bool clear_last_time_stats)21 void 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.
PrintJSONArray(size_t * array,const int len)37 V8_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
DumpJSONArray(std::stringstream & stream,size_t * array,const int len)46 V8_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
PrintKeyAndId(const char * key,int gc_count)56 void 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
PrintInstanceTypeJSON(const char * key,int gc_count,const char * name,int index)61 void 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
PrintJSON(const char * key)79 void 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
DumpInstanceTypeData(std::stringstream & stream,const char * name,int index)123 void 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
Dump(std::stringstream & stream)137 void 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
CheckpointObjectStats()180 void 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
isolate()239 Isolate* ObjectStats::isolate() { return heap()->isolate(); }
240
CollectStatistics(HeapObject * obj)241 void 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
272 class ObjectStatsCollector::CompilationCacheTableVisitor
273 : public ObjectVisitor {
274 public:
CompilationCacheTableVisitor(ObjectStatsCollector * parent)275 explicit CompilationCacheTableVisitor(ObjectStatsCollector* parent)
276 : parent_(parent) {}
277
VisitPointers(Object ** start,Object ** end)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
CollectGlobalStatistics()292 void 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
CanRecordFixedArray(Heap * heap,FixedArrayBase * array)329 static 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
IsCowArray(Heap * heap,FixedArrayBase * array)340 static bool IsCowArray(Heap* heap, FixedArrayBase* array) {
341 return array->map() == heap->fixed_cow_array_map();
342 }
343
SameLiveness(HeapObject * obj1,HeapObject * obj2)344 static bool SameLiveness(HeapObject* obj1, HeapObject* obj2) {
345 return obj1 == nullptr || obj2 == nullptr ||
346 ObjectMarking::Color(obj1) == ObjectMarking::Color(obj2);
347 }
348
RecordFixedArrayHelper(HeapObject * parent,FixedArray * array,int subtype,size_t overhead)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
RecursivelyRecordFixedArrayHelper(HeapObject * parent,FixedArray * array,int subtype)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>
RecordHashTableHelper(HeapObject * parent,HashTable * array,int subtype)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
RecordJSObjectDetails(JSObject * object)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
RecordJSWeakCollectionDetails(JSWeakCollection * obj)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
RecordJSCollectionDetails(JSObject * obj)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
RecordScriptDetails(Script * obj)443 void 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
RecordMapDetails(Map * map_obj)448 void 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
RecordTemplateInfoDetails(TemplateInfo * obj)493 void 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
RecordBytecodeArrayDetails(BytecodeArray * obj)504 void 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
RecordCodeDetails(Code * code)511 void 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
RecordSharedFunctionInfoDetails(SharedFunctionInfo * sfi)539 void 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
RecordJSFunctionDetails(JSFunction * function)557 void 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
RecordFixedArrayDetails(FixedArray * array)565 void 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