Line data Source code
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/objects/code-cache-inl.h"
14 : #include "src/objects/compilation-cache-inl.h"
15 : #include "src/utils.h"
16 :
17 : namespace v8 {
18 : namespace internal {
19 :
20 : static base::LazyMutex object_stats_mutex = LAZY_MUTEX_INITIALIZER;
21 :
22 :
23 0 : void ObjectStats::ClearObjectStats(bool clear_last_time_stats) {
24 0 : memset(object_counts_, 0, sizeof(object_counts_));
25 0 : memset(object_sizes_, 0, sizeof(object_sizes_));
26 0 : memset(over_allocated_, 0, sizeof(over_allocated_));
27 0 : memset(size_histogram_, 0, sizeof(size_histogram_));
28 0 : memset(over_allocated_histogram_, 0, sizeof(over_allocated_histogram_));
29 0 : if (clear_last_time_stats) {
30 0 : memset(object_counts_last_time_, 0, sizeof(object_counts_last_time_));
31 0 : memset(object_sizes_last_time_, 0, sizeof(object_sizes_last_time_));
32 : }
33 : visited_fixed_array_sub_types_.clear();
34 0 : }
35 :
36 : // Tell the compiler to never inline this: occasionally, the optimizer will
37 : // decide to inline this and unroll the loop, making the compiled code more than
38 : // 100KB larger.
39 0 : V8_NOINLINE static void PrintJSONArray(size_t* array, const int len) {
40 0 : PrintF("[ ");
41 0 : for (int i = 0; i < len; i++) {
42 0 : PrintF("%zu", array[i]);
43 0 : if (i != (len - 1)) PrintF(", ");
44 : }
45 0 : PrintF(" ]");
46 0 : }
47 :
48 0 : V8_NOINLINE static void DumpJSONArray(std::stringstream& stream, size_t* array,
49 : const int len) {
50 0 : stream << "[";
51 0 : for (int i = 0; i < len; i++) {
52 0 : stream << array[i];
53 0 : if (i != (len - 1)) stream << ",";
54 : }
55 0 : stream << "]";
56 0 : }
57 :
58 0 : void ObjectStats::PrintKeyAndId(const char* key, int gc_count) {
59 : PrintF("\"isolate\": \"%p\", \"id\": %d, \"key\": \"%s\", ",
60 0 : reinterpret_cast<void*>(isolate()), gc_count, key);
61 0 : }
62 :
63 0 : void ObjectStats::PrintInstanceTypeJSON(const char* key, int gc_count,
64 : const char* name, int index) {
65 0 : PrintF("{ ");
66 : PrintKeyAndId(key, gc_count);
67 0 : PrintF("\"type\": \"instance_type_data\", ");
68 0 : PrintF("\"instance_type\": %d, ", index);
69 0 : PrintF("\"instance_type_name\": \"%s\", ", name);
70 0 : PrintF("\"overall\": %zu, ", object_sizes_[index]);
71 0 : PrintF("\"count\": %zu, ", object_counts_[index]);
72 0 : PrintF("\"over_allocated\": %zu, ", over_allocated_[index]);
73 0 : PrintF("\"histogram\": ");
74 0 : PrintJSONArray(size_histogram_[index], kNumberOfBuckets);
75 0 : PrintF(",");
76 0 : PrintF("\"over_allocated_histogram\": ");
77 0 : PrintJSONArray(over_allocated_histogram_[index], kNumberOfBuckets);
78 0 : PrintF(" }\n");
79 0 : }
80 :
81 0 : void ObjectStats::PrintJSON(const char* key) {
82 : double time = isolate()->time_millis_since_init();
83 0 : int gc_count = heap()->gc_count();
84 :
85 : // gc_descriptor
86 0 : PrintF("{ ");
87 : PrintKeyAndId(key, gc_count);
88 0 : PrintF("\"type\": \"gc_descriptor\", \"time\": %f }\n", time);
89 : // bucket_sizes
90 0 : PrintF("{ ");
91 : PrintKeyAndId(key, gc_count);
92 0 : PrintF("\"type\": \"bucket_sizes\", \"sizes\": [ ");
93 0 : for (int i = 0; i < kNumberOfBuckets; i++) {
94 0 : PrintF("%d", 1 << (kFirstBucketShift + i));
95 0 : if (i != (kNumberOfBuckets - 1)) PrintF(", ");
96 : }
97 0 : PrintF(" ] }\n");
98 :
99 : #define INSTANCE_TYPE_WRAPPER(name) \
100 : PrintInstanceTypeJSON(key, gc_count, #name, name);
101 : #define CODE_KIND_WRAPPER(name) \
102 : PrintInstanceTypeJSON(key, gc_count, "*CODE_" #name, \
103 : FIRST_CODE_KIND_SUB_TYPE + Code::name);
104 : #define FIXED_ARRAY_SUB_INSTANCE_TYPE_WRAPPER(name) \
105 : PrintInstanceTypeJSON(key, gc_count, "*FIXED_ARRAY_" #name, \
106 : FIRST_FIXED_ARRAY_SUB_TYPE + name);
107 : #define CODE_AGE_WRAPPER(name) \
108 : PrintInstanceTypeJSON( \
109 : key, gc_count, "*CODE_AGE_" #name, \
110 : FIRST_CODE_AGE_SUB_TYPE + Code::k##name##CodeAge - Code::kFirstCodeAge);
111 :
112 0 : INSTANCE_TYPE_LIST(INSTANCE_TYPE_WRAPPER)
113 0 : CODE_KIND_LIST(CODE_KIND_WRAPPER)
114 0 : FIXED_ARRAY_SUB_INSTANCE_TYPE_LIST(FIXED_ARRAY_SUB_INSTANCE_TYPE_WRAPPER)
115 0 : CODE_AGE_LIST_COMPLETE(CODE_AGE_WRAPPER)
116 :
117 : #undef INSTANCE_TYPE_WRAPPER
118 : #undef CODE_KIND_WRAPPER
119 : #undef FIXED_ARRAY_SUB_INSTANCE_TYPE_WRAPPER
120 : #undef CODE_AGE_WRAPPER
121 : #undef PRINT_INSTANCE_TYPE_DATA
122 : #undef PRINT_KEY_AND_ID
123 0 : }
124 :
125 0 : void ObjectStats::DumpInstanceTypeData(std::stringstream& stream,
126 : const char* name, int index) {
127 0 : stream << "\"" << name << "\":{";
128 0 : stream << "\"type\":" << static_cast<int>(index) << ",";
129 0 : stream << "\"overall\":" << object_sizes_[index] << ",";
130 0 : stream << "\"count\":" << object_counts_[index] << ",";
131 0 : stream << "\"over_allocated\":" << over_allocated_[index] << ",";
132 0 : stream << "\"histogram\":";
133 0 : DumpJSONArray(stream, size_histogram_[index], kNumberOfBuckets);
134 0 : stream << ",\"over_allocated_histogram\":";
135 0 : DumpJSONArray(stream, over_allocated_histogram_[index], kNumberOfBuckets);
136 0 : stream << "},";
137 0 : }
138 :
139 0 : void ObjectStats::Dump(std::stringstream& stream) {
140 : double time = isolate()->time_millis_since_init();
141 0 : int gc_count = heap()->gc_count();
142 :
143 0 : stream << "{";
144 0 : stream << "\"isolate\":\"" << reinterpret_cast<void*>(isolate()) << "\",";
145 0 : stream << "\"id\":" << gc_count << ",";
146 0 : stream << "\"time\":" << time << ",";
147 0 : stream << "\"bucket_sizes\":[";
148 0 : for (int i = 0; i < kNumberOfBuckets; i++) {
149 0 : stream << (1 << (kFirstBucketShift + i));
150 0 : if (i != (kNumberOfBuckets - 1)) stream << ",";
151 : }
152 0 : stream << "],";
153 0 : stream << "\"type_data\":{";
154 :
155 : #define INSTANCE_TYPE_WRAPPER(name) DumpInstanceTypeData(stream, #name, name);
156 : #define CODE_KIND_WRAPPER(name) \
157 : DumpInstanceTypeData(stream, "*CODE_" #name, \
158 : FIRST_CODE_KIND_SUB_TYPE + Code::name);
159 :
160 : #define FIXED_ARRAY_SUB_INSTANCE_TYPE_WRAPPER(name) \
161 : DumpInstanceTypeData(stream, "*FIXED_ARRAY_" #name, \
162 : FIRST_FIXED_ARRAY_SUB_TYPE + name);
163 :
164 : #define CODE_AGE_WRAPPER(name) \
165 : DumpInstanceTypeData( \
166 : stream, "*CODE_AGE_" #name, \
167 : FIRST_CODE_AGE_SUB_TYPE + Code::k##name##CodeAge - Code::kFirstCodeAge);
168 :
169 0 : INSTANCE_TYPE_LIST(INSTANCE_TYPE_WRAPPER);
170 0 : CODE_KIND_LIST(CODE_KIND_WRAPPER);
171 0 : FIXED_ARRAY_SUB_INSTANCE_TYPE_LIST(FIXED_ARRAY_SUB_INSTANCE_TYPE_WRAPPER);
172 0 : CODE_AGE_LIST_COMPLETE(CODE_AGE_WRAPPER);
173 0 : stream << "\"END\":{}}}";
174 :
175 : #undef INSTANCE_TYPE_WRAPPER
176 : #undef CODE_KIND_WRAPPER
177 : #undef FIXED_ARRAY_SUB_INSTANCE_TYPE_WRAPPER
178 : #undef CODE_AGE_WRAPPER
179 : #undef PRINT_INSTANCE_TYPE_DATA
180 0 : }
181 :
182 0 : void ObjectStats::CheckpointObjectStats() {
183 : base::LockGuard<base::Mutex> lock_guard(object_stats_mutex.Pointer());
184 0 : Counters* counters = isolate()->counters();
185 : #define ADJUST_LAST_TIME_OBJECT_COUNT(name) \
186 : counters->count_of_##name()->Increment( \
187 : static_cast<int>(object_counts_[name])); \
188 : counters->count_of_##name()->Decrement( \
189 : static_cast<int>(object_counts_last_time_[name])); \
190 : counters->size_of_##name()->Increment( \
191 : static_cast<int>(object_sizes_[name])); \
192 : counters->size_of_##name()->Decrement( \
193 : static_cast<int>(object_sizes_last_time_[name]));
194 0 : INSTANCE_TYPE_LIST(ADJUST_LAST_TIME_OBJECT_COUNT)
195 : #undef ADJUST_LAST_TIME_OBJECT_COUNT
196 : int index;
197 : #define ADJUST_LAST_TIME_OBJECT_COUNT(name) \
198 : index = FIRST_CODE_KIND_SUB_TYPE + Code::name; \
199 : counters->count_of_CODE_TYPE_##name()->Increment( \
200 : static_cast<int>(object_counts_[index])); \
201 : counters->count_of_CODE_TYPE_##name()->Decrement( \
202 : static_cast<int>(object_counts_last_time_[index])); \
203 : counters->size_of_CODE_TYPE_##name()->Increment( \
204 : static_cast<int>(object_sizes_[index])); \
205 : counters->size_of_CODE_TYPE_##name()->Decrement( \
206 : static_cast<int>(object_sizes_last_time_[index]));
207 0 : CODE_KIND_LIST(ADJUST_LAST_TIME_OBJECT_COUNT)
208 : #undef ADJUST_LAST_TIME_OBJECT_COUNT
209 : #define ADJUST_LAST_TIME_OBJECT_COUNT(name) \
210 : index = FIRST_FIXED_ARRAY_SUB_TYPE + name; \
211 : counters->count_of_FIXED_ARRAY_##name()->Increment( \
212 : static_cast<int>(object_counts_[index])); \
213 : counters->count_of_FIXED_ARRAY_##name()->Decrement( \
214 : static_cast<int>(object_counts_last_time_[index])); \
215 : counters->size_of_FIXED_ARRAY_##name()->Increment( \
216 : static_cast<int>(object_sizes_[index])); \
217 : counters->size_of_FIXED_ARRAY_##name()->Decrement( \
218 : static_cast<int>(object_sizes_last_time_[index]));
219 0 : FIXED_ARRAY_SUB_INSTANCE_TYPE_LIST(ADJUST_LAST_TIME_OBJECT_COUNT)
220 : #undef ADJUST_LAST_TIME_OBJECT_COUNT
221 : #define ADJUST_LAST_TIME_OBJECT_COUNT(name) \
222 : index = \
223 : FIRST_CODE_AGE_SUB_TYPE + Code::k##name##CodeAge - Code::kFirstCodeAge; \
224 : counters->count_of_CODE_AGE_##name()->Increment( \
225 : static_cast<int>(object_counts_[index])); \
226 : counters->count_of_CODE_AGE_##name()->Decrement( \
227 : static_cast<int>(object_counts_last_time_[index])); \
228 : counters->size_of_CODE_AGE_##name()->Increment( \
229 : static_cast<int>(object_sizes_[index])); \
230 : counters->size_of_CODE_AGE_##name()->Decrement( \
231 : static_cast<int>(object_sizes_last_time_[index]));
232 0 : CODE_AGE_LIST_COMPLETE(ADJUST_LAST_TIME_OBJECT_COUNT)
233 : #undef ADJUST_LAST_TIME_OBJECT_COUNT
234 :
235 0 : MemCopy(object_counts_last_time_, object_counts_, sizeof(object_counts_));
236 0 : MemCopy(object_sizes_last_time_, object_sizes_, sizeof(object_sizes_));
237 0 : ClearObjectStats();
238 0 : }
239 :
240 :
241 0 : Isolate* ObjectStats::isolate() { return heap()->isolate(); }
242 :
243 0 : void ObjectStatsCollector::CollectStatistics(HeapObject* obj) {
244 : Map* map = obj->map();
245 :
246 : // Record for the InstanceType.
247 0 : int object_size = obj->Size();
248 0 : stats_->RecordObjectStats(map->instance_type(), object_size);
249 :
250 : // Record specific sub types where possible.
251 0 : if (obj->IsMap()) RecordMapDetails(Map::cast(obj));
252 0 : if (obj->IsObjectTemplateInfo() || obj->IsFunctionTemplateInfo()) {
253 0 : RecordTemplateInfoDetails(TemplateInfo::cast(obj));
254 : }
255 0 : if (obj->IsBytecodeArray()) {
256 0 : RecordBytecodeArrayDetails(BytecodeArray::cast(obj));
257 : }
258 0 : if (obj->IsCode()) RecordCodeDetails(Code::cast(obj));
259 0 : if (obj->IsSharedFunctionInfo()) {
260 0 : RecordSharedFunctionInfoDetails(SharedFunctionInfo::cast(obj));
261 : }
262 0 : if (obj->IsFixedArray()) RecordFixedArrayDetails(FixedArray::cast(obj));
263 0 : if (obj->IsJSObject()) RecordJSObjectDetails(JSObject::cast(obj));
264 0 : if (obj->IsJSWeakCollection()) {
265 0 : RecordJSWeakCollectionDetails(JSWeakCollection::cast(obj));
266 : }
267 0 : if (obj->IsJSCollection()) {
268 0 : RecordJSCollectionDetails(JSObject::cast(obj));
269 : }
270 0 : if (obj->IsJSFunction()) RecordJSFunctionDetails(JSFunction::cast(obj));
271 0 : if (obj->IsScript()) RecordScriptDetails(Script::cast(obj));
272 0 : }
273 :
274 0 : class ObjectStatsCollector::CompilationCacheTableVisitor : public RootVisitor {
275 : public:
276 : explicit CompilationCacheTableVisitor(ObjectStatsCollector* parent)
277 0 : : parent_(parent) {}
278 :
279 0 : void VisitRootPointers(Root root, Object** start, Object** end) override {
280 0 : for (Object** current = start; current < end; current++) {
281 0 : HeapObject* obj = HeapObject::cast(*current);
282 0 : if (obj->IsUndefined(parent_->heap_->isolate())) continue;
283 0 : CHECK(obj->IsCompilationCacheTable());
284 : parent_->RecordHashTableHelper(nullptr, CompilationCacheTable::cast(obj),
285 0 : COMPILATION_CACHE_TABLE_SUB_TYPE);
286 : }
287 0 : }
288 :
289 : private:
290 : ObjectStatsCollector* parent_;
291 : };
292 :
293 0 : void ObjectStatsCollector::CollectGlobalStatistics() {
294 : // Global FixedArrays.
295 0 : RecordFixedArrayHelper(nullptr, heap_->weak_new_space_object_to_code_list(),
296 0 : WEAK_NEW_SPACE_OBJECT_TO_CODE_SUB_TYPE, 0);
297 : RecordFixedArrayHelper(nullptr, heap_->serialized_templates(),
298 0 : SERIALIZED_TEMPLATES_SUB_TYPE, 0);
299 : RecordFixedArrayHelper(nullptr, heap_->number_string_cache(),
300 0 : NUMBER_STRING_CACHE_SUB_TYPE, 0);
301 : RecordFixedArrayHelper(nullptr, heap_->single_character_string_cache(),
302 0 : SINGLE_CHARACTER_STRING_CACHE_SUB_TYPE, 0);
303 : RecordFixedArrayHelper(nullptr, heap_->string_split_cache(),
304 0 : STRING_SPLIT_CACHE_SUB_TYPE, 0);
305 : RecordFixedArrayHelper(nullptr, heap_->regexp_multiple_cache(),
306 0 : REGEXP_MULTIPLE_CACHE_SUB_TYPE, 0);
307 0 : RecordFixedArrayHelper(nullptr, heap_->retained_maps(),
308 0 : RETAINED_MAPS_SUB_TYPE, 0);
309 :
310 : // Global weak FixedArrays.
311 : RecordFixedArrayHelper(
312 0 : nullptr, WeakFixedArray::cast(heap_->noscript_shared_function_infos()),
313 0 : NOSCRIPT_SHARED_FUNCTION_INFOS_SUB_TYPE, 0);
314 0 : RecordFixedArrayHelper(nullptr, WeakFixedArray::cast(heap_->script_list()),
315 0 : SCRIPT_LIST_SUB_TYPE, 0);
316 :
317 : // Global hash tables.
318 0 : RecordHashTableHelper(nullptr, heap_->string_table(), STRING_TABLE_SUB_TYPE);
319 : RecordHashTableHelper(nullptr, heap_->weak_object_to_code_table(),
320 0 : OBJECT_TO_CODE_SUB_TYPE);
321 : RecordHashTableHelper(nullptr, heap_->code_stubs(),
322 0 : CODE_STUBS_TABLE_SUB_TYPE);
323 : RecordHashTableHelper(nullptr, heap_->empty_properties_dictionary(),
324 0 : EMPTY_PROPERTIES_DICTIONARY_SUB_TYPE);
325 0 : CompilationCache* compilation_cache = heap_->isolate()->compilation_cache();
326 : CompilationCacheTableVisitor v(this);
327 0 : compilation_cache->Iterate(&v);
328 0 : }
329 :
330 0 : static bool CanRecordFixedArray(Heap* heap, FixedArrayBase* array) {
331 0 : return array->map()->instance_type() == FIXED_ARRAY_TYPE &&
332 0 : array->map() != heap->fixed_double_array_map() &&
333 0 : array != heap->empty_fixed_array() &&
334 0 : array != heap->empty_byte_array() &&
335 0 : array != heap->empty_sloppy_arguments_elements() &&
336 0 : array != heap->empty_slow_element_dictionary() &&
337 0 : array != heap->empty_descriptor_array() &&
338 0 : array != heap->empty_properties_dictionary();
339 : }
340 :
341 0 : static bool IsCowArray(Heap* heap, FixedArrayBase* array) {
342 : return array->map() == heap->fixed_cow_array_map();
343 : }
344 :
345 0 : static bool SameLiveness(HeapObject* obj1, HeapObject* obj2) {
346 0 : return obj1 == nullptr || obj2 == nullptr ||
347 0 : ObjectMarking::Color(obj1, MarkingState::Internal(obj1)) ==
348 0 : ObjectMarking::Color(obj2, MarkingState::Internal(obj2));
349 : }
350 :
351 0 : bool ObjectStatsCollector::RecordFixedArrayHelper(HeapObject* parent,
352 : FixedArray* array,
353 : int subtype,
354 : size_t overhead) {
355 0 : if (SameLiveness(parent, array) && CanRecordFixedArray(heap_, array) &&
356 0 : !IsCowArray(heap_, array)) {
357 0 : return stats_->RecordFixedArraySubTypeStats(array, subtype, array->Size(),
358 0 : overhead);
359 : }
360 : return false;
361 : }
362 :
363 0 : void ObjectStatsCollector::RecursivelyRecordFixedArrayHelper(HeapObject* parent,
364 : FixedArray* array,
365 : int subtype) {
366 0 : if (RecordFixedArrayHelper(parent, array, subtype, 0)) {
367 0 : for (int i = 0; i < array->length(); i++) {
368 0 : if (array->get(i)->IsFixedArray()) {
369 : RecursivelyRecordFixedArrayHelper(
370 0 : parent, FixedArray::cast(array->get(i)), subtype);
371 : }
372 : }
373 : }
374 0 : }
375 :
376 : template <class HashTable>
377 0 : void ObjectStatsCollector::RecordHashTableHelper(HeapObject* parent,
378 : HashTable* array,
379 : int subtype) {
380 0 : int used = array->NumberOfElements() * HashTable::kEntrySize * kPointerSize;
381 0 : CHECK_GE(array->Size(), used);
382 0 : size_t overhead = array->Size() - used -
383 : HashTable::kElementsStartIndex * kPointerSize -
384 0 : FixedArray::kHeaderSize;
385 0 : RecordFixedArrayHelper(parent, array, subtype, overhead);
386 0 : }
387 :
388 0 : void ObjectStatsCollector::RecordJSObjectDetails(JSObject* object) {
389 : size_t overhead = 0;
390 : FixedArrayBase* elements = object->elements();
391 0 : if (CanRecordFixedArray(heap_, elements) && !IsCowArray(heap_, elements)) {
392 0 : if (elements->IsDictionary() && SameLiveness(object, elements)) {
393 : SeededNumberDictionary* dict = SeededNumberDictionary::cast(elements);
394 0 : RecordHashTableHelper(object, dict, DICTIONARY_ELEMENTS_SUB_TYPE);
395 : } else {
396 0 : if (IsFastHoleyElementsKind(object->GetElementsKind())) {
397 0 : int used = object->GetFastElementsUsage() * kPointerSize;
398 0 : if (object->GetElementsKind() == FAST_HOLEY_DOUBLE_ELEMENTS) used *= 2;
399 0 : CHECK_GE(elements->Size(), used);
400 0 : overhead = elements->Size() - used - FixedArray::kHeaderSize;
401 : }
402 : stats_->RecordFixedArraySubTypeStats(elements, FAST_ELEMENTS_SUB_TYPE,
403 0 : elements->Size(), overhead);
404 : }
405 : }
406 :
407 : overhead = 0;
408 : FixedArrayBase* properties = object->properties();
409 0 : if (CanRecordFixedArray(heap_, properties) &&
410 0 : SameLiveness(object, properties) && !IsCowArray(heap_, properties)) {
411 0 : if (properties->IsDictionary()) {
412 : NameDictionary* dict = NameDictionary::cast(properties);
413 0 : RecordHashTableHelper(object, dict, DICTIONARY_PROPERTIES_SUB_TYPE);
414 : } else {
415 : stats_->RecordFixedArraySubTypeStats(properties, FAST_PROPERTIES_SUB_TYPE,
416 0 : properties->Size(), overhead);
417 : }
418 : }
419 0 : }
420 :
421 0 : void ObjectStatsCollector::RecordJSWeakCollectionDetails(
422 : JSWeakCollection* obj) {
423 0 : if (obj->table()->IsHashTable()) {
424 : ObjectHashTable* table = ObjectHashTable::cast(obj->table());
425 0 : int used = table->NumberOfElements() * ObjectHashTable::kEntrySize;
426 0 : size_t overhead = table->Size() - used;
427 0 : RecordFixedArrayHelper(obj, table, JS_WEAK_COLLECTION_SUB_TYPE, overhead);
428 : }
429 0 : }
430 :
431 0 : void ObjectStatsCollector::RecordJSCollectionDetails(JSObject* obj) {
432 : // The JS versions use a different HashTable implementation that cannot use
433 : // the regular helper. Since overall impact is usually small just record
434 : // without overhead.
435 0 : if (obj->IsJSMap()) {
436 : RecordFixedArrayHelper(nullptr, FixedArray::cast(JSMap::cast(obj)->table()),
437 0 : JS_COLLECTION_SUB_TYPE, 0);
438 : }
439 0 : if (obj->IsJSSet()) {
440 : RecordFixedArrayHelper(nullptr, FixedArray::cast(JSSet::cast(obj)->table()),
441 0 : JS_COLLECTION_SUB_TYPE, 0);
442 : }
443 0 : }
444 :
445 0 : void ObjectStatsCollector::RecordScriptDetails(Script* obj) {
446 : FixedArray* infos = FixedArray::cast(obj->shared_function_infos());
447 0 : RecordFixedArrayHelper(obj, infos, SHARED_FUNCTION_INFOS_SUB_TYPE, 0);
448 0 : }
449 :
450 0 : void ObjectStatsCollector::RecordMapDetails(Map* map_obj) {
451 : DescriptorArray* array = map_obj->instance_descriptors();
452 0 : if (map_obj->owns_descriptors() && array != heap_->empty_descriptor_array() &&
453 0 : SameLiveness(map_obj, array)) {
454 0 : RecordFixedArrayHelper(map_obj, array, DESCRIPTOR_ARRAY_SUB_TYPE, 0);
455 0 : if (array->HasEnumCache()) {
456 : RecordFixedArrayHelper(array, array->GetEnumCache(), ENUM_CACHE_SUB_TYPE,
457 0 : 0);
458 : }
459 0 : if (array->HasEnumIndicesCache()) {
460 : RecordFixedArrayHelper(array, array->GetEnumIndicesCache(),
461 0 : ENUM_INDICES_CACHE_SUB_TYPE, 0);
462 : }
463 : }
464 :
465 0 : if (map_obj->has_code_cache()) {
466 : FixedArray* code_cache = map_obj->code_cache();
467 0 : if (code_cache->IsCodeCacheHashTable()) {
468 : RecordHashTableHelper(map_obj, CodeCacheHashTable::cast(code_cache),
469 0 : MAP_CODE_CACHE_SUB_TYPE);
470 : } else {
471 0 : RecordFixedArrayHelper(map_obj, code_cache, MAP_CODE_CACHE_SUB_TYPE, 0);
472 : }
473 : }
474 :
475 0 : for (DependentCode* cur_dependent_code = map_obj->dependent_code();
476 0 : 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 : 0);
481 : }
482 :
483 0 : if (map_obj->is_prototype_map()) {
484 0 : if (map_obj->prototype_info()->IsPrototypeInfo()) {
485 : PrototypeInfo* info = PrototypeInfo::cast(map_obj->prototype_info());
486 : Object* users = info->prototype_users();
487 0 : if (users->IsWeakFixedArray()) {
488 : RecordFixedArrayHelper(map_obj, WeakFixedArray::cast(users),
489 0 : PROTOTYPE_USERS_SUB_TYPE, 0);
490 : }
491 : }
492 : }
493 0 : }
494 :
495 0 : void ObjectStatsCollector::RecordTemplateInfoDetails(TemplateInfo* obj) {
496 0 : if (obj->property_accessors()->IsFixedArray()) {
497 : RecordFixedArrayHelper(obj, FixedArray::cast(obj->property_accessors()),
498 0 : TEMPLATE_INFO_SUB_TYPE, 0);
499 : }
500 0 : if (obj->property_list()->IsFixedArray()) {
501 : RecordFixedArrayHelper(obj, FixedArray::cast(obj->property_list()),
502 0 : TEMPLATE_INFO_SUB_TYPE, 0);
503 : }
504 0 : }
505 :
506 0 : void ObjectStatsCollector::RecordBytecodeArrayDetails(BytecodeArray* obj) {
507 : RecordFixedArrayHelper(obj, obj->constant_pool(),
508 0 : BYTECODE_ARRAY_CONSTANT_POOL_SUB_TYPE, 0);
509 : RecordFixedArrayHelper(obj, obj->handler_table(),
510 0 : BYTECODE_ARRAY_HANDLER_TABLE_SUB_TYPE, 0);
511 0 : }
512 :
513 0 : void ObjectStatsCollector::RecordCodeDetails(Code* code) {
514 0 : stats_->RecordCodeSubTypeStats(code->kind(), code->GetAge(), code->Size());
515 : RecordFixedArrayHelper(code, code->deoptimization_data(),
516 0 : DEOPTIMIZATION_DATA_SUB_TYPE, 0);
517 0 : if (code->kind() == Code::Kind::OPTIMIZED_FUNCTION) {
518 : DeoptimizationInputData* input_data =
519 : DeoptimizationInputData::cast(code->deoptimization_data());
520 0 : if (input_data->length() > 0) {
521 : RecordFixedArrayHelper(code->deoptimization_data(),
522 : input_data->LiteralArray(),
523 0 : OPTIMIZED_CODE_LITERALS_SUB_TYPE, 0);
524 : }
525 : }
526 : RecordFixedArrayHelper(code, code->handler_table(), HANDLER_TABLE_SUB_TYPE,
527 0 : 0);
528 : int const mode_mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT);
529 0 : for (RelocIterator it(code, mode_mask); !it.done(); it.next()) {
530 0 : RelocInfo::Mode mode = it.rinfo()->rmode();
531 0 : if (mode == RelocInfo::EMBEDDED_OBJECT) {
532 0 : Object* target = it.rinfo()->target_object();
533 0 : if (target->IsFixedArray()) {
534 : RecursivelyRecordFixedArrayHelper(code, FixedArray::cast(target),
535 0 : EMBEDDED_OBJECT_SUB_TYPE);
536 : }
537 : }
538 : }
539 0 : }
540 :
541 0 : void ObjectStatsCollector::RecordSharedFunctionInfoDetails(
542 : SharedFunctionInfo* sfi) {
543 : FixedArray* scope_info = sfi->scope_info();
544 0 : RecordFixedArrayHelper(sfi, scope_info, SCOPE_INFO_SUB_TYPE, 0);
545 : FeedbackMetadata* feedback_metadata = sfi->feedback_metadata();
546 0 : if (!feedback_metadata->is_empty()) {
547 : RecordFixedArrayHelper(sfi, feedback_metadata, FEEDBACK_METADATA_SUB_TYPE,
548 0 : 0);
549 : }
550 :
551 0 : if (!sfi->OptimizedCodeMapIsCleared()) {
552 : FixedArray* optimized_code_map = sfi->optimized_code_map();
553 : RecordFixedArrayHelper(sfi, optimized_code_map, OPTIMIZED_CODE_MAP_SUB_TYPE,
554 0 : 0);
555 : // Optimized code map should be small, so skip accounting.
556 : }
557 0 : }
558 :
559 0 : void ObjectStatsCollector::RecordJSFunctionDetails(JSFunction* function) {
560 0 : if (function->feedback_vector_cell()->value()->IsFeedbackVector()) {
561 : FeedbackVector* feedback_vector = function->feedback_vector();
562 : RecordFixedArrayHelper(function, feedback_vector, FEEDBACK_VECTOR_SUB_TYPE,
563 0 : 0);
564 : }
565 0 : }
566 :
567 0 : void ObjectStatsCollector::RecordFixedArrayDetails(FixedArray* array) {
568 0 : if (array->IsContext()) {
569 0 : RecordFixedArrayHelper(nullptr, array, CONTEXT_SUB_TYPE, 0);
570 : }
571 0 : if (IsCowArray(heap_, array) && CanRecordFixedArray(heap_, array)) {
572 : stats_->RecordFixedArraySubTypeStats(array, COPY_ON_WRITE_SUB_TYPE,
573 0 : array->Size(), 0);
574 : }
575 0 : if (array->IsNativeContext()) {
576 : Context* native_ctx = Context::cast(array);
577 : RecordHashTableHelper(array,
578 : native_ctx->slow_template_instantiations_cache(),
579 0 : SLOW_TEMPLATE_INSTANTIATIONS_CACHE_SUB_TYPE);
580 : FixedArray* fast_cache = native_ctx->fast_template_instantiations_cache();
581 : stats_->RecordFixedArraySubTypeStats(
582 : fast_cache, FAST_TEMPLATE_INSTANTIATIONS_CACHE_SUB_TYPE,
583 0 : fast_cache->Size(), 0);
584 : }
585 0 : }
586 :
587 : } // namespace internal
588 : } // namespace v8
|