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 : 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 0 : if (details.representation().IsDouble() &&
128 0 : map->IsUnboxedDoubleField(index)) {
129 0 : ++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 0 : 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 : PrintF(", \"embedder_fields\": %zu",
208 0 : embedder_fields_count_ * kEmbedderDataSlotSize);
209 : 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 0 : 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 0 : stream << ",\"embedder_fields\":"
263 0 : << (embedder_fields_count_ * kEmbedderDataSlotSize);
264 0 : stream << ",\"unboxed_double_fields\": "
265 0 : << (unboxed_double_fields_count_ * kDoubleSize);
266 0 : 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 : 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 0 : 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 0 : 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 : 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 0 : 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 : 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 : object, FixedArray::cast(JSMap::cast(object)->table()),
537 0 : ObjectStats::JS_COLLECTION_TABLE_TYPE);
538 : }
539 0 : if (object->IsJSSet()) {
540 : RecordSimpleVirtualObjectStats(
541 : 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 0 : 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 0 : if (obj == *isolate->factory()->uninitialized_symbol() ||
582 : obj == *isolate->factory()->premonomorphic_symbol()) {
583 : return ObjectStats::FEEDBACK_VECTOR_SLOT_LOAD_UNUSED_TYPE;
584 : }
585 0 : return ObjectStats::FEEDBACK_VECTOR_SLOT_LOAD_TYPE;
586 :
587 : case FeedbackSlotKind::kStoreNamedSloppy:
588 : case FeedbackSlotKind::kStoreNamedStrict:
589 : case FeedbackSlotKind::kStoreOwnNamed:
590 : case FeedbackSlotKind::kStoreGlobalSloppy:
591 : case FeedbackSlotKind::kStoreGlobalStrict:
592 : case FeedbackSlotKind::kStoreKeyedSloppy:
593 : case FeedbackSlotKind::kStoreKeyedStrict:
594 0 : if (obj == *isolate->factory()->uninitialized_symbol() ||
595 : obj == *isolate->factory()->premonomorphic_symbol()) {
596 : return ObjectStats::FEEDBACK_VECTOR_SLOT_STORE_UNUSED_TYPE;
597 : }
598 0 : return ObjectStats::FEEDBACK_VECTOR_SLOT_STORE_TYPE;
599 :
600 : case FeedbackSlotKind::kBinaryOp:
601 : case FeedbackSlotKind::kCompareOp:
602 : return ObjectStats::FEEDBACK_VECTOR_SLOT_ENUM_TYPE;
603 :
604 : default:
605 0 : return ObjectStats::FEEDBACK_VECTOR_SLOT_OTHER_TYPE;
606 : }
607 : }
608 :
609 0 : void ObjectStatsCollectorImpl::RecordVirtualFeedbackVectorDetails(
610 : FeedbackVector vector) {
611 0 : if (virtual_objects_.find(vector) != virtual_objects_.end()) return;
612 : // Manually insert the feedback vector into the virtual object list, since
613 : // we're logging its component parts separately.
614 : virtual_objects_.insert(vector);
615 :
616 : size_t calculated_size = 0;
617 :
618 : // Log the feedback vector's header (fixed fields).
619 : size_t header_size = vector->slots_start().address() - vector->address();
620 : stats_->RecordVirtualObjectStats(ObjectStats::FEEDBACK_VECTOR_HEADER_TYPE,
621 0 : header_size, ObjectStats::kNoOverAllocation);
622 : calculated_size += header_size;
623 :
624 : // Iterate over the feedback slots and log each one.
625 0 : if (!vector->shared_function_info()->HasFeedbackMetadata()) return;
626 :
627 : FeedbackMetadataIterator it(vector->metadata());
628 0 : while (it.HasNext()) {
629 0 : FeedbackSlot slot = it.Next();
630 : // Log the entry (or entries) taken up by this slot.
631 0 : size_t slot_size = it.entry_size() * kTaggedSize;
632 : stats_->RecordVirtualObjectStats(
633 : GetFeedbackSlotType(vector->Get(slot), it.kind(), heap_->isolate()),
634 0 : slot_size, ObjectStats::kNoOverAllocation);
635 0 : calculated_size += slot_size;
636 :
637 : // Log the monomorphic/polymorphic helper objects that this slot owns.
638 0 : for (int i = 0; i < it.entry_size(); i++) {
639 0 : MaybeObject raw_object = vector->get(slot.ToInt() + i);
640 0 : HeapObject object;
641 0 : if (raw_object->GetHeapObject(&object)) {
642 0 : if (object->IsCell() || object->IsWeakFixedArray()) {
643 : RecordSimpleVirtualObjectStats(
644 0 : vector, object, ObjectStats::FEEDBACK_VECTOR_ENTRY_TYPE);
645 : }
646 : }
647 : }
648 : }
649 :
650 0 : CHECK_EQ(calculated_size, vector->Size());
651 : }
652 :
653 0 : void ObjectStatsCollectorImpl::RecordVirtualFixedArrayDetails(
654 : FixedArray array) {
655 0 : if (IsCowArray(array)) {
656 : RecordVirtualObjectStats(HeapObject(), array, ObjectStats::COW_ARRAY_TYPE,
657 0 : array->Size(), ObjectStats::kNoOverAllocation,
658 0 : kIgnoreCow);
659 : }
660 0 : }
661 :
662 0 : void ObjectStatsCollectorImpl::CollectStatistics(
663 : HeapObject obj, Phase phase, CollectFieldStats collect_field_stats) {
664 : Map map = obj->map();
665 0 : switch (phase) {
666 : case kPhase1:
667 0 : if (obj->IsFeedbackVector()) {
668 0 : RecordVirtualFeedbackVectorDetails(FeedbackVector::cast(obj));
669 0 : } else if (obj->IsMap()) {
670 0 : RecordVirtualMapDetails(Map::cast(obj));
671 0 : } else if (obj->IsBytecodeArray()) {
672 0 : RecordVirtualBytecodeArrayDetails(BytecodeArray::cast(obj));
673 0 : } else if (obj->IsCode()) {
674 0 : RecordVirtualCodeDetails(Code::cast(obj));
675 0 : } else if (obj->IsFunctionTemplateInfo()) {
676 : RecordVirtualFunctionTemplateInfoDetails(
677 0 : FunctionTemplateInfo::cast(obj));
678 0 : } else if (obj->IsJSFunction()) {
679 0 : RecordVirtualJSFunctionDetails(JSFunction::cast(obj));
680 0 : } else if (obj->IsJSGlobalObject()) {
681 0 : RecordVirtualJSGlobalObjectDetails(JSGlobalObject::cast(obj));
682 0 : } else if (obj->IsJSObject()) {
683 : // This phase needs to come after RecordVirtualAllocationSiteDetails
684 : // to properly split among boilerplates.
685 0 : RecordVirtualJSObjectDetails(JSObject::cast(obj));
686 0 : } else if (obj->IsJSCollection()) {
687 0 : RecordVirtualJSCollectionDetails(JSObject::cast(obj));
688 0 : } else if (obj->IsSharedFunctionInfo()) {
689 0 : RecordVirtualSharedFunctionInfoDetails(SharedFunctionInfo::cast(obj));
690 0 : } else if (obj->IsContext()) {
691 0 : RecordVirtualContext(Context::cast(obj));
692 0 : } else if (obj->IsScript()) {
693 0 : RecordVirtualScriptDetails(Script::cast(obj));
694 0 : } else if (obj->IsArrayBoilerplateDescription()) {
695 : RecordVirtualArrayBoilerplateDescription(
696 0 : ArrayBoilerplateDescription::cast(obj));
697 0 : } else if (obj->IsFixedArrayExact()) {
698 : // Has to go last as it triggers too eagerly.
699 0 : RecordVirtualFixedArrayDetails(FixedArray::cast(obj));
700 : }
701 : break;
702 : case kPhase2:
703 0 : if (obj->IsExternalString()) {
704 : // This has to be in Phase2 to avoid conflicting with recording Script
705 : // sources. We still want to run RecordObjectStats after though.
706 0 : RecordVirtualExternalStringDetails(ExternalString::cast(obj));
707 : }
708 0 : RecordObjectStats(obj, map->instance_type(), obj->Size());
709 0 : if (collect_field_stats == CollectFieldStats::kYes) {
710 0 : field_stats_collector_.RecordStats(obj);
711 : }
712 : break;
713 : }
714 0 : }
715 :
716 0 : void ObjectStatsCollectorImpl::CollectGlobalStatistics() {
717 : // Iterate boilerplates first to disambiguate them from regular JS objects.
718 0 : Object list = heap_->allocation_sites_list();
719 0 : while (list->IsAllocationSite()) {
720 : AllocationSite site = AllocationSite::cast(list);
721 0 : RecordVirtualAllocationSiteDetails(site);
722 0 : list = site->weak_next();
723 : }
724 :
725 : // FixedArray.
726 0 : RecordSimpleVirtualObjectStats(HeapObject(), heap_->serialized_objects(),
727 0 : ObjectStats::SERIALIZED_OBJECTS_TYPE);
728 0 : RecordSimpleVirtualObjectStats(HeapObject(), heap_->number_string_cache(),
729 0 : ObjectStats::NUMBER_STRING_CACHE_TYPE);
730 : RecordSimpleVirtualObjectStats(
731 0 : HeapObject(), heap_->single_character_string_cache(),
732 0 : ObjectStats::SINGLE_CHARACTER_STRING_CACHE_TYPE);
733 0 : RecordSimpleVirtualObjectStats(HeapObject(), heap_->string_split_cache(),
734 0 : ObjectStats::STRING_SPLIT_CACHE_TYPE);
735 0 : RecordSimpleVirtualObjectStats(HeapObject(), heap_->regexp_multiple_cache(),
736 0 : ObjectStats::REGEXP_MULTIPLE_CACHE_TYPE);
737 0 : RecordSimpleVirtualObjectStats(HeapObject(), heap_->retained_maps(),
738 0 : ObjectStats::RETAINED_MAPS_TYPE);
739 :
740 : // WeakArrayList.
741 : RecordSimpleVirtualObjectStats(
742 : HeapObject(),
743 0 : WeakArrayList::cast(heap_->noscript_shared_function_infos()),
744 0 : ObjectStats::NOSCRIPT_SHARED_FUNCTION_INFOS_TYPE);
745 : RecordSimpleVirtualObjectStats(HeapObject(),
746 0 : WeakArrayList::cast(heap_->script_list()),
747 0 : ObjectStats::SCRIPT_LIST_TYPE);
748 0 : }
749 :
750 0 : void ObjectStatsCollectorImpl::RecordObjectStats(HeapObject obj,
751 : InstanceType type,
752 : size_t size) {
753 0 : if (virtual_objects_.find(obj) == virtual_objects_.end()) {
754 0 : stats_->RecordObjectStats(type, size);
755 : }
756 0 : }
757 :
758 0 : bool ObjectStatsCollectorImpl::CanRecordFixedArray(FixedArrayBase array) {
759 0 : ReadOnlyRoots roots(heap_);
760 0 : return array != roots.empty_fixed_array() &&
761 0 : array != roots.empty_sloppy_arguments_elements() &&
762 0 : array != roots.empty_slow_element_dictionary() &&
763 0 : array != roots.empty_property_dictionary();
764 : }
765 :
766 0 : bool ObjectStatsCollectorImpl::IsCowArray(FixedArrayBase array) {
767 0 : return array->map() == ReadOnlyRoots(heap_).fixed_cow_array_map();
768 : }
769 :
770 0 : bool ObjectStatsCollectorImpl::SameLiveness(HeapObject obj1, HeapObject obj2) {
771 0 : return obj1.is_null() || obj2.is_null() ||
772 0 : marking_state_->Color(obj1) == marking_state_->Color(obj2);
773 : }
774 :
775 0 : void ObjectStatsCollectorImpl::RecordVirtualMapDetails(Map map) {
776 : // TODO(mlippautz): map->dependent_code(): DEPENDENT_CODE_TYPE.
777 :
778 0 : DescriptorArray array = map->instance_descriptors();
779 0 : if (map->owns_descriptors() &&
780 0 : array != ReadOnlyRoots(heap_).empty_descriptor_array()) {
781 : // DescriptorArray has its own instance type.
782 0 : EnumCache enum_cache = array->enum_cache();
783 : RecordSimpleVirtualObjectStats(array, enum_cache->keys(),
784 0 : ObjectStats::ENUM_CACHE_TYPE);
785 : RecordSimpleVirtualObjectStats(array, enum_cache->indices(),
786 0 : ObjectStats::ENUM_INDICES_CACHE_TYPE);
787 : }
788 :
789 0 : if (map->is_prototype_map()) {
790 0 : if (map->prototype_info()->IsPrototypeInfo()) {
791 : PrototypeInfo info = PrototypeInfo::cast(map->prototype_info());
792 0 : Object users = info->prototype_users();
793 0 : if (users->IsWeakFixedArray()) {
794 : RecordSimpleVirtualObjectStats(map, WeakArrayList::cast(users),
795 0 : ObjectStats::PROTOTYPE_USERS_TYPE);
796 : }
797 : }
798 : }
799 0 : }
800 :
801 0 : void ObjectStatsCollectorImpl::RecordVirtualScriptDetails(Script script) {
802 : RecordSimpleVirtualObjectStats(
803 : script, script->shared_function_infos(),
804 0 : ObjectStats::SCRIPT_SHARED_FUNCTION_INFOS_TYPE);
805 :
806 : // Log the size of external source code.
807 0 : Object raw_source = script->source();
808 0 : if (raw_source->IsExternalString()) {
809 : // The contents of external strings aren't on the heap, so we have to record
810 : // them manually. The on-heap String object is recorded indepentendely in
811 : // the normal pass.
812 0 : ExternalString string = ExternalString::cast(raw_source);
813 : Address resource = string->resource_as_address();
814 0 : size_t off_heap_size = string->ExternalPayloadSize();
815 : RecordExternalResourceStats(
816 : resource,
817 0 : string->IsOneByteRepresentation()
818 : ? ObjectStats::SCRIPT_SOURCE_EXTERNAL_ONE_BYTE_TYPE
819 : : ObjectStats::SCRIPT_SOURCE_EXTERNAL_TWO_BYTE_TYPE,
820 0 : off_heap_size);
821 0 : } else if (raw_source->IsString()) {
822 0 : String source = String::cast(raw_source);
823 : RecordSimpleVirtualObjectStats(
824 : script, source,
825 0 : source->IsOneByteRepresentation()
826 : ? ObjectStats::SCRIPT_SOURCE_NON_EXTERNAL_ONE_BYTE_TYPE
827 0 : : ObjectStats::SCRIPT_SOURCE_NON_EXTERNAL_TWO_BYTE_TYPE);
828 : }
829 0 : }
830 :
831 0 : void ObjectStatsCollectorImpl::RecordVirtualExternalStringDetails(
832 : ExternalString string) {
833 : // Track the external string resource size in a separate category.
834 :
835 : Address resource = string->resource_as_address();
836 0 : size_t off_heap_size = string->ExternalPayloadSize();
837 : RecordExternalResourceStats(
838 : resource,
839 0 : string->IsOneByteRepresentation()
840 : ? ObjectStats::STRING_EXTERNAL_RESOURCE_ONE_BYTE_TYPE
841 : : ObjectStats::STRING_EXTERNAL_RESOURCE_TWO_BYTE_TYPE,
842 0 : off_heap_size);
843 0 : }
844 :
845 0 : void ObjectStatsCollectorImpl::RecordVirtualSharedFunctionInfoDetails(
846 : SharedFunctionInfo info) {
847 : // Uncompiled SharedFunctionInfo gets its own category.
848 0 : if (!info->is_compiled()) {
849 : RecordSimpleVirtualObjectStats(
850 0 : HeapObject(), info, ObjectStats::UNCOMPILED_SHARED_FUNCTION_INFO_TYPE);
851 : }
852 0 : }
853 :
854 0 : void ObjectStatsCollectorImpl::RecordVirtualJSFunctionDetails(
855 : JSFunction function) {
856 : // Uncompiled JSFunctions get their own category.
857 0 : if (!function->is_compiled()) {
858 : RecordSimpleVirtualObjectStats(HeapObject(), function,
859 0 : ObjectStats::UNCOMPILED_JS_FUNCTION_TYPE);
860 : }
861 0 : }
862 0 : void ObjectStatsCollectorImpl::RecordVirtualArrayBoilerplateDescription(
863 : ArrayBoilerplateDescription description) {
864 : RecordVirtualObjectsForConstantPoolOrEmbeddedObjects(
865 : description, description->constant_elements(),
866 0 : ObjectStats::ARRAY_BOILERPLATE_DESCRIPTION_ELEMENTS_TYPE);
867 0 : }
868 :
869 0 : void ObjectStatsCollectorImpl::
870 : RecordVirtualObjectsForConstantPoolOrEmbeddedObjects(
871 : HeapObject parent, HeapObject object,
872 : ObjectStats::VirtualInstanceType type) {
873 0 : if (!RecordSimpleVirtualObjectStats(parent, object, type)) return;
874 0 : if (object->IsFixedArrayExact()) {
875 : FixedArray array = FixedArray::cast(object);
876 0 : for (int i = 0; i < array->length(); i++) {
877 0 : Object entry = array->get(i);
878 0 : if (!entry->IsHeapObject()) continue;
879 : RecordVirtualObjectsForConstantPoolOrEmbeddedObjects(
880 0 : array, HeapObject::cast(entry), type);
881 : }
882 : }
883 : }
884 :
885 0 : void ObjectStatsCollectorImpl::RecordVirtualBytecodeArrayDetails(
886 : BytecodeArray bytecode) {
887 : RecordSimpleVirtualObjectStats(
888 : bytecode, bytecode->constant_pool(),
889 0 : ObjectStats::BYTECODE_ARRAY_CONSTANT_POOL_TYPE);
890 : // FixedArrays on constant pool are used for holding descriptor information.
891 : // They are shared with optimized code.
892 0 : FixedArray constant_pool = FixedArray::cast(bytecode->constant_pool());
893 0 : for (int i = 0; i < constant_pool->length(); i++) {
894 0 : Object entry = constant_pool->get(i);
895 0 : if (entry->IsFixedArrayExact()) {
896 : RecordVirtualObjectsForConstantPoolOrEmbeddedObjects(
897 : constant_pool, HeapObject::cast(entry),
898 0 : ObjectStats::EMBEDDED_OBJECT_TYPE);
899 : }
900 : }
901 : RecordSimpleVirtualObjectStats(
902 : bytecode, bytecode->handler_table(),
903 0 : ObjectStats::BYTECODE_ARRAY_HANDLER_TABLE_TYPE);
904 : RecordSimpleVirtualObjectStats(bytecode, bytecode->SourcePositionTable(),
905 0 : ObjectStats::SOURCE_POSITION_TABLE_TYPE);
906 0 : }
907 :
908 : namespace {
909 :
910 0 : ObjectStats::VirtualInstanceType CodeKindToVirtualInstanceType(
911 : Code::Kind kind) {
912 0 : switch (kind) {
913 : #define CODE_KIND_CASE(type) \
914 : case Code::type: \
915 : return ObjectStats::type;
916 0 : CODE_KIND_LIST(CODE_KIND_CASE)
917 : #undef CODE_KIND_CASE
918 : default:
919 0 : UNREACHABLE();
920 : }
921 : UNREACHABLE();
922 : }
923 :
924 : } // namespace
925 :
926 0 : void ObjectStatsCollectorImpl::RecordVirtualCodeDetails(Code code) {
927 : RecordSimpleVirtualObjectStats(HeapObject(), code,
928 0 : CodeKindToVirtualInstanceType(code->kind()));
929 : RecordSimpleVirtualObjectStats(code, code->deoptimization_data(),
930 0 : ObjectStats::DEOPTIMIZATION_DATA_TYPE);
931 : RecordSimpleVirtualObjectStats(code, code->relocation_info(),
932 0 : ObjectStats::RELOC_INFO_TYPE);
933 0 : Object source_position_table = code->source_position_table();
934 0 : if (source_position_table->IsSourcePositionTableWithFrameCache()) {
935 : RecordSimpleVirtualObjectStats(
936 : code,
937 : SourcePositionTableWithFrameCache::cast(source_position_table)
938 0 : ->source_position_table(),
939 0 : ObjectStats::SOURCE_POSITION_TABLE_TYPE);
940 0 : } else if (source_position_table->IsHeapObject()) {
941 : RecordSimpleVirtualObjectStats(code,
942 : HeapObject::cast(source_position_table),
943 0 : ObjectStats::SOURCE_POSITION_TABLE_TYPE);
944 : }
945 0 : if (code->kind() == Code::Kind::OPTIMIZED_FUNCTION) {
946 : DeoptimizationData input_data =
947 0 : DeoptimizationData::cast(code->deoptimization_data());
948 0 : if (input_data->length() > 0) {
949 : RecordSimpleVirtualObjectStats(code->deoptimization_data(),
950 : input_data->LiteralArray(),
951 0 : ObjectStats::OPTIMIZED_CODE_LITERALS_TYPE);
952 : }
953 : }
954 : int const mode_mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT);
955 0 : for (RelocIterator it(code, mode_mask); !it.done(); it.next()) {
956 0 : RelocInfo::Mode mode = it.rinfo()->rmode();
957 0 : if (mode == RelocInfo::EMBEDDED_OBJECT) {
958 0 : Object target = it.rinfo()->target_object();
959 0 : if (target->IsFixedArrayExact()) {
960 : RecordVirtualObjectsForConstantPoolOrEmbeddedObjects(
961 0 : code, HeapObject::cast(target), ObjectStats::EMBEDDED_OBJECT_TYPE);
962 : }
963 : }
964 : }
965 0 : }
966 :
967 0 : void ObjectStatsCollectorImpl::RecordVirtualContext(Context context) {
968 0 : if (context->IsNativeContext()) {
969 0 : RecordObjectStats(context, NATIVE_CONTEXT_TYPE, context->Size());
970 0 : } else if (context->IsFunctionContext()) {
971 0 : RecordObjectStats(context, FUNCTION_CONTEXT_TYPE, context->Size());
972 : } else {
973 : RecordSimpleVirtualObjectStats(HeapObject(), context,
974 0 : ObjectStats::OTHER_CONTEXT_TYPE);
975 : }
976 0 : }
977 :
978 : class ObjectStatsVisitor {
979 : public:
980 : ObjectStatsVisitor(Heap* heap, ObjectStatsCollectorImpl* live_collector,
981 : ObjectStatsCollectorImpl* dead_collector,
982 : ObjectStatsCollectorImpl::Phase phase)
983 : : live_collector_(live_collector),
984 : dead_collector_(dead_collector),
985 : marking_state_(
986 0 : heap->mark_compact_collector()->non_atomic_marking_state()),
987 0 : phase_(phase) {}
988 :
989 0 : bool Visit(HeapObject obj, int size) {
990 0 : if (marking_state_->IsBlack(obj)) {
991 : live_collector_->CollectStatistics(
992 0 : obj, phase_, ObjectStatsCollectorImpl::CollectFieldStats::kYes);
993 : } else {
994 : DCHECK(!marking_state_->IsGrey(obj));
995 : dead_collector_->CollectStatistics(
996 0 : obj, phase_, ObjectStatsCollectorImpl::CollectFieldStats::kNo);
997 : }
998 0 : return true;
999 : }
1000 :
1001 : private:
1002 : ObjectStatsCollectorImpl* live_collector_;
1003 : ObjectStatsCollectorImpl* dead_collector_;
1004 : MarkCompactCollector::NonAtomicMarkingState* marking_state_;
1005 : ObjectStatsCollectorImpl::Phase phase_;
1006 : };
1007 :
1008 : namespace {
1009 :
1010 0 : void IterateHeap(Heap* heap, ObjectStatsVisitor* visitor) {
1011 0 : SpaceIterator space_it(heap);
1012 0 : HeapObject obj;
1013 0 : while (space_it.has_next()) {
1014 0 : std::unique_ptr<ObjectIterator> it(space_it.next()->GetObjectIterator());
1015 : ObjectIterator* obj_it = it.get();
1016 0 : for (obj = obj_it->Next(); !obj.is_null(); obj = obj_it->Next()) {
1017 0 : visitor->Visit(obj, obj->Size());
1018 : }
1019 0 : }
1020 0 : }
1021 :
1022 : } // namespace
1023 :
1024 0 : void ObjectStatsCollector::Collect() {
1025 0 : ObjectStatsCollectorImpl live_collector(heap_, live_);
1026 0 : ObjectStatsCollectorImpl dead_collector(heap_, dead_);
1027 0 : live_collector.CollectGlobalStatistics();
1028 0 : for (int i = 0; i < ObjectStatsCollectorImpl::kNumberOfPhases; i++) {
1029 : ObjectStatsVisitor visitor(heap_, &live_collector, &dead_collector,
1030 0 : static_cast<ObjectStatsCollectorImpl::Phase>(i));
1031 0 : IterateHeap(heap_, &visitor);
1032 0 : }
1033 0 : }
1034 :
1035 : } // namespace internal
1036 178779 : } // namespace v8
|