v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
object-stats.cc
Go to the documentation of this file.
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
7
8#include <unordered_set>
9
10#include "src/base/bits.h"
13#include "src/common/globals.h"
16#include "src/heap/heap-inl.h"
28#include "src/objects/slots.h"
31#include "src/utils/memcopy.h"
32#include "src/utils/ostreams.h"
33
34namespace v8 {
35namespace internal {
36
38
40
42 public:
43 FieldStatsCollector(Heap* heap, size_t* tagged_fields_count,
44 size_t* embedder_fields_count,
45 size_t* inobject_smi_fields_count,
46 size_t* boxed_double_fields_count,
47 size_t* string_data_count, size_t* raw_fields_count)
49 heap_(heap),
50 tagged_fields_count_(tagged_fields_count),
51 embedder_fields_count_(embedder_fields_count),
52 inobject_smi_fields_count_(inobject_smi_fields_count),
53 boxed_double_fields_count_(boxed_double_fields_count),
54 string_data_count_(string_data_count),
55 raw_fields_count_(raw_fields_count) {}
56
58 size_t old_pointer_fields_count = *tagged_fields_count_;
59 VisitObject(heap_->isolate(), host, this);
60 size_t tagged_fields_count_in_object =
61 *tagged_fields_count_ - old_pointer_fields_count;
62
63 int object_size_in_words = host->Size(cage_base()) / kTaggedSize;
64 DCHECK_LE(tagged_fields_count_in_object, object_size_in_words);
65 size_t raw_fields_count_in_object =
66 object_size_in_words - tagged_fields_count_in_object;
67
68 if (IsJSObject(host, cage_base())) {
69 JSObjectFieldStats field_stats = GetInobjectFieldStats(host->map());
70 // Embedder fields are already included into pointer words.
72 tagged_fields_count_in_object);
73 tagged_fields_count_in_object -= field_stats.embedded_fields_count_;
76
77 // Smi fields are also included into pointer words.
78 tagged_fields_count_in_object -= field_stats.smi_fields_count_;
81 } else if (IsHeapNumber(host, cage_base())) {
82 DCHECK_LE(kDoubleSize / kTaggedSize, raw_fields_count_in_object);
83 raw_fields_count_in_object -= kDoubleSize / kTaggedSize;
85 } else if (IsSeqString(host, cage_base())) {
86 int string_data =
87 Cast<SeqString>(host)->length(kAcquireLoad) *
88 (Cast<String>(host)->IsOneByteRepresentation() ? 1 : 2) / kTaggedSize;
89 DCHECK_LE(string_data, raw_fields_count_in_object);
90 raw_fields_count_in_object -= string_data;
91 *string_data_count_ += string_data;
92 }
93 *raw_fields_count_ += raw_fields_count_in_object;
94 }
95
104
109
111 RelocInfo* rinfo) override {
112 // InstructionStream target is most likely encoded as a relative 32-bit
113 // offset and not as a full tagged value, so there's nothing to count.
114 }
115
117 RelocInfo* rinfo) override {
119 }
120
122 // Just do nothing, but avoid the inherited UNREACHABLE implementation.
123 }
124
125 private:
132 std::unordered_map<Tagged<Map>, JSObjectFieldStats, Object::Hasher>
134
136
137 Heap* const heap_;
138 size_t* const tagged_fields_count_;
142 size_t* const string_data_count_;
143 size_t* const raw_fields_count_;
144};
145
148 auto iter = field_stats_cache_.find(map);
149 if (iter != field_stats_cache_.end()) {
150 return iter->second;
151 }
152 // Iterate descriptor array and calculate stats.
153 JSObjectFieldStats stats;
155 if (!map->is_dictionary_map()) {
156 Tagged<DescriptorArray> descriptors = map->instance_descriptors();
157 for (InternalIndex descriptor : map->IterateOwnDescriptors()) {
158 PropertyDetails details = descriptors->GetDetails(descriptor);
159 if (details.location() == PropertyLocation::kField) {
160 FieldIndex index = FieldIndex::ForDetails(map, details);
161 // Stop on first out-of-object field.
162 if (!index.is_inobject()) break;
163 if (details.representation().IsSmi()) {
164 ++stats.smi_fields_count_;
165 }
166 }
167 }
168 }
169 field_stats_cache_.insert(std::make_pair(map, stats));
170 return stats;
171}
172
173void ObjectStats::ClearObjectStats(bool clear_last_time_stats) {
174 memset(object_counts_, 0, sizeof(object_counts_));
175 memset(object_sizes_, 0, sizeof(object_sizes_));
176 memset(over_allocated_, 0, sizeof(over_allocated_));
177 memset(size_histogram_, 0, sizeof(size_histogram_));
179 if (clear_last_time_stats) {
182 }
189}
190
191// Tell the compiler to never inline this: occasionally, the optimizer will
192// decide to inline this and unroll the loop, making the compiled code more than
193// 100KB larger.
194V8_NOINLINE static void PrintJSONArray(size_t* array, const int len) {
195 PrintF("[ ");
196 for (int i = 0; i < len; i++) {
197 PrintF("%zu", array[i]);
198 if (i != (len - 1)) PrintF(", ");
199 }
200 PrintF(" ]");
201}
202
203V8_NOINLINE static void DumpJSONArray(std::stringstream& stream, size_t* array,
204 const int len) {
205 stream << PrintCollection(base::Vector<size_t>(array, len));
206}
207
208void ObjectStats::PrintKeyAndId(const char* key, int gc_count) {
209 PrintF("\"isolate\": \"%p\", \"id\": %d, \"key\": \"%s\", ",
210 reinterpret_cast<void*>(isolate()), gc_count, key);
211}
212
213void ObjectStats::PrintInstanceTypeJSON(const char* key, int gc_count,
214 const char* name, int index) {
215 PrintF("{ ");
216 PrintKeyAndId(key, gc_count);
217 PrintF("\"type\": \"instance_type_data\", ");
218 PrintF("\"instance_type\": %d, ", index);
219 PrintF("\"instance_type_name\": \"%s\", ", name);
220 PrintF("\"overall\": %zu, ", object_sizes_[index]);
221 PrintF("\"count\": %zu, ", object_counts_[index]);
222 PrintF("\"over_allocated\": %zu, ", over_allocated_[index]);
223 PrintF("\"histogram\": ");
225 PrintF(",");
226 PrintF("\"over_allocated_histogram\": ");
228 PrintF(" }\n");
229}
230
231void ObjectStats::PrintJSON(const char* key) {
232 double time = isolate()->time_millis_since_init();
233 int gc_count = heap()->gc_count();
234
235 // gc_descriptor
236 PrintF("{ ");
237 PrintKeyAndId(key, gc_count);
238 PrintF("\"type\": \"gc_descriptor\", \"time\": %f }\n", time);
239 // field_data
240 PrintF("{ ");
241 PrintKeyAndId(key, gc_count);
242 PrintF("\"type\": \"field_data\"");
243 PrintF(", \"tagged_fields\": %zu", tagged_fields_count_ * kTaggedSize);
244 PrintF(", \"embedder_fields\": %zu",
246 PrintF(", \"inobject_smi_fields\": %zu",
248 PrintF(", \"boxed_double_fields\": %zu",
250 PrintF(", \"string_data\": %zu", string_data_count_ * kTaggedSize);
251 PrintF(", \"other_raw_fields\": %zu", raw_fields_count_ * kSystemPointerSize);
252 PrintF(" }\n");
253 // bucket_sizes
254 PrintF("{ ");
255 PrintKeyAndId(key, gc_count);
256 PrintF("\"type\": \"bucket_sizes\", \"sizes\": [ ");
257 for (int i = 0; i < kNumberOfBuckets; i++) {
258 PrintF("%d", 1 << (kFirstBucketShift + i));
259 if (i != (kNumberOfBuckets - 1)) PrintF(", ");
260 }
261 PrintF(" ] }\n");
262
263#define INSTANCE_TYPE_WRAPPER(name) \
264 PrintInstanceTypeJSON(key, gc_count, #name, name);
265
266#define VIRTUAL_INSTANCE_TYPE_WRAPPER(name) \
267 PrintInstanceTypeJSON( \
268 key, gc_count, #name, \
269 FIRST_VIRTUAL_TYPE + static_cast<int>(VirtualInstanceType::name));
270
273
274#undef INSTANCE_TYPE_WRAPPER
275#undef VIRTUAL_INSTANCE_TYPE_WRAPPER
276}
277
278void ObjectStats::DumpInstanceTypeData(std::stringstream& stream,
279 const char* name, int index) {
280 stream << "\"" << name << "\":{";
281 stream << "\"type\":" << static_cast<int>(index) << ",";
282 stream << "\"overall\":" << object_sizes_[index] << ",";
283 stream << "\"count\":" << object_counts_[index] << ",";
284 stream << "\"over_allocated\":" << over_allocated_[index] << ",";
285 stream << "\"histogram\":";
287 stream << ",\"over_allocated_histogram\":";
289 stream << "},";
290}
291
292void ObjectStats::Dump(std::stringstream& stream) {
293 double time = isolate()->time_millis_since_init();
294 int gc_count = heap()->gc_count();
295
296 stream << "{";
297 stream << "\"isolate\":\"" << reinterpret_cast<void*>(isolate()) << "\",";
298 stream << "\"id\":" << gc_count << ",";
299 stream << "\"time\":" << time << ",";
300
301 // field_data
302 stream << "\"field_data\":{";
303 stream << "\"tagged_fields\":" << (tagged_fields_count_ * kTaggedSize);
304 stream << ",\"embedder_fields\":"
306 stream << ",\"inobject_smi_fields\": "
308 stream << ",\"boxed_double_fields\": "
310 stream << ",\"string_data\": " << (string_data_count_ * kTaggedSize);
311 stream << ",\"other_raw_fields\":"
313 stream << "}, ";
314
315 stream << "\"bucket_sizes\":[";
316 for (int i = 0; i < kNumberOfBuckets; i++) {
317 stream << (1 << (kFirstBucketShift + i));
318 if (i != (kNumberOfBuckets - 1)) stream << ",";
319 }
320 stream << "],";
321 stream << "\"type_data\":{";
322
323#define INSTANCE_TYPE_WRAPPER(name) DumpInstanceTypeData(stream, #name, name);
324
325#define VIRTUAL_INSTANCE_TYPE_WRAPPER(name) \
326 DumpInstanceTypeData( \
327 stream, #name, \
328 FIRST_VIRTUAL_TYPE + static_cast<int>(VirtualInstanceType::name));
329
332 stream << "\"END\":{}}}";
333
334#undef INSTANCE_TYPE_WRAPPER
335#undef VIRTUAL_INSTANCE_TYPE_WRAPPER
336}
337
344
345namespace {
346
347int Log2ForSize(size_t size) {
348 DCHECK_GT(size, 0);
349 return kSizetSize * 8 - 1 - base::bits::CountLeadingZeros(size);
350}
351
352} // namespace
353
355 if (size == 0) return 0;
356 return std::min({std::max(Log2ForSize(size) + 1 - kFirstBucketShift, 0),
358}
359
361 size_t over_allocated) {
362 DCHECK_LE(type, LAST_TYPE);
366 over_allocated_[type] += over_allocated;
368}
369
381
383
385 public:
390 static const int kNumberOfPhases = kPhase2 + 1;
391
393
395
396 enum class CollectFieldStats { kNo, kYes };
398 CollectFieldStats collect_field_stats);
399
400 private:
405
406 Isolate* isolate() { return heap_->isolate(); }
407
411 size_t size, size_t over_allocated,
412 CowMode check_cow_array = kCheckCow);
415 size_t size);
416 // Gets size from |ob| and assumes no over allocating.
420 // Records space wasted for property details and value (field type) slots
421 // in DescriptorArrays.
423
424 // For HashTable it is possible to compute over allocated memory.
425 template <typename Dictionary>
427 Tagged<Dictionary> hash_table,
429
433
434 // Blocklist for objects that should not be recorded using
435 // VirtualObjectStats and RecordSimpleVirtualObjectStats. For recording those
436 // objects dispatch to the low level ObjectStats::RecordObjectStats manually.
437 bool ShouldRecordObject(Tagged<HeapObject> object, CowMode check_cow_array);
438
440 Tagged<HeapObject> obj, InstanceType type, size_t size,
441 size_t over_allocated = ObjectStats::kNoOverAllocation);
442
443 // Specific recursion into constant pool or embedded code objects. Records
444 // FixedArrays and Tuple2.
448
449 // Details.
464
467
471
472 Heap* const heap_;
475 std::unordered_set<Tagged<HeapObject>, Object::Hasher, Object::KeyEqualSafe>
477 std::unordered_set<Address> external_resources_;
479};
480
482 ObjectStats* stats)
483 : heap_(heap),
484 stats_(stats),
485 marking_state_(heap->non_atomic_marking_state()),
486 field_stats_collector_(
487 heap_, &stats->tagged_fields_count_, &stats->embedder_fields_count_,
488 &stats->inobject_smi_fields_count_,
489 &stats->boxed_double_fields_count_, &stats->string_data_count_,
490 &stats->raw_fields_count_) {}
491
493 CowMode check_cow_array) {
494 if (IsFixedArrayExact(obj)) {
495 Tagged<FixedArray> fixed_array = Cast<FixedArray>(obj);
496 bool cow_check = check_cow_array == kIgnoreCow || !IsCowArray(fixed_array);
497 return CanRecordFixedArray(fixed_array) && cow_check;
498 }
499 if (obj.SafeEquals(ReadOnlyRoots(heap_).empty_property_array())) return false;
500 return true;
501}
502
503template <typename Dictionary>
505 Tagged<HeapObject> parent, Tagged<Dictionary> hash_table,
507 size_t over_allocated =
508 (hash_table->Capacity() - (hash_table->NumberOfElements() +
509 hash_table->NumberOfDeletedElements())) *
510 Dictionary::kEntrySize * kTaggedSize;
511 RecordVirtualObjectStats(parent, hash_table, type, hash_table->Size(),
512 over_allocated);
513}
514
521
524 // The caller ensures that these stats were not recorded for this object yet.
525
526 int wasted_value_slots_count = obj->number_of_slack_descriptors();
527 for (InternalIndex i : InternalIndex::Range(obj->number_of_descriptors())) {
528 if (!obj->IsInitializedDescriptor(i)) {
529 // This is a half-initialized DescriptorArray, don't read from it.
530 return;
531 }
532 PropertyDetails details = obj->GetDetails(i);
533 if (details.location() != PropertyLocation::kField) continue;
534 Tagged<FieldType> field_type = obj->GetFieldType(i);
535 if (IsAny(field_type)) {
536 ++wasted_value_slots_count;
537 }
538 }
539 if (wasted_value_slots_count == 0) return;
540 int wasted_value_slots_size = wasted_value_slots_count * kTaggedSize;
542 StatsEnum::WASTED_DESCRIPTOR_ARRAY_VALUES_TYPE, wasted_value_slots_size,
544
545 // It should be possible to pack PropertyDetails into one byte.
546 int wasted_details_space =
547 obj->number_of_all_descriptors() * (kTaggedSize - 1);
549 StatsEnum::WASTED_DESCRIPTOR_ARRAY_DETAILS_TYPE, wasted_details_space,
551}
552
555 ObjectStats::VirtualInstanceType type, size_t size, size_t over_allocated,
556 CowMode check_cow_array) {
557 CHECK_LT(over_allocated, size);
558 if (!SameLiveness(parent, obj) || !ShouldRecordObject(obj, check_cow_array)) {
559 return false;
560 }
561
562 if (virtual_objects_.find(obj) == virtual_objects_.end()) {
563 virtual_objects_.insert(obj);
564 stats_->RecordVirtualObjectStats(type, size, over_allocated);
565 return true;
566 }
567 return false;
568}
569
571 Address resource, ObjectStats::VirtualInstanceType type, size_t size) {
572 if (external_resources_.find(resource) == external_resources_.end()) {
573 external_resources_.insert(resource);
574 stats_->RecordVirtualObjectStats(type, size, 0);
575 }
576}
577
580 if (!site->PointsToLiteral()) return;
581 Tagged<JSObject> boilerplate = site->boilerplate();
582 if (IsJSArray(boilerplate)) {
583 RecordSimpleVirtualObjectStats(site, boilerplate,
584 StatsEnum::JS_ARRAY_BOILERPLATE_TYPE);
585 // Array boilerplates cannot have properties.
586 } else {
588 site, boilerplate, StatsEnum::JS_OBJECT_BOILERPLATE_TYPE,
589 boilerplate->Size(), ObjectStats::kNoOverAllocation);
590 if (boilerplate->HasFastProperties()) {
591 // We'll mis-classify the empty_property_array here. Given that there is a
592 // single instance, this is negligible.
593 Tagged<PropertyArray> properties = boilerplate->property_array();
595 site, properties, StatsEnum::BOILERPLATE_PROPERTY_ARRAY_TYPE);
596 } else {
597 Tagged<NameDictionary> properties = boilerplate->property_dictionary();
599 site, properties, StatsEnum::BOILERPLATE_PROPERTY_DICTIONARY_TYPE);
600 }
601 }
602 Tagged<FixedArrayBase> elements = boilerplate->elements();
603 RecordSimpleVirtualObjectStats(site, elements,
604 StatsEnum::BOILERPLATE_ELEMENTS_TYPE);
605}
606
609 // named_property_handler and indexed_property_handler are recorded as
610 // INTERCEPTOR_INFO_TYPE.
611 if (!IsUndefined(fti->GetInstanceCallHandler(), isolate())) {
613 fti, Cast<FunctionTemplateInfo>(fti->GetInstanceCallHandler()),
614 StatsEnum::FUNCTION_TEMPLATE_INFO_ENTRIES_TYPE);
615 }
616}
617
619 Tagged<JSGlobalObject> object) {
620 // Properties.
621 Tagged<GlobalDictionary> properties = object->global_dictionary(kAcquireLoad);
622 RecordHashTableVirtualObjectStats(object, properties,
623 StatsEnum::GLOBAL_PROPERTIES_TYPE);
624 // Elements.
625 Tagged<FixedArrayBase> elements = object->elements();
626 RecordSimpleVirtualObjectStats(object, elements,
627 StatsEnum::GLOBAL_ELEMENTS_TYPE);
628}
629
631 Tagged<JSObject> object) {
632 // JSGlobalObject is recorded separately.
633 if (IsJSGlobalObject(object)) return;
634
635 // Uncompiled JSFunction has a separate type.
636 if (IsJSFunction(object) &&
637 !Cast<JSFunction>(object)->is_compiled(isolate())) {
639 StatsEnum::JS_UNCOMPILED_FUNCTION_TYPE);
640 }
641
642 // Properties.
643 if (object->HasFastProperties()) {
644 Tagged<PropertyArray> properties = object->property_array();
645 if (properties != ReadOnlyRoots(heap_).empty_property_array()) {
646 size_t over_allocated =
647 object->map()->UnusedPropertyFields() * kTaggedSize;
648 RecordVirtualObjectStats(object, properties,
649 object->map()->is_prototype_map()
650 ? StatsEnum::PROTOTYPE_PROPERTY_ARRAY_TYPE
651 : StatsEnum::OBJECT_PROPERTY_ARRAY_TYPE,
652 properties->Size(), over_allocated);
653 }
654 } else {
655 Tagged<NameDictionary> properties = object->property_dictionary();
657 object, properties,
658 object->map()->is_prototype_map()
659 ? StatsEnum::PROTOTYPE_PROPERTY_DICTIONARY_TYPE
660 : StatsEnum::OBJECT_PROPERTY_DICTIONARY_TYPE);
661 }
662
663 // Elements.
664 Tagged<FixedArrayBase> elements = object->elements();
665 if (object->HasDictionaryElements()) {
667 object, Cast<NumberDictionary>(elements),
668 IsJSArray(object) ? StatsEnum::ARRAY_DICTIONARY_ELEMENTS_TYPE
669 : StatsEnum::OBJECT_DICTIONARY_ELEMENTS_TYPE);
670 } else if (IsJSArray(object)) {
671 if (elements != ReadOnlyRoots(heap_).empty_fixed_array()) {
672 size_t element_size =
673 (elements->Size() - FixedArrayBase::kHeaderSize) / elements->length();
674 uint32_t length = Object::NumberValue(Cast<JSArray>(object)->length());
675 size_t over_allocated = (elements->length() - length) * element_size;
676 RecordVirtualObjectStats(object, elements, StatsEnum::ARRAY_ELEMENTS_TYPE,
677 elements->Size(), over_allocated);
678 }
679 } else {
680 RecordSimpleVirtualObjectStats(object, elements,
681 StatsEnum::OBJECT_ELEMENTS_TYPE);
682 }
683
684 // JSCollections.
685 if (IsJSCollection(object)) {
686 Tagged<Object> maybe_table = Cast<JSCollection>(object)->table();
687 if (!IsUndefined(maybe_table, isolate())) {
688 DCHECK(IsFixedArray(maybe_table, isolate()));
689 // TODO(bmeurer): Properly compute over-allocation here.
691 StatsEnum::JS_COLLECTION_TABLE_TYPE);
692 }
693 }
694}
695
697 Tagged<MaybeObject> maybe_obj, FeedbackSlotKind kind, Isolate* isolate) {
698 if (maybe_obj.IsCleared()) return StatsEnum::FEEDBACK_VECTOR_SLOT_OTHER_TYPE;
699 Tagged<Object> obj = maybe_obj.GetHeapObjectOrSmi();
700 switch (kind) {
702 if (obj == *isolate->factory()->uninitialized_symbol()) {
703 return StatsEnum::FEEDBACK_VECTOR_SLOT_CALL_UNUSED_TYPE;
704 }
705 return StatsEnum::FEEDBACK_VECTOR_SLOT_CALL_TYPE;
706
712 if (obj == *isolate->factory()->uninitialized_symbol()) {
713 return StatsEnum::FEEDBACK_VECTOR_SLOT_LOAD_UNUSED_TYPE;
714 }
715 return StatsEnum::FEEDBACK_VECTOR_SLOT_LOAD_TYPE;
716
724 if (obj == *isolate->factory()->uninitialized_symbol()) {
725 return StatsEnum::FEEDBACK_VECTOR_SLOT_STORE_UNUSED_TYPE;
726 }
727 return StatsEnum::FEEDBACK_VECTOR_SLOT_STORE_TYPE;
728
731 return StatsEnum::FEEDBACK_VECTOR_SLOT_ENUM_TYPE;
732
733 default:
734 return StatsEnum::FEEDBACK_VECTOR_SLOT_OTHER_TYPE;
735 }
736}
737
739 Tagged<FeedbackVector> vector) {
740 if (virtual_objects_.find(vector) != virtual_objects_.end()) return;
741 // Manually insert the feedback vector into the virtual object list, since
742 // we're logging its component parts separately.
743 virtual_objects_.insert(vector);
744
745 size_t calculated_size = 0;
746
747 // Log the feedback vector's header (fixed fields).
748 size_t header_size = vector->slots_start().address() - vector.address();
749 stats_->RecordVirtualObjectStats(StatsEnum::FEEDBACK_VECTOR_HEADER_TYPE,
750 header_size, ObjectStats::kNoOverAllocation);
751 calculated_size += header_size;
752
753 // Iterate over the feedback slots and log each one.
754 if (!vector->shared_function_info()->HasFeedbackMetadata()) return;
755
756 FeedbackMetadataIterator it(vector->metadata());
757 while (it.HasNext()) {
758 FeedbackSlot slot = it.Next();
759 // Log the entry (or entries) taken up by this slot.
760 size_t slot_size = it.entry_size() * kTaggedSize;
762 GetFeedbackSlotType(vector->Get(slot), it.kind(), heap_->isolate()),
764 calculated_size += slot_size;
765
766 // Log the monomorphic/polymorphic helper objects that this slot owns.
767 for (int i = 0; i < it.entry_size(); i++) {
768 Tagged<MaybeObject> raw_object = vector->Get(slot.WithOffset(i));
769 Tagged<HeapObject> object;
770 if (raw_object.GetHeapObject(&object)) {
771 if (IsCell(object, cage_base()) ||
772 IsWeakFixedArray(object, cage_base())) {
773 RecordSimpleVirtualObjectStats(vector, object,
774 StatsEnum::FEEDBACK_VECTOR_ENTRY_TYPE);
775 }
776 }
777 }
778 }
779
780 CHECK_EQ(calculated_size, vector->Size());
781}
782
784 Tagged<FixedArray> array) {
785 if (IsCowArray(array)) {
786 RecordVirtualObjectStats(HeapObject(), array, StatsEnum::COW_ARRAY_TYPE,
787 array->Size(), ObjectStats::kNoOverAllocation,
788 kIgnoreCow);
789 }
790}
791
793 Tagged<HeapObject> obj, Phase phase,
794 CollectFieldStats collect_field_stats) {
796 Tagged<Map> map = obj->map(cage_base());
797 InstanceType instance_type = map->instance_type();
798 switch (phase) {
799 case kPhase1:
800 if (InstanceTypeChecker::IsFeedbackVector(instance_type)) {
802 } else if (InstanceTypeChecker::IsMap(instance_type)) {
804 } else if (InstanceTypeChecker::IsBytecodeArray(instance_type)) {
806 } else if (InstanceTypeChecker::IsInstructionStream(instance_type)) {
808 } else if (InstanceTypeChecker::IsFunctionTemplateInfo(instance_type)) {
811 } else if (InstanceTypeChecker::IsJSGlobalObject(instance_type)) {
813 } else if (InstanceTypeChecker::IsJSObject(instance_type)) {
814 // This phase needs to come after RecordVirtualAllocationSiteDetails
815 // to properly split among boilerplates.
817 } else if (InstanceTypeChecker::IsSharedFunctionInfo(instance_type)) {
819 } else if (InstanceTypeChecker::IsContext(instance_type)) {
821 } else if (InstanceTypeChecker::IsScript(instance_type)) {
823 } else if (InstanceTypeChecker::IsArrayBoilerplateDescription(
824 instance_type)) {
827 } else if (InstanceTypeChecker::IsFixedArrayExact(instance_type)) {
828 // Has to go last as it triggers too eagerly.
830 }
831 break;
832 case kPhase2:
833 size_t over_allocated = ObjectStats::kNoOverAllocation;
834 if (InstanceTypeChecker::IsExternalString(instance_type)) {
835 // This has to be in Phase2 to avoid conflicting with recording Script
836 // sources. We still want to run RecordObjectStats after though.
838
839 } else if (InstanceTypeChecker::IsJSObject(instance_type)) {
840 over_allocated = map->instance_size() - map->UsedInstanceSize();
841 }
842 bool recorded = RecordObjectStats(obj, instance_type,
843 obj->Size(cage_base()), over_allocated);
844
845 if (recorded && InstanceTypeChecker::IsDescriptorArray(instance_type)) {
846 // This DescriptorArray has just been recorded, append stats about
847 // potentially wasted memory in the object.
849 }
850
851 if (collect_field_stats == CollectFieldStats::kYes) {
853 }
854 break;
855 }
856}
857
859 // Iterate boilerplates first to disambiguate them from regular JS objects.
862 while (TryCast(list, &site)) {
864 list = site->weak_next();
865 }
866
867 // FixedArray.
868 RecordSimpleVirtualObjectStats(HeapObject(), heap_->serialized_objects(),
869 StatsEnum::SERIALIZED_OBJECTS_TYPE);
870 RecordSimpleVirtualObjectStats(HeapObject(), heap_->number_string_cache(),
871 StatsEnum::NUMBER_STRING_CACHE_TYPE);
874 StatsEnum::SINGLE_CHARACTER_STRING_TABLE_TYPE);
875 RecordSimpleVirtualObjectStats(HeapObject(), heap_->string_split_cache(),
876 StatsEnum::STRING_SPLIT_CACHE_TYPE);
877 RecordSimpleVirtualObjectStats(HeapObject(), heap_->regexp_multiple_cache(),
878 StatsEnum::REGEXP_MULTIPLE_CACHE_TYPE);
879
880 // WeakArrayList.
882 Cast<WeakArrayList>(heap_->script_list()),
883 StatsEnum::SCRIPT_LIST_TYPE);
884}
885
887 InstanceType type, size_t size,
888 size_t over_allocated) {
889 if (virtual_objects_.find(obj) == virtual_objects_.end()) {
890 stats_->RecordObjectStats(type, size, over_allocated);
891 return true;
892 }
893 return false;
894}
895
898 ReadOnlyRoots roots(heap_);
899 return array != roots.empty_fixed_array() &&
900 array != roots.empty_slow_element_dictionary() &&
901 array != roots.empty_property_dictionary();
902}
903
905 return array->map() == ReadOnlyRoots(heap_).fixed_cow_array_map();
906}
907
909 Tagged<HeapObject> obj2) {
910 if (obj1.is_null() || obj2.is_null()) return true;
911 const auto obj1_marked =
913 const auto obj2_marked =
915 return obj1_marked == obj2_marked;
916}
917
919 // TODO(mlippautz): map->dependent_code(): DEPENDENT_CODE_TYPE.
920
921 // For Map we want to distinguish between various different states
922 // to get a better picture of what's going on in MapSpace. This
923 // method computes the virtual instance type to use for a given map,
924 // using MAP_TYPE for regular maps that aren't special in any way.
925 if (map->is_prototype_map()) {
926 if (map->is_dictionary_map()) {
928 StatsEnum::MAP_PROTOTYPE_DICTIONARY_TYPE);
929 } else if (map->is_abandoned_prototype_map()) {
931 StatsEnum::MAP_ABANDONED_PROTOTYPE_TYPE);
932 } else {
934 StatsEnum::MAP_PROTOTYPE_TYPE);
935 }
936 } else if (map->is_deprecated()) {
938 StatsEnum::MAP_DEPRECATED_TYPE);
939 } else if (map->is_dictionary_map()) {
941 StatsEnum::MAP_DICTIONARY_TYPE);
942 } else if (map->is_stable()) {
944 StatsEnum::MAP_STABLE_TYPE);
945 } else {
946 // This will be logged as MAP_TYPE in Phase2.
947 }
948
949 Tagged<DescriptorArray> array = map->instance_descriptors(cage_base());
950 if (map->owns_descriptors() &&
951 array != ReadOnlyRoots(heap_).empty_descriptor_array()) {
952 // Generally DescriptorArrays have their own instance type already
953 // (DESCRIPTOR_ARRAY_TYPE), but we'd like to be able to tell which
954 // of those are for (abandoned) prototypes, and which of those are
955 // owned by deprecated maps.
956 bool recorded = false;
957 if (map->is_prototype_map()) {
959 map, array, StatsEnum::PROTOTYPE_DESCRIPTOR_ARRAY_TYPE);
960
961 } else if (map->is_deprecated()) {
963 map, array, StatsEnum::DEPRECATED_DESCRIPTOR_ARRAY_TYPE);
964 }
965 if (recorded) {
966 // This DescriptorArray has just been recorded, append stats about
967 // potentially wasted memory in the object.
969 }
970
971 Tagged<EnumCache> enum_cache = array->enum_cache();
972 RecordSimpleVirtualObjectStats(array, enum_cache->keys(),
973 StatsEnum::ENUM_KEYS_CACHE_TYPE);
974 RecordSimpleVirtualObjectStats(array, enum_cache->indices(),
975 StatsEnum::ENUM_INDICES_CACHE_TYPE);
976 }
977
978 if (map->is_prototype_map()) {
979 Tagged<PrototypeInfo> prototype_info;
980 if (map->TryGetPrototypeInfo(&prototype_info)) {
981 Tagged<Object> users = prototype_info->prototype_users();
982 if (IsWeakFixedArray(users, cage_base())) {
984 StatsEnum::PROTOTYPE_USERS_TYPE);
985 }
986 }
987 }
988}
989
991 Tagged<Script> script) {
992 RecordSimpleVirtualObjectStats(script, script->infos(),
993 StatsEnum::SCRIPT_INFOS_TYPE);
994
995 // Log the size of external source code.
996 Tagged<Object> raw_source = script->source();
997 if (IsExternalString(raw_source, cage_base())) {
998 // The contents of external strings aren't on the heap, so we have to record
999 // them manually. The on-heap String object is recorded independently in
1000 // the normal pass.
1001 Tagged<ExternalString> string = Cast<ExternalString>(raw_source);
1002 Address resource = string->resource_as_address();
1003 size_t off_heap_size = string->ExternalPayloadSize();
1005 resource,
1006 string->IsOneByteRepresentation()
1007 ? StatsEnum::SCRIPT_SOURCE_EXTERNAL_ONE_BYTE_TYPE
1008 : StatsEnum::SCRIPT_SOURCE_EXTERNAL_TWO_BYTE_TYPE,
1009 off_heap_size);
1010 } else if (IsString(raw_source, cage_base())) {
1011 Tagged<String> source = Cast<String>(raw_source);
1013 script, source,
1014 source->IsOneByteRepresentation()
1015 ? StatsEnum::SCRIPT_SOURCE_NON_EXTERNAL_ONE_BYTE_TYPE
1016 : StatsEnum::SCRIPT_SOURCE_NON_EXTERNAL_TWO_BYTE_TYPE);
1017 }
1018}
1019
1021 Tagged<ExternalString> string) {
1022 // Track the external string resource size in a separate category.
1023
1024 Address resource = string->resource_as_address();
1025 size_t off_heap_size = string->ExternalPayloadSize();
1027 resource,
1028 string->IsOneByteRepresentation()
1029 ? StatsEnum::STRING_EXTERNAL_RESOURCE_ONE_BYTE_TYPE
1030 : StatsEnum::STRING_EXTERNAL_RESOURCE_TWO_BYTE_TYPE,
1031 off_heap_size);
1032}
1033
1036 // Uncompiled SharedFunctionInfo gets its own category.
1037 if (!info->is_compiled()) {
1039 HeapObject(), info, StatsEnum::UNCOMPILED_SHARED_FUNCTION_INFO_TYPE);
1040 }
1041}
1042
1046 description, description->constant_elements(),
1047 StatsEnum::ARRAY_BOILERPLATE_DESCRIPTION_ELEMENTS_TYPE);
1048}
1049
1054 if (!RecordSimpleVirtualObjectStats(parent, object, type)) return;
1055 if (IsFixedArrayExact(object, cage_base())) {
1056 Tagged<FixedArray> array = Cast<FixedArray>(object);
1057 for (int i = 0; i < array->length(); i++) {
1058 Tagged<Object> entry = array->get(i);
1059 if (!IsHeapObject(entry)) continue;
1061 array, Cast<HeapObject>(entry), type);
1062 }
1063 }
1064}
1065
1067 Tagged<BytecodeArray> bytecode) {
1068 RecordSimpleVirtualObjectStats(bytecode, bytecode->constant_pool(),
1069 StatsEnum::BYTECODE_ARRAY_CONSTANT_POOL_TYPE);
1070 // FixedArrays on constant pool are used for holding descriptor information.
1071 // They are shared with optimized code.
1073 Cast<TrustedFixedArray>(bytecode->constant_pool());
1074 for (int i = 0; i < constant_pool->length(); i++) {
1075 Tagged<Object> entry = constant_pool->get(i);
1076 if (IsFixedArrayExact(entry)) {
1079 StatsEnum::EMBEDDED_OBJECT_TYPE);
1080 }
1081 }
1082 RecordSimpleVirtualObjectStats(bytecode, bytecode->handler_table(),
1083 StatsEnum::BYTECODE_ARRAY_HANDLER_TABLE_TYPE);
1084 if (bytecode->HasSourcePositionTable()) {
1085 RecordSimpleVirtualObjectStats(bytecode, bytecode->SourcePositionTable(),
1086 StatsEnum::SOURCE_POSITION_TABLE_TYPE);
1087 }
1088}
1089
1090namespace {
1091
1092ObjectStats::VirtualInstanceType CodeKindToVirtualInstanceType(CodeKind kind) {
1093 switch (kind) {
1094#define CODE_KIND_CASE(type) \
1095 case CodeKind::type: \
1096 return StatsEnum::type;
1098#undef CODE_KIND_CASE
1099 }
1100 UNREACHABLE();
1101}
1102
1103} // namespace
1104
1106 Tagged<InstructionStream> istream) {
1108 if (!istream->TryGetCode(&code, kAcquireLoad)) return;
1110 CodeKindToVirtualInstanceType(code->kind()));
1111 RecordSimpleVirtualObjectStats(istream, istream->relocation_info(),
1112 StatsEnum::RELOC_INFO_TYPE);
1113 if (CodeKindIsOptimizedJSFunction(code->kind())) {
1114 Tagged<Object> source_position_table = code->source_position_table();
1115 if (IsHeapObject(source_position_table)) {
1117 Cast<HeapObject>(source_position_table),
1118 StatsEnum::SOURCE_POSITION_TABLE_TYPE);
1119 }
1120 RecordSimpleVirtualObjectStats(istream, code->deoptimization_data(),
1121 StatsEnum::DEOPTIMIZATION_DATA_TYPE);
1122 Tagged<DeoptimizationData> input_data =
1123 Cast<DeoptimizationData>(code->deoptimization_data());
1124 if (input_data->length() > 0) {
1125 RecordSimpleVirtualObjectStats(code->deoptimization_data(),
1126 input_data->LiteralArray(),
1127 StatsEnum::OPTIMIZED_CODE_LITERALS_TYPE);
1128 }
1129 }
1130 int const mode_mask = RelocInfo::EmbeddedObjectModeMask();
1131 for (RelocIterator it(code, mode_mask); !it.done(); it.next()) {
1132 DCHECK(RelocInfo::IsEmbeddedObjectMode(it.rinfo()->rmode()));
1133 Tagged<Object> target = it.rinfo()->target_object(cage_base());
1134 if (IsFixedArrayExact(target, cage_base())) {
1136 istream, Cast<HeapObject>(target), StatsEnum::EMBEDDED_OBJECT_TYPE);
1137 }
1138 }
1139}
1140
1142 if (IsNativeContext(context)) {
1143 RecordObjectStats(context, NATIVE_CONTEXT_TYPE, context->Size());
1144 if (IsWeakArrayList(context->retained_maps(), cage_base())) {
1146 context, Cast<WeakArrayList>(context->retained_maps()),
1147 StatsEnum::RETAINED_MAPS_TYPE);
1148 }
1149
1150 } else if (context->IsFunctionContext()) {
1151 RecordObjectStats(context, FUNCTION_CONTEXT_TYPE, context->Size());
1152 } else {
1154 StatsEnum::OTHER_CONTEXT_TYPE);
1155 }
1156}
1157
1186
1187namespace {
1188
1189void IterateHeap(Heap* heap, ObjectStatsVisitor* visitor) {
1190 // We don't perform a GC while collecting object stats but need this scope for
1191 // the nested SafepointScope inside CombinedHeapObjectIterator.
1192 AllowGarbageCollection allow_gc;
1194 for (Tagged<HeapObject> obj = iterator.Next(); !obj.is_null();
1195 obj = iterator.Next()) {
1196 visitor->Visit(obj);
1197 }
1198}
1199
1200} // namespace
1201
1203 ObjectStatsCollectorImpl live_collector(heap_, live_);
1204 ObjectStatsCollectorImpl dead_collector(heap_, dead_);
1205 live_collector.CollectGlobalStatistics();
1206 for (int i = 0; i < ObjectStatsCollectorImpl::kNumberOfPhases; i++) {
1207 ObjectStatsVisitor visitor(heap_, &live_collector, &dead_collector,
1208 static_cast<ObjectStatsCollectorImpl::Phase>(i));
1209 IterateHeap(heap_, &visitor);
1210 }
1211}
1212
1213} // namespace internal
1214} // namespace v8
Builtins::Kind kind
Definition builtins.cc:40
FeedbackSlot WithOffset(int offset) const
Definition utils.h:658
static FieldIndex ForDetails(Tagged< Map > map, PropertyDetails details)
void RecordStats(Tagged< HeapObject > host)
void VisitPointers(Tagged< HeapObject > host, MaybeObjectSlot start, MaybeObjectSlot end) override
std::unordered_map< Tagged< Map >, JSObjectFieldStats, Object::Hasher > field_stats_cache_
void VisitMapPointer(Tagged< HeapObject > host) override
FieldStatsCollector(Heap *heap, size_t *tagged_fields_count, size_t *embedder_fields_count, size_t *inobject_smi_fields_count, size_t *boxed_double_fields_count, size_t *string_data_count, size_t *raw_fields_count)
V8_INLINE void VisitInstructionStreamPointer(Tagged< Code > host, InstructionStreamSlot slot) override
void VisitEmbeddedPointer(Tagged< InstructionStream > host, RelocInfo *rinfo) override
JSObjectFieldStats GetInobjectFieldStats(Tagged< Map > map)
void VisitPointers(Tagged< HeapObject > host, ObjectSlot start, ObjectSlot end) override
void VisitCodeTarget(Tagged< InstructionStream > host, RelocInfo *rinfo) override
static constexpr int kHeaderSize
V8_INLINE Tagged< FixedArray > single_character_string_table()
Definition heap-inl.h:78
int gc_count() const
Definition heap.h:1351
Tagged< UnionOf< Smi, Undefined, AllocationSiteWithWeakNext > > allocation_sites_list()
Definition heap.h:471
Isolate * isolate() const
Definition heap-inl.h:61
double time_millis_since_init() const
Definition isolate.h:1615
int GetEmbedderFieldCount() const
NonAtomicMarkingState *const marking_state_
void RecordVirtualExternalStringDetails(Tagged< ExternalString > script)
void RecordVirtualFixedArrayDetails(Tagged< FixedArray > array)
void RecordVirtualJSGlobalObjectDetails(Tagged< JSGlobalObject > object)
bool SameLiveness(Tagged< HeapObject > obj1, Tagged< HeapObject > obj2)
void RecordExternalResourceStats(Address resource, ObjectStats::VirtualInstanceType type, size_t size)
bool IsCowArray(Tagged< FixedArrayBase > array)
ObjectStatsCollectorImpl(Heap *heap, ObjectStats *stats)
std::unordered_set< Tagged< HeapObject >, Object::Hasher, Object::KeyEqualSafe > virtual_objects_
void RecordHashTableVirtualObjectStats(Tagged< HeapObject > parent, Tagged< Dictionary > hash_table, ObjectStats::VirtualInstanceType type)
void RecordVirtualBytecodeArrayDetails(Tagged< BytecodeArray > bytecode)
void RecordVirtualSharedFunctionInfoDetails(Tagged< SharedFunctionInfo > info)
bool RecordSimpleVirtualObjectStats(Tagged< HeapObject > parent, Tagged< HeapObject > obj, ObjectStats::VirtualInstanceType type)
bool RecordVirtualObjectStats(Tagged< HeapObject > parent, Tagged< HeapObject > obj, ObjectStats::VirtualInstanceType type, size_t size, size_t over_allocated, CowMode check_cow_array=kCheckCow)
bool CanRecordFixedArray(Tagged< FixedArrayBase > array)
bool ShouldRecordObject(Tagged< HeapObject > object, CowMode check_cow_array)
void RecordVirtualScriptDetails(Tagged< Script > script)
void RecordVirtualContext(Tagged< Context > context)
void RecordVirtualJSObjectDetails(Tagged< JSObject > object)
void RecordVirtualObjectsForConstantPoolOrEmbeddedObjects(Tagged< HeapObject > parent, Tagged< HeapObject > object, ObjectStats::VirtualInstanceType type)
void RecordPotentialDescriptorArraySavingsStats(Tagged< DescriptorArray > obj)
void RecordVirtualAllocationSiteDetails(Tagged< AllocationSite > site)
void CollectStatistics(Tagged< HeapObject > obj, Phase phase, CollectFieldStats collect_field_stats)
void RecordVirtualFunctionTemplateInfoDetails(Tagged< FunctionTemplateInfo > fti)
void RecordVirtualCodeDetails(Tagged< InstructionStream > code)
std::unordered_set< Address > external_resources_
void RecordVirtualMapDetails(Tagged< Map > map)
void RecordVirtualFeedbackVectorDetails(Tagged< FeedbackVector > vector)
void RecordVirtualArrayBoilerplateDescription(Tagged< ArrayBoilerplateDescription > description)
bool RecordObjectStats(Tagged< HeapObject > obj, InstanceType type, size_t size, size_t over_allocated=ObjectStats::kNoOverAllocation)
ObjectStatsCollectorImpl::Phase phase_
ObjectStatsVisitor(Heap *heap, ObjectStatsCollectorImpl *live_collector, ObjectStatsCollectorImpl *dead_collector, ObjectStatsCollectorImpl::Phase phase)
void Visit(Tagged< HeapObject > obj)
ObjectStatsCollectorImpl *const dead_collector_
ObjectStatsCollectorImpl *const live_collector_
NonAtomicMarkingState *const marking_state_
static const int kNumberOfBuckets
size_t object_sizes_last_time_[OBJECT_STATS_COUNT]
size_t over_allocated_histogram_[OBJECT_STATS_COUNT][kNumberOfBuckets]
size_t object_counts_last_time_[OBJECT_STATS_COUNT]
V8_NOINLINE void DumpInstanceTypeData(std::stringstream &stream, const char *name, int index)
void RecordVirtualObjectStats(VirtualInstanceType type, size_t size, size_t over_allocated)
size_t over_allocated_[OBJECT_STATS_COUNT]
int HistogramIndexFromSize(size_t size)
void PrintKeyAndId(const char *key, int gc_count)
void PrintJSON(const char *key)
void Dump(std::stringstream &stream)
void RecordObjectStats(InstanceType type, size_t size, size_t over_allocated=kNoOverAllocation)
static const int kFirstBucketShift
size_t size_histogram_[OBJECT_STATS_COUNT][kNumberOfBuckets]
V8_NOINLINE void PrintInstanceTypeJSON(const char *key, int gc_count, const char *name, int index)
size_t object_sizes_[OBJECT_STATS_COUNT]
static const int kLastValueBucketIndex
size_t object_counts_[OBJECT_STATS_COUNT]
void ClearObjectStats(bool clear_last_time_stats=false)
static const size_t kNoOverAllocation
static constexpr int FIRST_VIRTUAL_TYPE
PtrComprCageBase cage_base() const
Definition visitors.h:225
static double NumberValue(Tagged< Number > obj)
PropertyLocation location() const
Representation representation() const
static constexpr bool IsEmbeddedObjectMode(Mode mode)
Definition reloc-info.h:209
static int EmbeddedObjectModeMask()
Definition reloc-info.h:378
constexpr bool IsSmi() const
constexpr bool SafeEquals(TaggedImpl< kOtherRefType, StorageType > other) const
Definition tagged-impl.h:93
constexpr bool IsCleared() const
bool GetHeapObject(Tagged< HeapObject > *result) const
Tagged< Object > GetHeapObjectOrSmi() const
V8_INLINE constexpr bool is_null() const
Definition tagged.h:502
#define CODE_KIND_LIST(V)
Definition code-kind.h:18
Handle< Code > code
int start
int end
NonAtomicMarkingState * marking_state_
#define LAZY_MUTEX_INITIALIZER
Definition mutex.h:105
constexpr unsigned CountLeadingZeros(T value)
Definition bits.h:100
V8_INLINE constexpr bool IsExternalString(InstanceType instance_type)
bool TryCast(Tagged< From > value, Tagged< To > *out)
Definition casting.h:77
constexpr int kTaggedSize
Definition globals.h:542
constexpr bool CodeKindIsOptimizedJSFunction(CodeKind kind)
Definition code-kind.h:66
static ObjectStats::VirtualInstanceType GetFeedbackSlotType(Tagged< MaybeObject > maybe_obj, FeedbackSlotKind kind, Isolate *isolate)
static V8_NOINLINE void PrintJSONArray(size_t *array, const int len)
void PrintF(const char *format,...)
Definition utils.cc:39
constexpr int kEmbedderDataSlotSize
Definition globals.h:664
void VisitObject(Isolate *isolate, Tagged< HeapObject > object, ObjectVisitor *visitor)
return Cast< NumberDictionary >(elements(cage_base))
constexpr int kSystemPointerSize
Definition globals.h:410
V8_INLINE constexpr bool IsHeapObject(TaggedImpl< kRefType, StorageType > obj)
Definition objects.h:669
bool IsAny(Tagged< FieldType > obj)
Definition field-type.h:51
static const int kDescriptorIndexBitCount
constexpr int kDoubleSize
Definition globals.h:407
constexpr int kSizetSize
Definition globals.h:404
void MemCopy(void *dest, const void *src, size_t size)
Definition memcopy.h:124
static V8_NOINLINE void DumpJSONArray(std::stringstream &stream, size_t *array, const int len)
static base::LazyMutex object_stats_mutex
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
static constexpr AcquireLoadTag kAcquireLoad
Definition globals.h:2908
#define CODE_KIND_CASE(type)
#define VIRTUAL_INSTANCE_TYPE_WRAPPER(name)
#define INSTANCE_TYPE_WRAPPER(name)
#define VIRTUAL_INSTANCE_TYPE_LIST(V)
#define INSTANCE_TYPE_LIST(V)
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define CHECK_LT(lhs, rhs)
#define CHECK_EQ(lhs, rhs)
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_GT(v1, v2)
Definition logging.h:487
static V8_INLINE bool IsMarkedOrAlwaysLive(Heap *heap, MarkingStateT *marking_state, Tagged< HeapObject > object)
Heap * heap_
#define V8_INLINE
Definition v8config.h:500
#define V8_NOINLINE
Definition v8config.h:586
wasm::ValueType type