Line data Source code
1 : // Copyright 2015 the V8 project authors. All rights reserved.
2 : //
3 : // Use of this source code is governed by a BSD-style license that can be
4 : // found in the LICENSE file.
5 :
6 : #include "src/heap/object-stats.h"
7 :
8 : #include <unordered_set>
9 :
10 : #include "src/assembler-inl.h"
11 : #include "src/base/bits.h"
12 : #include "src/compilation-cache.h"
13 : #include "src/counters.h"
14 : #include "src/globals.h"
15 : #include "src/heap/heap-inl.h"
16 : #include "src/heap/mark-compact.h"
17 : #include "src/isolate.h"
18 : #include "src/memcopy.h"
19 : #include "src/objects/compilation-cache-inl.h"
20 : #include "src/objects/heap-object.h"
21 : #include "src/objects/js-collection-inl.h"
22 : #include "src/objects/literal-objects-inl.h"
23 : #include "src/objects/slots.h"
24 : #include "src/objects/templates.h"
25 : #include "src/ostreams.h"
26 :
27 : namespace v8 {
28 : namespace internal {
29 :
30 : static base::LazyMutex object_stats_mutex = LAZY_MUTEX_INITIALIZER;
31 :
32 0 : class FieldStatsCollector : public ObjectVisitor {
33 : public:
34 : FieldStatsCollector(size_t* tagged_fields_count,
35 : size_t* embedder_fields_count,
36 : size_t* unboxed_double_fields_count,
37 : size_t* raw_fields_count)
38 : : tagged_fields_count_(tagged_fields_count),
39 : embedder_fields_count_(embedder_fields_count),
40 : unboxed_double_fields_count_(unboxed_double_fields_count),
41 0 : raw_fields_count_(raw_fields_count) {}
42 :
43 0 : void RecordStats(HeapObject host) {
44 0 : size_t old_pointer_fields_count = *tagged_fields_count_;
45 0 : host->Iterate(this);
46 : size_t tagged_fields_count_in_object =
47 0 : *tagged_fields_count_ - old_pointer_fields_count;
48 :
49 0 : int object_size_in_words = host->Size() / kTaggedSize;
50 : DCHECK_LE(tagged_fields_count_in_object, object_size_in_words);
51 : size_t raw_fields_count_in_object =
52 0 : object_size_in_words - tagged_fields_count_in_object;
53 :
54 0 : if (host->IsJSObject()) {
55 0 : JSObjectFieldStats field_stats = GetInobjectFieldStats(host->map());
56 : // Embedder fields are already included into pointer words.
57 : DCHECK_LE(field_stats.embedded_fields_count_,
58 : tagged_fields_count_in_object);
59 0 : tagged_fields_count_in_object -= field_stats.embedded_fields_count_;
60 0 : *tagged_fields_count_ -= field_stats.embedded_fields_count_;
61 0 : *embedder_fields_count_ += field_stats.embedded_fields_count_;
62 :
63 : // The rest are data words.
64 : DCHECK_LE(field_stats.unboxed_double_fields_count_,
65 : raw_fields_count_in_object);
66 0 : raw_fields_count_in_object -= field_stats.unboxed_double_fields_count_;
67 0 : *unboxed_double_fields_count_ += field_stats.unboxed_double_fields_count_;
68 : }
69 0 : *raw_fields_count_ += raw_fields_count_in_object;
70 0 : }
71 :
72 0 : void VisitPointers(HeapObject host, ObjectSlot start,
73 : ObjectSlot end) override {
74 0 : *tagged_fields_count_ += (end - start);
75 0 : }
76 0 : void VisitPointers(HeapObject host, MaybeObjectSlot start,
77 : MaybeObjectSlot end) override {
78 0 : *tagged_fields_count_ += (end - start);
79 0 : }
80 :
81 0 : void VisitCodeTarget(Code host, RelocInfo* rinfo) override {
82 : // Code target is most likely encoded as a relative 32-bit offset and not
83 : // as a full tagged value, so there's nothing to count.
84 0 : }
85 :
86 0 : void VisitEmbeddedPointer(Code host, RelocInfo* rinfo) override {
87 0 : *tagged_fields_count_ += 1;
88 0 : }
89 :
90 : private:
91 : struct JSObjectFieldStats {
92 : JSObjectFieldStats()
93 : : embedded_fields_count_(0), unboxed_double_fields_count_(0) {}
94 :
95 : unsigned embedded_fields_count_ : kDescriptorIndexBitCount;
96 : unsigned unboxed_double_fields_count_ : kDescriptorIndexBitCount;
97 : };
98 : std::unordered_map<Map, JSObjectFieldStats, Object::Hasher>
99 : field_stats_cache_;
100 :
101 : JSObjectFieldStats GetInobjectFieldStats(Map map);
102 :
103 : size_t* const tagged_fields_count_;
104 : size_t* const embedder_fields_count_;
105 : size_t* const unboxed_double_fields_count_;
106 : size_t* const raw_fields_count_;
107 : };
108 :
109 : FieldStatsCollector::JSObjectFieldStats
110 0 : FieldStatsCollector::GetInobjectFieldStats(Map map) {
111 : auto iter = field_stats_cache_.find(map);
112 0 : if (iter != field_stats_cache_.end()) {
113 0 : return iter->second;
114 : }
115 : // Iterate descriptor array and calculate stats.
116 : JSObjectFieldStats stats;
117 0 : stats.embedded_fields_count_ = JSObject::GetEmbedderFieldCount(map);
118 0 : if (!map->is_dictionary_map()) {
119 : int nof = map->NumberOfOwnDescriptors();
120 0 : DescriptorArray descriptors = map->instance_descriptors();
121 0 : for (int descriptor = 0; descriptor < nof; descriptor++) {
122 0 : PropertyDetails details = descriptors->GetDetails(descriptor);
123 0 : if (details.location() == kField) {
124 0 : FieldIndex index = FieldIndex::ForDescriptor(map, descriptor);
125 : // Stop on first out-of-object field.
126 0 : if (!index.is_inobject()) break;
127 : if (details.representation().IsDouble() &&
128 : map->IsUnboxedDoubleField(index)) {
129 : ++stats.unboxed_double_fields_count_;
130 : }
131 : }
132 : }
133 : }
134 0 : field_stats_cache_.insert(std::make_pair(map, stats));
135 0 : return stats;
136 : }
137 :
138 0 : void ObjectStats::ClearObjectStats(bool clear_last_time_stats) {
139 0 : memset(object_counts_, 0, sizeof(object_counts_));
140 0 : memset(object_sizes_, 0, sizeof(object_sizes_));
141 0 : memset(over_allocated_, 0, sizeof(over_allocated_));
142 0 : memset(size_histogram_, 0, sizeof(size_histogram_));
143 0 : memset(over_allocated_histogram_, 0, sizeof(over_allocated_histogram_));
144 0 : if (clear_last_time_stats) {
145 0 : memset(object_counts_last_time_, 0, sizeof(object_counts_last_time_));
146 0 : memset(object_sizes_last_time_, 0, sizeof(object_sizes_last_time_));
147 : }
148 0 : tagged_fields_count_ = 0;
149 0 : embedder_fields_count_ = 0;
150 0 : unboxed_double_fields_count_ = 0;
151 0 : raw_fields_count_ = 0;
152 0 : }
153 :
154 : // Tell the compiler to never inline this: occasionally, the optimizer will
155 : // decide to inline this and unroll the loop, making the compiled code more than
156 : // 100KB larger.
157 0 : V8_NOINLINE static void PrintJSONArray(size_t* array, const int len) {
158 0 : PrintF("[ ");
159 0 : for (int i = 0; i < len; i++) {
160 0 : PrintF("%zu", array[i]);
161 0 : if (i != (len - 1)) PrintF(", ");
162 : }
163 0 : PrintF(" ]");
164 0 : }
165 :
166 0 : V8_NOINLINE static void DumpJSONArray(std::stringstream& stream, size_t* array,
167 : const int len) {
168 0 : stream << PrintCollection(Vector<size_t>(array, len));
169 0 : }
170 :
171 0 : void ObjectStats::PrintKeyAndId(const char* key, int gc_count) {
172 : PrintF("\"isolate\": \"%p\", \"id\": %d, \"key\": \"%s\", ",
173 0 : reinterpret_cast<void*>(isolate()), gc_count, key);
174 0 : }
175 :
176 0 : void ObjectStats::PrintInstanceTypeJSON(const char* key, int gc_count,
177 : const char* name, int index) {
178 0 : PrintF("{ ");
179 : PrintKeyAndId(key, gc_count);
180 0 : PrintF("\"type\": \"instance_type_data\", ");
181 0 : PrintF("\"instance_type\": %d, ", index);
182 0 : PrintF("\"instance_type_name\": \"%s\", ", name);
183 0 : PrintF("\"overall\": %zu, ", object_sizes_[index]);
184 0 : PrintF("\"count\": %zu, ", object_counts_[index]);
185 0 : PrintF("\"over_allocated\": %zu, ", over_allocated_[index]);
186 0 : PrintF("\"histogram\": ");
187 0 : PrintJSONArray(size_histogram_[index], kNumberOfBuckets);
188 0 : PrintF(",");
189 0 : PrintF("\"over_allocated_histogram\": ");
190 0 : PrintJSONArray(over_allocated_histogram_[index], kNumberOfBuckets);
191 0 : PrintF(" }\n");
192 0 : }
193 :
194 0 : void ObjectStats::PrintJSON(const char* key) {
195 : double time = isolate()->time_millis_since_init();
196 : int gc_count = heap()->gc_count();
197 :
198 : // gc_descriptor
199 0 : PrintF("{ ");
200 : PrintKeyAndId(key, gc_count);
201 0 : PrintF("\"type\": \"gc_descriptor\", \"time\": %f }\n", time);
202 : // field_data
203 0 : PrintF("{ ");
204 : PrintKeyAndId(key, gc_count);
205 0 : PrintF("\"type\": \"field_data\"");
206 0 : PrintF(", \"tagged_fields\": %zu", tagged_fields_count_ * kTaggedSize);
207 0 : PrintF(", \"embedder_fields\": %zu",
208 0 : embedder_fields_count_ * kEmbedderDataSlotSize);
209 0 : PrintF(", \"unboxed_double_fields\": %zu",
210 0 : unboxed_double_fields_count_ * kDoubleSize);
211 0 : PrintF(", \"other_raw_fields\": %zu", raw_fields_count_ * kSystemPointerSize);
212 0 : PrintF(" }\n");
213 : // bucket_sizes
214 0 : PrintF("{ ");
215 : PrintKeyAndId(key, gc_count);
216 0 : PrintF("\"type\": \"bucket_sizes\", \"sizes\": [ ");
217 0 : for (int i = 0; i < kNumberOfBuckets; i++) {
218 0 : PrintF("%d", 1 << (kFirstBucketShift + i));
219 0 : if (i != (kNumberOfBuckets - 1)) PrintF(", ");
220 : }
221 0 : PrintF(" ] }\n");
222 :
223 : #define INSTANCE_TYPE_WRAPPER(name) \
224 : PrintInstanceTypeJSON(key, gc_count, #name, name);
225 :
226 : #define VIRTUAL_INSTANCE_TYPE_WRAPPER(name) \
227 : PrintInstanceTypeJSON(key, gc_count, #name, FIRST_VIRTUAL_TYPE + name);
228 :
229 0 : INSTANCE_TYPE_LIST(INSTANCE_TYPE_WRAPPER)
230 0 : VIRTUAL_INSTANCE_TYPE_LIST(VIRTUAL_INSTANCE_TYPE_WRAPPER)
231 :
232 : #undef INSTANCE_TYPE_WRAPPER
233 : #undef VIRTUAL_INSTANCE_TYPE_WRAPPER
234 0 : }
235 :
236 0 : void ObjectStats::DumpInstanceTypeData(std::stringstream& stream,
237 : const char* name, int index) {
238 0 : stream << "\"" << name << "\":{";
239 0 : stream << "\"type\":" << static_cast<int>(index) << ",";
240 0 : stream << "\"overall\":" << object_sizes_[index] << ",";
241 0 : stream << "\"count\":" << object_counts_[index] << ",";
242 0 : stream << "\"over_allocated\":" << over_allocated_[index] << ",";
243 0 : stream << "\"histogram\":";
244 0 : DumpJSONArray(stream, size_histogram_[index], kNumberOfBuckets);
245 0 : stream << ",\"over_allocated_histogram\":";
246 0 : DumpJSONArray(stream, over_allocated_histogram_[index], kNumberOfBuckets);
247 0 : stream << "},";
248 0 : }
249 :
250 0 : void ObjectStats::Dump(std::stringstream& stream) {
251 : double time = isolate()->time_millis_since_init();
252 : int gc_count = heap()->gc_count();
253 :
254 0 : stream << "{";
255 0 : stream << "\"isolate\":\"" << reinterpret_cast<void*>(isolate()) << "\",";
256 0 : stream << "\"id\":" << gc_count << ",";
257 0 : stream << "\"time\":" << time << ",";
258 :
259 : // field_data
260 0 : stream << "\"field_data\":{";
261 0 : stream << "\"tagged_fields\":" << (tagged_fields_count_ * kTaggedSize);
262 : stream << ",\"embedder_fields\":"
263 0 : << (embedder_fields_count_ * kEmbedderDataSlotSize);
264 : stream << ",\"unboxed_double_fields\": "
265 0 : << (unboxed_double_fields_count_ * kDoubleSize);
266 : stream << ",\"other_raw_fields\":"
267 0 : << (raw_fields_count_ * kSystemPointerSize);
268 0 : stream << "}, ";
269 :
270 0 : stream << "\"bucket_sizes\":[";
271 0 : for (int i = 0; i < kNumberOfBuckets; i++) {
272 0 : stream << (1 << (kFirstBucketShift + i));
273 0 : if (i != (kNumberOfBuckets - 1)) stream << ",";
274 : }
275 0 : stream << "],";
276 0 : stream << "\"type_data\":{";
277 :
278 : #define INSTANCE_TYPE_WRAPPER(name) DumpInstanceTypeData(stream, #name, name);
279 :
280 : #define VIRTUAL_INSTANCE_TYPE_WRAPPER(name) \
281 : DumpInstanceTypeData(stream, #name, FIRST_VIRTUAL_TYPE + name);
282 :
283 0 : INSTANCE_TYPE_LIST(INSTANCE_TYPE_WRAPPER);
284 0 : VIRTUAL_INSTANCE_TYPE_LIST(VIRTUAL_INSTANCE_TYPE_WRAPPER)
285 0 : stream << "\"END\":{}}}";
286 :
287 : #undef INSTANCE_TYPE_WRAPPER
288 : #undef VIRTUAL_INSTANCE_TYPE_WRAPPER
289 0 : }
290 :
291 0 : void ObjectStats::CheckpointObjectStats() {
292 : base::MutexGuard lock_guard(object_stats_mutex.Pointer());
293 0 : MemCopy(object_counts_last_time_, object_counts_, sizeof(object_counts_));
294 0 : MemCopy(object_sizes_last_time_, object_sizes_, sizeof(object_sizes_));
295 0 : ClearObjectStats();
296 0 : }
297 :
298 : namespace {
299 :
300 : int Log2ForSize(size_t size) {
301 : DCHECK_GT(size, 0);
302 0 : return kSizetSize * 8 - 1 - base::bits::CountLeadingZeros(size);
303 : }
304 :
305 : } // namespace
306 :
307 0 : int ObjectStats::HistogramIndexFromSize(size_t size) {
308 0 : if (size == 0) return 0;
309 0 : return Min(Max(Log2ForSize(size) + 1 - kFirstBucketShift, 0),
310 0 : kLastValueBucketIndex);
311 : }
312 :
313 0 : void ObjectStats::RecordObjectStats(InstanceType type, size_t size) {
314 : DCHECK_LE(type, LAST_TYPE);
315 0 : object_counts_[type]++;
316 0 : object_sizes_[type] += size;
317 0 : size_histogram_[type][HistogramIndexFromSize(size)]++;
318 0 : }
319 :
320 0 : void ObjectStats::RecordVirtualObjectStats(VirtualInstanceType type,
321 : size_t size, size_t over_allocated) {
322 : DCHECK_LE(type, LAST_VIRTUAL_TYPE);
323 0 : object_counts_[FIRST_VIRTUAL_TYPE + type]++;
324 0 : object_sizes_[FIRST_VIRTUAL_TYPE + type] += size;
325 0 : size_histogram_[FIRST_VIRTUAL_TYPE + type][HistogramIndexFromSize(size)]++;
326 0 : over_allocated_[FIRST_VIRTUAL_TYPE + type] += over_allocated;
327 : over_allocated_histogram_[FIRST_VIRTUAL_TYPE + type]
328 0 : [HistogramIndexFromSize(size)]++;
329 0 : }
330 :
331 0 : Isolate* ObjectStats::isolate() { return heap()->isolate(); }
332 :
333 0 : class ObjectStatsCollectorImpl {
334 : public:
335 : enum Phase {
336 : kPhase1,
337 : kPhase2,
338 : };
339 : static const int kNumberOfPhases = kPhase2 + 1;
340 :
341 : ObjectStatsCollectorImpl(Heap* heap, ObjectStats* stats);
342 :
343 : void CollectGlobalStatistics();
344 :
345 : enum class CollectFieldStats { kNo, kYes };
346 : void CollectStatistics(HeapObject obj, Phase phase,
347 : CollectFieldStats collect_field_stats);
348 :
349 : private:
350 : enum CowMode {
351 : kCheckCow,
352 : kIgnoreCow,
353 : };
354 :
355 : Isolate* isolate() { return heap_->isolate(); }
356 :
357 : bool RecordVirtualObjectStats(HeapObject parent, HeapObject obj,
358 : ObjectStats::VirtualInstanceType type,
359 : size_t size, size_t over_allocated,
360 : CowMode check_cow_array = kCheckCow);
361 : void RecordExternalResourceStats(Address resource,
362 : ObjectStats::VirtualInstanceType type,
363 : size_t size);
364 : // Gets size from |ob| and assumes no over allocating.
365 : bool RecordSimpleVirtualObjectStats(HeapObject parent, HeapObject obj,
366 : ObjectStats::VirtualInstanceType type);
367 : // For HashTable it is possible to compute over allocated memory.
368 : void RecordHashTableVirtualObjectStats(HeapObject parent,
369 : FixedArray hash_table,
370 : ObjectStats::VirtualInstanceType type);
371 :
372 : bool SameLiveness(HeapObject obj1, HeapObject obj2);
373 : bool CanRecordFixedArray(FixedArrayBase array);
374 : bool IsCowArray(FixedArrayBase array);
375 :
376 : // Blacklist for objects that should not be recorded using
377 : // VirtualObjectStats and RecordSimpleVirtualObjectStats. For recording those
378 : // objects dispatch to the low level ObjectStats::RecordObjectStats manually.
379 : bool ShouldRecordObject(HeapObject object, CowMode check_cow_array);
380 :
381 : void RecordObjectStats(HeapObject obj, InstanceType type, size_t size);
382 :
383 : // Specific recursion into constant pool or embedded code objects. Records
384 : // FixedArrays and Tuple2.
385 : void RecordVirtualObjectsForConstantPoolOrEmbeddedObjects(
386 : HeapObject parent, HeapObject object,
387 : ObjectStats::VirtualInstanceType type);
388 :
389 : // Details.
390 : void RecordVirtualAllocationSiteDetails(AllocationSite site);
391 : void RecordVirtualBytecodeArrayDetails(BytecodeArray bytecode);
392 : void RecordVirtualCodeDetails(Code code);
393 : void RecordVirtualContext(Context context);
394 : void RecordVirtualFeedbackVectorDetails(FeedbackVector vector);
395 : void RecordVirtualFixedArrayDetails(FixedArray array);
396 : void RecordVirtualFunctionTemplateInfoDetails(FunctionTemplateInfo fti);
397 : void RecordVirtualJSGlobalObjectDetails(JSGlobalObject object);
398 : void RecordVirtualJSCollectionDetails(JSObject object);
399 : void RecordVirtualJSObjectDetails(JSObject object);
400 : void RecordVirtualMapDetails(Map map);
401 : void RecordVirtualScriptDetails(Script script);
402 : void RecordVirtualExternalStringDetails(ExternalString script);
403 : void RecordVirtualSharedFunctionInfoDetails(SharedFunctionInfo info);
404 : void RecordVirtualJSFunctionDetails(JSFunction function);
405 :
406 : void RecordVirtualArrayBoilerplateDescription(
407 : ArrayBoilerplateDescription description);
408 : Heap* heap_;
409 : ObjectStats* stats_;
410 : MarkCompactCollector::NonAtomicMarkingState* marking_state_;
411 : std::unordered_set<HeapObject, Object::Hasher> virtual_objects_;
412 : std::unordered_set<Address> external_resources_;
413 : FieldStatsCollector field_stats_collector_;
414 : };
415 :
416 0 : ObjectStatsCollectorImpl::ObjectStatsCollectorImpl(Heap* heap,
417 : ObjectStats* stats)
418 : : heap_(heap),
419 : stats_(stats),
420 : marking_state_(
421 : heap->mark_compact_collector()->non_atomic_marking_state()),
422 : field_stats_collector_(
423 : &stats->tagged_fields_count_, &stats->embedder_fields_count_,
424 0 : &stats->unboxed_double_fields_count_, &stats->raw_fields_count_) {}
425 :
426 0 : bool ObjectStatsCollectorImpl::ShouldRecordObject(HeapObject obj,
427 : CowMode check_cow_array) {
428 0 : if (obj->IsFixedArrayExact()) {
429 : FixedArray fixed_array = FixedArray::cast(obj);
430 0 : bool cow_check = check_cow_array == kIgnoreCow || !IsCowArray(fixed_array);
431 0 : return CanRecordFixedArray(fixed_array) && cow_check;
432 : }
433 0 : if (obj == ReadOnlyRoots(heap_).empty_property_array()) return false;
434 0 : return true;
435 : }
436 :
437 0 : void ObjectStatsCollectorImpl::RecordHashTableVirtualObjectStats(
438 : HeapObject parent, FixedArray hash_table,
439 : ObjectStats::VirtualInstanceType type) {
440 0 : CHECK(hash_table->IsHashTable());
441 : // TODO(mlippautz): Implement over allocation for hash tables.
442 0 : RecordVirtualObjectStats(parent, hash_table, type, hash_table->Size(),
443 0 : ObjectStats::kNoOverAllocation);
444 0 : }
445 :
446 0 : bool ObjectStatsCollectorImpl::RecordSimpleVirtualObjectStats(
447 : HeapObject parent, HeapObject obj, ObjectStats::VirtualInstanceType type) {
448 0 : return RecordVirtualObjectStats(parent, obj, type, obj->Size(),
449 0 : ObjectStats::kNoOverAllocation, kCheckCow);
450 : }
451 :
452 0 : bool ObjectStatsCollectorImpl::RecordVirtualObjectStats(
453 : HeapObject parent, HeapObject obj, ObjectStats::VirtualInstanceType type,
454 : size_t size, size_t over_allocated, CowMode check_cow_array) {
455 0 : if (!SameLiveness(parent, obj) || !ShouldRecordObject(obj, check_cow_array)) {
456 : return false;
457 : }
458 :
459 0 : if (virtual_objects_.find(obj) == virtual_objects_.end()) {
460 : virtual_objects_.insert(obj);
461 0 : stats_->RecordVirtualObjectStats(type, size, over_allocated);
462 0 : return true;
463 : }
464 : return false;
465 : }
466 :
467 0 : void ObjectStatsCollectorImpl::RecordExternalResourceStats(
468 : Address resource, ObjectStats::VirtualInstanceType type, size_t size) {
469 0 : if (external_resources_.find(resource) == external_resources_.end()) {
470 : external_resources_.insert(resource);
471 0 : stats_->RecordVirtualObjectStats(type, size, 0);
472 : }
473 0 : }
474 :
475 0 : void ObjectStatsCollectorImpl::RecordVirtualAllocationSiteDetails(
476 : AllocationSite site) {
477 0 : if (!site->PointsToLiteral()) return;
478 0 : JSObject boilerplate = site->boilerplate();
479 0 : if (boilerplate->IsJSArray()) {
480 : RecordSimpleVirtualObjectStats(site, boilerplate,
481 0 : ObjectStats::JS_ARRAY_BOILERPLATE_TYPE);
482 : // Array boilerplates cannot have properties.
483 : } else {
484 0 : RecordVirtualObjectStats(
485 : site, boilerplate, ObjectStats::JS_OBJECT_BOILERPLATE_TYPE,
486 0 : boilerplate->Size(), ObjectStats::kNoOverAllocation);
487 0 : if (boilerplate->HasFastProperties()) {
488 : // We'll mis-classify the empty_property_array here. Given that there is a
489 : // single instance, this is negligible.
490 0 : PropertyArray properties = boilerplate->property_array();
491 : RecordSimpleVirtualObjectStats(
492 0 : site, properties, ObjectStats::BOILERPLATE_PROPERTY_ARRAY_TYPE);
493 : } else {
494 0 : NameDictionary properties = boilerplate->property_dictionary();
495 : RecordSimpleVirtualObjectStats(
496 0 : site, properties, ObjectStats::BOILERPLATE_PROPERTY_DICTIONARY_TYPE);
497 : }
498 : }
499 0 : FixedArrayBase elements = boilerplate->elements();
500 : RecordSimpleVirtualObjectStats(site, elements,
501 0 : ObjectStats::BOILERPLATE_ELEMENTS_TYPE);
502 : }
503 :
504 0 : void ObjectStatsCollectorImpl::RecordVirtualFunctionTemplateInfoDetails(
505 : FunctionTemplateInfo fti) {
506 : // named_property_handler and indexed_property_handler are recorded as
507 : // INTERCEPTOR_INFO_TYPE.
508 0 : if (!fti->call_code()->IsUndefined(isolate())) {
509 : RecordSimpleVirtualObjectStats(
510 0 : fti, CallHandlerInfo::cast(fti->call_code()),
511 0 : ObjectStats::FUNCTION_TEMPLATE_INFO_ENTRIES_TYPE);
512 : }
513 0 : if (!fti->GetInstanceCallHandler()->IsUndefined(isolate())) {
514 : RecordSimpleVirtualObjectStats(
515 0 : fti, CallHandlerInfo::cast(fti->GetInstanceCallHandler()),
516 0 : ObjectStats::FUNCTION_TEMPLATE_INFO_ENTRIES_TYPE);
517 : }
518 0 : }
519 :
520 0 : void ObjectStatsCollectorImpl::RecordVirtualJSGlobalObjectDetails(
521 : JSGlobalObject object) {
522 : // Properties.
523 0 : GlobalDictionary properties = object->global_dictionary();
524 : RecordHashTableVirtualObjectStats(object, properties,
525 0 : ObjectStats::GLOBAL_PROPERTIES_TYPE);
526 : // Elements.
527 0 : FixedArrayBase elements = object->elements();
528 : RecordSimpleVirtualObjectStats(object, elements,
529 0 : ObjectStats::GLOBAL_ELEMENTS_TYPE);
530 0 : }
531 :
532 0 : void ObjectStatsCollectorImpl::RecordVirtualJSCollectionDetails(
533 : JSObject object) {
534 0 : if (object->IsJSMap()) {
535 : RecordSimpleVirtualObjectStats(
536 0 : object, FixedArray::cast(JSMap::cast(object)->table()),
537 0 : ObjectStats::JS_COLLECTION_TABLE_TYPE);
538 : }
539 0 : if (object->IsJSSet()) {
540 : RecordSimpleVirtualObjectStats(
541 0 : object, FixedArray::cast(JSSet::cast(object)->table()),
542 0 : ObjectStats::JS_COLLECTION_TABLE_TYPE);
543 : }
544 0 : }
545 :
546 0 : void ObjectStatsCollectorImpl::RecordVirtualJSObjectDetails(JSObject object) {
547 : // JSGlobalObject is recorded separately.
548 0 : if (object->IsJSGlobalObject()) return;
549 :
550 : // Properties.
551 0 : if (object->HasFastProperties()) {
552 0 : PropertyArray properties = object->property_array();
553 0 : CHECK_EQ(PROPERTY_ARRAY_TYPE, properties->map()->instance_type());
554 : } else {
555 0 : NameDictionary properties = object->property_dictionary();
556 : RecordHashTableVirtualObjectStats(
557 0 : object, properties, ObjectStats::OBJECT_PROPERTY_DICTIONARY_TYPE);
558 : }
559 : // Elements.
560 0 : FixedArrayBase elements = object->elements();
561 0 : RecordSimpleVirtualObjectStats(object, elements, ObjectStats::ELEMENTS_TYPE);
562 : }
563 :
564 0 : static ObjectStats::VirtualInstanceType GetFeedbackSlotType(
565 : MaybeObject maybe_obj, FeedbackSlotKind kind, Isolate* isolate) {
566 0 : if (maybe_obj->IsCleared())
567 : return ObjectStats::FEEDBACK_VECTOR_SLOT_OTHER_TYPE;
568 : Object obj = maybe_obj->GetHeapObjectOrSmi();
569 0 : switch (kind) {
570 : case FeedbackSlotKind::kCall:
571 0 : if (obj == *isolate->factory()->uninitialized_symbol() ||
572 : obj == *isolate->factory()->premonomorphic_symbol()) {
573 : return ObjectStats::FEEDBACK_VECTOR_SLOT_CALL_UNUSED_TYPE;
574 : }
575 0 : return ObjectStats::FEEDBACK_VECTOR_SLOT_CALL_TYPE;
576 :
577 : case FeedbackSlotKind::kLoadProperty:
578 : case FeedbackSlotKind::kLoadGlobalInsideTypeof:
579 : case FeedbackSlotKind::kLoadGlobalNotInsideTypeof:
580 : case FeedbackSlotKind::kLoadKeyed:
581 : case FeedbackSlotKind::kHasKeyed:
582 0 : if (obj == *isolate->factory()->uninitialized_symbol() ||
583 : obj == *isolate->factory()->premonomorphic_symbol()) {
584 : return ObjectStats::FEEDBACK_VECTOR_SLOT_LOAD_UNUSED_TYPE;
585 : }
586 0 : return ObjectStats::FEEDBACK_VECTOR_SLOT_LOAD_TYPE;
587 :
588 : case FeedbackSlotKind::kStoreNamedSloppy:
589 : case FeedbackSlotKind::kStoreNamedStrict:
590 : case FeedbackSlotKind::kStoreOwnNamed:
591 : case FeedbackSlotKind::kStoreGlobalSloppy:
592 : case FeedbackSlotKind::kStoreGlobalStrict:
593 : case FeedbackSlotKind::kStoreKeyedSloppy:
594 : case FeedbackSlotKind::kStoreKeyedStrict:
595 0 : if (obj == *isolate->factory()->uninitialized_symbol() ||
596 : obj == *isolate->factory()->premonomorphic_symbol()) {
597 : return ObjectStats::FEEDBACK_VECTOR_SLOT_STORE_UNUSED_TYPE;
598 : }
599 0 : return ObjectStats::FEEDBACK_VECTOR_SLOT_STORE_TYPE;
600 :
601 : case FeedbackSlotKind::kBinaryOp:
602 : case FeedbackSlotKind::kCompareOp:
603 : return ObjectStats::FEEDBACK_VECTOR_SLOT_ENUM_TYPE;
604 :
605 : default:
606 0 : return ObjectStats::FEEDBACK_VECTOR_SLOT_OTHER_TYPE;
607 : }
608 : }
609 :
610 0 : void ObjectStatsCollectorImpl::RecordVirtualFeedbackVectorDetails(
611 : FeedbackVector vector) {
612 0 : if (virtual_objects_.find(vector) != virtual_objects_.end()) return;
613 : // Manually insert the feedback vector into the virtual object list, since
614 : // we're logging its component parts separately.
615 : virtual_objects_.insert(vector);
616 :
617 : size_t calculated_size = 0;
618 :
619 : // Log the feedback_cell array used for create closures.
620 0 : RecordVirtualObjectStats(
621 0 : vector, vector->closure_feedback_cell_array(),
622 : ObjectStats::FEEDBACK_VECTOR_CREATE_CLOSURE_ARRAY_TYPE,
623 0 : vector->closure_feedback_cell_array()->Size(),
624 0 : ObjectStats::kNoOverAllocation);
625 :
626 : // Log the feedback vector's header (fixed fields).
627 : size_t header_size = vector->slots_start().address() - vector->address();
628 0 : stats_->RecordVirtualObjectStats(ObjectStats::FEEDBACK_VECTOR_HEADER_TYPE,
629 0 : header_size, ObjectStats::kNoOverAllocation);
630 : calculated_size += header_size;
631 :
632 : // Iterate over the feedback slots and log each one.
633 0 : if (!vector->shared_function_info()->HasFeedbackMetadata()) return;
634 :
635 0 : FeedbackMetadataIterator it(vector->metadata());
636 0 : while (it.HasNext()) {
637 0 : FeedbackSlot slot = it.Next();
638 : // Log the entry (or entries) taken up by this slot.
639 0 : size_t slot_size = it.entry_size() * kTaggedSize;
640 0 : stats_->RecordVirtualObjectStats(
641 0 : GetFeedbackSlotType(vector->Get(slot), it.kind(), heap_->isolate()),
642 0 : slot_size, ObjectStats::kNoOverAllocation);
643 0 : calculated_size += slot_size;
644 :
645 : // Log the monomorphic/polymorphic helper objects that this slot owns.
646 0 : for (int i = 0; i < it.entry_size(); i++) {
647 0 : MaybeObject raw_object = vector->get(slot.ToInt() + i);
648 : HeapObject object;
649 0 : if (raw_object->GetHeapObject(&object)) {
650 0 : if (object->IsCell() || object->IsWeakFixedArray()) {
651 : RecordSimpleVirtualObjectStats(
652 0 : vector, object, ObjectStats::FEEDBACK_VECTOR_ENTRY_TYPE);
653 : }
654 : }
655 : }
656 : }
657 :
658 0 : CHECK_EQ(calculated_size, vector->Size());
659 : }
660 :
661 0 : void ObjectStatsCollectorImpl::RecordVirtualFixedArrayDetails(
662 : FixedArray array) {
663 0 : if (IsCowArray(array)) {
664 0 : RecordVirtualObjectStats(HeapObject(), array, ObjectStats::COW_ARRAY_TYPE,
665 0 : array->Size(), ObjectStats::kNoOverAllocation,
666 0 : kIgnoreCow);
667 : }
668 0 : }
669 :
670 0 : void ObjectStatsCollectorImpl::CollectStatistics(
671 : HeapObject obj, Phase phase, CollectFieldStats collect_field_stats) {
672 : Map map = obj->map();
673 0 : switch (phase) {
674 : case kPhase1:
675 0 : if (obj->IsFeedbackVector()) {
676 0 : RecordVirtualFeedbackVectorDetails(FeedbackVector::cast(obj));
677 0 : } else if (obj->IsMap()) {
678 0 : RecordVirtualMapDetails(Map::cast(obj));
679 0 : } else if (obj->IsBytecodeArray()) {
680 0 : RecordVirtualBytecodeArrayDetails(BytecodeArray::cast(obj));
681 0 : } else if (obj->IsCode()) {
682 0 : RecordVirtualCodeDetails(Code::cast(obj));
683 0 : } else if (obj->IsFunctionTemplateInfo()) {
684 : RecordVirtualFunctionTemplateInfoDetails(
685 0 : FunctionTemplateInfo::cast(obj));
686 0 : } else if (obj->IsJSFunction()) {
687 0 : RecordVirtualJSFunctionDetails(JSFunction::cast(obj));
688 0 : } else if (obj->IsJSGlobalObject()) {
689 0 : RecordVirtualJSGlobalObjectDetails(JSGlobalObject::cast(obj));
690 0 : } else if (obj->IsJSObject()) {
691 : // This phase needs to come after RecordVirtualAllocationSiteDetails
692 : // to properly split among boilerplates.
693 0 : RecordVirtualJSObjectDetails(JSObject::cast(obj));
694 0 : } else if (obj->IsJSCollection()) {
695 0 : RecordVirtualJSCollectionDetails(JSObject::cast(obj));
696 0 : } else if (obj->IsSharedFunctionInfo()) {
697 0 : RecordVirtualSharedFunctionInfoDetails(SharedFunctionInfo::cast(obj));
698 0 : } else if (obj->IsContext()) {
699 0 : RecordVirtualContext(Context::cast(obj));
700 0 : } else if (obj->IsScript()) {
701 0 : RecordVirtualScriptDetails(Script::cast(obj));
702 0 : } else if (obj->IsArrayBoilerplateDescription()) {
703 : RecordVirtualArrayBoilerplateDescription(
704 0 : ArrayBoilerplateDescription::cast(obj));
705 0 : } else if (obj->IsFixedArrayExact()) {
706 : // Has to go last as it triggers too eagerly.
707 0 : RecordVirtualFixedArrayDetails(FixedArray::cast(obj));
708 : }
709 : break;
710 : case kPhase2:
711 0 : if (obj->IsExternalString()) {
712 : // This has to be in Phase2 to avoid conflicting with recording Script
713 : // sources. We still want to run RecordObjectStats after though.
714 0 : RecordVirtualExternalStringDetails(ExternalString::cast(obj));
715 : }
716 0 : RecordObjectStats(obj, map->instance_type(), obj->Size());
717 0 : if (collect_field_stats == CollectFieldStats::kYes) {
718 0 : field_stats_collector_.RecordStats(obj);
719 : }
720 : break;
721 : }
722 0 : }
723 :
724 0 : void ObjectStatsCollectorImpl::CollectGlobalStatistics() {
725 : // Iterate boilerplates first to disambiguate them from regular JS objects.
726 0 : Object list = heap_->allocation_sites_list();
727 0 : while (list->IsAllocationSite()) {
728 : AllocationSite site = AllocationSite::cast(list);
729 0 : RecordVirtualAllocationSiteDetails(site);
730 : list = site->weak_next();
731 : }
732 :
733 : // FixedArray.
734 0 : RecordSimpleVirtualObjectStats(HeapObject(), heap_->serialized_objects(),
735 0 : ObjectStats::SERIALIZED_OBJECTS_TYPE);
736 0 : RecordSimpleVirtualObjectStats(HeapObject(), heap_->number_string_cache(),
737 0 : ObjectStats::NUMBER_STRING_CACHE_TYPE);
738 0 : RecordSimpleVirtualObjectStats(
739 0 : HeapObject(), heap_->single_character_string_cache(),
740 0 : ObjectStats::SINGLE_CHARACTER_STRING_CACHE_TYPE);
741 0 : RecordSimpleVirtualObjectStats(HeapObject(), heap_->string_split_cache(),
742 0 : ObjectStats::STRING_SPLIT_CACHE_TYPE);
743 0 : RecordSimpleVirtualObjectStats(HeapObject(), heap_->regexp_multiple_cache(),
744 0 : ObjectStats::REGEXP_MULTIPLE_CACHE_TYPE);
745 0 : RecordSimpleVirtualObjectStats(HeapObject(), heap_->retained_maps(),
746 0 : ObjectStats::RETAINED_MAPS_TYPE);
747 :
748 : // WeakArrayList.
749 0 : RecordSimpleVirtualObjectStats(
750 : HeapObject(),
751 0 : WeakArrayList::cast(heap_->noscript_shared_function_infos()),
752 0 : ObjectStats::NOSCRIPT_SHARED_FUNCTION_INFOS_TYPE);
753 0 : RecordSimpleVirtualObjectStats(HeapObject(),
754 0 : WeakArrayList::cast(heap_->script_list()),
755 0 : ObjectStats::SCRIPT_LIST_TYPE);
756 0 : }
757 :
758 0 : void ObjectStatsCollectorImpl::RecordObjectStats(HeapObject obj,
759 : InstanceType type,
760 : size_t size) {
761 0 : if (virtual_objects_.find(obj) == virtual_objects_.end()) {
762 0 : stats_->RecordObjectStats(type, size);
763 : }
764 0 : }
765 :
766 0 : bool ObjectStatsCollectorImpl::CanRecordFixedArray(FixedArrayBase array) {
767 0 : ReadOnlyRoots roots(heap_);
768 0 : return array != roots.empty_fixed_array() &&
769 0 : array != roots.empty_sloppy_arguments_elements() &&
770 0 : array != roots.empty_slow_element_dictionary() &&
771 0 : array != roots.empty_property_dictionary();
772 : }
773 :
774 0 : bool ObjectStatsCollectorImpl::IsCowArray(FixedArrayBase array) {
775 0 : return array->map() == ReadOnlyRoots(heap_).fixed_cow_array_map();
776 : }
777 :
778 0 : bool ObjectStatsCollectorImpl::SameLiveness(HeapObject obj1, HeapObject obj2) {
779 0 : return obj1.is_null() || obj2.is_null() ||
780 0 : marking_state_->Color(obj1) == marking_state_->Color(obj2);
781 : }
782 :
783 0 : void ObjectStatsCollectorImpl::RecordVirtualMapDetails(Map map) {
784 : // TODO(mlippautz): map->dependent_code(): DEPENDENT_CODE_TYPE.
785 :
786 : DescriptorArray array = map->instance_descriptors();
787 0 : if (map->owns_descriptors() &&
788 0 : array != ReadOnlyRoots(heap_).empty_descriptor_array()) {
789 : // DescriptorArray has its own instance type.
790 : EnumCache enum_cache = array->enum_cache();
791 0 : RecordSimpleVirtualObjectStats(array, enum_cache->keys(),
792 0 : ObjectStats::ENUM_CACHE_TYPE);
793 0 : RecordSimpleVirtualObjectStats(array, enum_cache->indices(),
794 0 : ObjectStats::ENUM_INDICES_CACHE_TYPE);
795 : }
796 :
797 0 : if (map->is_prototype_map()) {
798 0 : if (map->prototype_info()->IsPrototypeInfo()) {
799 : PrototypeInfo info = PrototypeInfo::cast(map->prototype_info());
800 : Object users = info->prototype_users();
801 0 : if (users->IsWeakFixedArray()) {
802 0 : RecordSimpleVirtualObjectStats(map, WeakArrayList::cast(users),
803 0 : ObjectStats::PROTOTYPE_USERS_TYPE);
804 : }
805 : }
806 : }
807 0 : }
808 :
809 0 : void ObjectStatsCollectorImpl::RecordVirtualScriptDetails(Script script) {
810 : RecordSimpleVirtualObjectStats(
811 0 : script, script->shared_function_infos(),
812 0 : ObjectStats::SCRIPT_SHARED_FUNCTION_INFOS_TYPE);
813 :
814 : // Log the size of external source code.
815 : Object raw_source = script->source();
816 0 : if (raw_source->IsExternalString()) {
817 : // The contents of external strings aren't on the heap, so we have to record
818 : // them manually. The on-heap String object is recorded indepentendely in
819 : // the normal pass.
820 0 : ExternalString string = ExternalString::cast(raw_source);
821 : Address resource = string->resource_as_address();
822 0 : size_t off_heap_size = string->ExternalPayloadSize();
823 0 : RecordExternalResourceStats(
824 : resource,
825 0 : string->IsOneByteRepresentation()
826 : ? ObjectStats::SCRIPT_SOURCE_EXTERNAL_ONE_BYTE_TYPE
827 : : ObjectStats::SCRIPT_SOURCE_EXTERNAL_TWO_BYTE_TYPE,
828 0 : off_heap_size);
829 0 : } else if (raw_source->IsString()) {
830 0 : String source = String::cast(raw_source);
831 0 : RecordSimpleVirtualObjectStats(
832 : script, source,
833 0 : source->IsOneByteRepresentation()
834 : ? ObjectStats::SCRIPT_SOURCE_NON_EXTERNAL_ONE_BYTE_TYPE
835 0 : : ObjectStats::SCRIPT_SOURCE_NON_EXTERNAL_TWO_BYTE_TYPE);
836 : }
837 0 : }
838 :
839 0 : void ObjectStatsCollectorImpl::RecordVirtualExternalStringDetails(
840 : ExternalString string) {
841 : // Track the external string resource size in a separate category.
842 :
843 : Address resource = string->resource_as_address();
844 0 : size_t off_heap_size = string->ExternalPayloadSize();
845 0 : RecordExternalResourceStats(
846 : resource,
847 0 : string->IsOneByteRepresentation()
848 : ? ObjectStats::STRING_EXTERNAL_RESOURCE_ONE_BYTE_TYPE
849 : : ObjectStats::STRING_EXTERNAL_RESOURCE_TWO_BYTE_TYPE,
850 0 : off_heap_size);
851 0 : }
852 :
853 0 : void ObjectStatsCollectorImpl::RecordVirtualSharedFunctionInfoDetails(
854 : SharedFunctionInfo info) {
855 : // Uncompiled SharedFunctionInfo gets its own category.
856 0 : if (!info->is_compiled()) {
857 0 : RecordSimpleVirtualObjectStats(
858 0 : HeapObject(), info, ObjectStats::UNCOMPILED_SHARED_FUNCTION_INFO_TYPE);
859 : }
860 0 : }
861 :
862 0 : void ObjectStatsCollectorImpl::RecordVirtualJSFunctionDetails(
863 : JSFunction function) {
864 : // Uncompiled JSFunctions get their own category.
865 0 : if (!function->is_compiled()) {
866 0 : RecordSimpleVirtualObjectStats(HeapObject(), function,
867 0 : ObjectStats::UNCOMPILED_JS_FUNCTION_TYPE);
868 : }
869 0 : }
870 0 : void ObjectStatsCollectorImpl::RecordVirtualArrayBoilerplateDescription(
871 : ArrayBoilerplateDescription description) {
872 : RecordVirtualObjectsForConstantPoolOrEmbeddedObjects(
873 0 : description, description->constant_elements(),
874 0 : ObjectStats::ARRAY_BOILERPLATE_DESCRIPTION_ELEMENTS_TYPE);
875 0 : }
876 :
877 0 : void ObjectStatsCollectorImpl::
878 : RecordVirtualObjectsForConstantPoolOrEmbeddedObjects(
879 : HeapObject parent, HeapObject object,
880 : ObjectStats::VirtualInstanceType type) {
881 0 : if (!RecordSimpleVirtualObjectStats(parent, object, type)) return;
882 0 : if (object->IsFixedArrayExact()) {
883 : FixedArray array = FixedArray::cast(object);
884 0 : for (int i = 0; i < array->length(); i++) {
885 : Object entry = array->get(i);
886 0 : if (!entry->IsHeapObject()) continue;
887 : RecordVirtualObjectsForConstantPoolOrEmbeddedObjects(
888 0 : array, HeapObject::cast(entry), type);
889 : }
890 : }
891 : }
892 :
893 0 : void ObjectStatsCollectorImpl::RecordVirtualBytecodeArrayDetails(
894 : BytecodeArray bytecode) {
895 : RecordSimpleVirtualObjectStats(
896 0 : bytecode, bytecode->constant_pool(),
897 0 : ObjectStats::BYTECODE_ARRAY_CONSTANT_POOL_TYPE);
898 : // FixedArrays on constant pool are used for holding descriptor information.
899 : // They are shared with optimized code.
900 : FixedArray constant_pool = FixedArray::cast(bytecode->constant_pool());
901 0 : for (int i = 0; i < constant_pool->length(); i++) {
902 : Object entry = constant_pool->get(i);
903 0 : if (entry->IsFixedArrayExact()) {
904 : RecordVirtualObjectsForConstantPoolOrEmbeddedObjects(
905 : constant_pool, HeapObject::cast(entry),
906 0 : ObjectStats::EMBEDDED_OBJECT_TYPE);
907 : }
908 : }
909 : RecordSimpleVirtualObjectStats(
910 0 : bytecode, bytecode->handler_table(),
911 0 : ObjectStats::BYTECODE_ARRAY_HANDLER_TABLE_TYPE);
912 0 : RecordSimpleVirtualObjectStats(bytecode, bytecode->SourcePositionTable(),
913 0 : ObjectStats::SOURCE_POSITION_TABLE_TYPE);
914 0 : }
915 :
916 : namespace {
917 :
918 0 : ObjectStats::VirtualInstanceType CodeKindToVirtualInstanceType(
919 : Code::Kind kind) {
920 0 : switch (kind) {
921 : #define CODE_KIND_CASE(type) \
922 : case Code::type: \
923 : return ObjectStats::type;
924 0 : CODE_KIND_LIST(CODE_KIND_CASE)
925 : #undef CODE_KIND_CASE
926 : default:
927 0 : UNREACHABLE();
928 : }
929 : UNREACHABLE();
930 : }
931 :
932 : } // namespace
933 :
934 0 : void ObjectStatsCollectorImpl::RecordVirtualCodeDetails(Code code) {
935 0 : RecordSimpleVirtualObjectStats(HeapObject(), code,
936 0 : CodeKindToVirtualInstanceType(code->kind()));
937 0 : RecordSimpleVirtualObjectStats(code, code->deoptimization_data(),
938 0 : ObjectStats::DEOPTIMIZATION_DATA_TYPE);
939 0 : RecordSimpleVirtualObjectStats(code, code->relocation_info(),
940 0 : ObjectStats::RELOC_INFO_TYPE);
941 : Object source_position_table = code->source_position_table();
942 0 : if (source_position_table->IsSourcePositionTableWithFrameCache()) {
943 : RecordSimpleVirtualObjectStats(
944 : code,
945 : SourcePositionTableWithFrameCache::cast(source_position_table)
946 0 : ->source_position_table(),
947 0 : ObjectStats::SOURCE_POSITION_TABLE_TYPE);
948 0 : } else if (source_position_table->IsHeapObject()) {
949 : RecordSimpleVirtualObjectStats(code,
950 : HeapObject::cast(source_position_table),
951 0 : ObjectStats::SOURCE_POSITION_TABLE_TYPE);
952 : }
953 0 : if (code->kind() == Code::Kind::OPTIMIZED_FUNCTION) {
954 : DeoptimizationData input_data =
955 : DeoptimizationData::cast(code->deoptimization_data());
956 0 : if (input_data->length() > 0) {
957 0 : RecordSimpleVirtualObjectStats(code->deoptimization_data(),
958 0 : input_data->LiteralArray(),
959 0 : ObjectStats::OPTIMIZED_CODE_LITERALS_TYPE);
960 : }
961 : }
962 : int const mode_mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT);
963 0 : for (RelocIterator it(code, mode_mask); !it.done(); it.next()) {
964 : RelocInfo::Mode mode = it.rinfo()->rmode();
965 0 : if (mode == RelocInfo::EMBEDDED_OBJECT) {
966 : Object target = it.rinfo()->target_object();
967 0 : if (target->IsFixedArrayExact()) {
968 : RecordVirtualObjectsForConstantPoolOrEmbeddedObjects(
969 0 : code, HeapObject::cast(target), ObjectStats::EMBEDDED_OBJECT_TYPE);
970 : }
971 : }
972 : }
973 0 : }
974 :
975 0 : void ObjectStatsCollectorImpl::RecordVirtualContext(Context context) {
976 0 : if (context->IsNativeContext()) {
977 0 : RecordObjectStats(context, NATIVE_CONTEXT_TYPE, context->Size());
978 0 : } else if (context->IsFunctionContext()) {
979 0 : RecordObjectStats(context, FUNCTION_CONTEXT_TYPE, context->Size());
980 : } else {
981 0 : RecordSimpleVirtualObjectStats(HeapObject(), context,
982 0 : ObjectStats::OTHER_CONTEXT_TYPE);
983 : }
984 0 : }
985 :
986 : class ObjectStatsVisitor {
987 : public:
988 : ObjectStatsVisitor(Heap* heap, ObjectStatsCollectorImpl* live_collector,
989 : ObjectStatsCollectorImpl* dead_collector,
990 : ObjectStatsCollectorImpl::Phase phase)
991 : : live_collector_(live_collector),
992 : dead_collector_(dead_collector),
993 : marking_state_(
994 : heap->mark_compact_collector()->non_atomic_marking_state()),
995 0 : phase_(phase) {}
996 :
997 0 : bool Visit(HeapObject obj, int size) {
998 0 : if (marking_state_->IsBlack(obj)) {
999 0 : live_collector_->CollectStatistics(
1000 0 : obj, phase_, ObjectStatsCollectorImpl::CollectFieldStats::kYes);
1001 : } else {
1002 : DCHECK(!marking_state_->IsGrey(obj));
1003 0 : dead_collector_->CollectStatistics(
1004 0 : obj, phase_, ObjectStatsCollectorImpl::CollectFieldStats::kNo);
1005 : }
1006 0 : return true;
1007 : }
1008 :
1009 : private:
1010 : ObjectStatsCollectorImpl* live_collector_;
1011 : ObjectStatsCollectorImpl* dead_collector_;
1012 : MarkCompactCollector::NonAtomicMarkingState* marking_state_;
1013 : ObjectStatsCollectorImpl::Phase phase_;
1014 : };
1015 :
1016 : namespace {
1017 :
1018 0 : void IterateHeap(Heap* heap, ObjectStatsVisitor* visitor) {
1019 0 : SpaceIterator space_it(heap);
1020 0 : HeapObject obj;
1021 0 : while (space_it.has_next()) {
1022 0 : std::unique_ptr<ObjectIterator> it(space_it.next()->GetObjectIterator());
1023 : ObjectIterator* obj_it = it.get();
1024 0 : for (obj = obj_it->Next(); !obj.is_null(); obj = obj_it->Next()) {
1025 0 : visitor->Visit(obj, obj->Size());
1026 : }
1027 : }
1028 0 : }
1029 :
1030 : } // namespace
1031 :
1032 0 : void ObjectStatsCollector::Collect() {
1033 0 : ObjectStatsCollectorImpl live_collector(heap_, live_);
1034 0 : ObjectStatsCollectorImpl dead_collector(heap_, dead_);
1035 0 : live_collector.CollectGlobalStatistics();
1036 0 : for (int i = 0; i < ObjectStatsCollectorImpl::kNumberOfPhases; i++) {
1037 0 : ObjectStatsVisitor visitor(heap_, &live_collector, &dead_collector,
1038 0 : static_cast<ObjectStatsCollectorImpl::Phase>(i));
1039 0 : IterateHeap(heap_, &visitor);
1040 : }
1041 0 : }
1042 :
1043 : } // namespace internal
1044 120216 : } // namespace v8
|