Reland "[heap-stats] Fix heap-stats with ptr-cage"

This is a reland of 9ae463bc43

- Don't run the heap stats during bootstrapping

Original change's description:
> [heap-stats] Fix heap-stats with ptr-cage
>
> - Heap-stats was trying to load the map without explicitly passing in
>   the PtrComprBase causing failures with Code objects in external code
>   space
> - Extend the debugPrint.js tests to run with some more debugging and
>   testing flags to prevent future regressions
>
> Change-Id: I1f0d03cb31480f316fe533b507ff98fe3befbe8e
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3432386
> Reviewed-by: Igor Sheludko <ishell@chromium.org>
> Auto-Submit: Camillo Bruni <cbruni@chromium.org>
> Reviewed-by: Dominik Inführ <dinfuehr@chromium.org>
> Commit-Queue: Dominik Inführ <dinfuehr@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#78919}

Bug: chromium:1297436
Change-Id: Ib42ae7b8c5f4a427abbce633a1b3ac36ad32994b
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3437046
Reviewed-by: Dominik Inführ <dinfuehr@chromium.org>
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/main@{#79127}
This commit is contained in:
Camillo Bruni 2022-02-03 16:52:11 +01:00 committed by V8 LUCI CQ
parent a944e66b05
commit 148d9853e0
3 changed files with 88 additions and 55 deletions

View File

@ -2289,28 +2289,29 @@ void MarkCompactCollector::ProcessTopOptimizedFrame(ObjectVisitor* visitor,
}
void MarkCompactCollector::RecordObjectStats() {
if (V8_UNLIKELY(TracingFlags::is_gc_stats_enabled())) {
heap()->CreateObjectStats();
ObjectStatsCollector collector(heap(), heap()->live_object_stats_.get(),
heap()->dead_object_stats_.get());
collector.Collect();
if (V8_UNLIKELY(TracingFlags::gc_stats.load(std::memory_order_relaxed) &
v8::tracing::TracingCategoryObserver::ENABLED_BY_TRACING)) {
std::stringstream live, dead;
heap()->live_object_stats_->Dump(live);
heap()->dead_object_stats_->Dump(dead);
TRACE_EVENT_INSTANT2(TRACE_DISABLED_BY_DEFAULT("v8.gc_stats"),
"V8.GC_Objects_Stats", TRACE_EVENT_SCOPE_THREAD,
"live", TRACE_STR_COPY(live.str().c_str()), "dead",
TRACE_STR_COPY(dead.str().c_str()));
}
if (FLAG_trace_gc_object_stats) {
heap()->live_object_stats_->PrintJSON("live");
heap()->dead_object_stats_->PrintJSON("dead");
}
heap()->live_object_stats_->CheckpointObjectStats();
heap()->dead_object_stats_->ClearObjectStats();
if (V8_LIKELY(!TracingFlags::is_gc_stats_enabled())) return;
// Cannot run during bootstrapping due to incomplete objects.
if (isolate()->bootstrapper()->IsActive()) return;
heap()->CreateObjectStats();
ObjectStatsCollector collector(heap(), heap()->live_object_stats_.get(),
heap()->dead_object_stats_.get());
collector.Collect();
if (V8_UNLIKELY(TracingFlags::gc_stats.load(std::memory_order_relaxed) &
v8::tracing::TracingCategoryObserver::ENABLED_BY_TRACING)) {
std::stringstream live, dead;
heap()->live_object_stats_->Dump(live);
heap()->dead_object_stats_->Dump(dead);
TRACE_EVENT_INSTANT2(TRACE_DISABLED_BY_DEFAULT("v8.gc_stats"),
"V8.GC_Objects_Stats", TRACE_EVENT_SCOPE_THREAD,
"live", TRACE_STR_COPY(live.str().c_str()), "dead",
TRACE_STR_COPY(dead.str().c_str()));
}
if (FLAG_trace_gc_object_stats) {
heap()->live_object_stats_->PrintJSON("live");
heap()->dead_object_stats_->PrintJSON("dead");
}
heap()->live_object_stats_->CheckpointObjectStats();
heap()->dead_object_stats_->ClearObjectStats();
}
void MarkCompactCollector::MarkLiveObjects() {

View File

@ -443,6 +443,11 @@ class ObjectStatsCollectorImpl {
void RecordVirtualArrayBoilerplateDescription(
ArrayBoilerplateDescription description);
PtrComprCageBase cage_base() const {
return field_stats_collector_.cage_base();
}
Heap* heap_;
ObjectStats* stats_;
MarkCompactCollector::NonAtomicMarkingState* marking_state_;
@ -488,7 +493,7 @@ void ObjectStatsCollectorImpl::RecordHashTableVirtualObjectStats(
bool ObjectStatsCollectorImpl::RecordSimpleVirtualObjectStats(
HeapObject parent, HeapObject obj, ObjectStats::VirtualInstanceType type) {
return RecordVirtualObjectStats(parent, obj, type, obj.Size(),
return RecordVirtualObjectStats(parent, obj, type, obj.Size(cage_base()),
ObjectStats::kNoOverAllocation, kCheckCow);
}
@ -711,7 +716,8 @@ void ObjectStatsCollectorImpl::RecordVirtualFeedbackVectorDetails(
MaybeObject raw_object = vector.Get(slot.WithOffset(i));
HeapObject object;
if (raw_object->GetHeapObject(&object)) {
if (object.IsCell() || object.IsWeakFixedArray()) {
if (object.IsCell(cage_base()) ||
object.IsWeakFixedArray(cage_base())) {
RecordSimpleVirtualObjectStats(
vector, object, ObjectStats::FEEDBACK_VECTOR_ENTRY_TYPE);
}
@ -733,51 +739,55 @@ void ObjectStatsCollectorImpl::RecordVirtualFixedArrayDetails(
void ObjectStatsCollectorImpl::CollectStatistics(
HeapObject obj, Phase phase, CollectFieldStats collect_field_stats) {
Map map = obj.map();
DisallowGarbageCollection no_gc;
Map map = obj.map(cage_base());
InstanceType instance_type = map.instance_type();
switch (phase) {
case kPhase1:
if (obj.IsFeedbackVector()) {
if (InstanceTypeChecker::IsFeedbackVector(instance_type)) {
RecordVirtualFeedbackVectorDetails(FeedbackVector::cast(obj));
} else if (obj.IsMap()) {
} else if (InstanceTypeChecker::IsMap(instance_type)) {
RecordVirtualMapDetails(Map::cast(obj));
} else if (obj.IsBytecodeArray()) {
} else if (InstanceTypeChecker::IsBytecodeArray(instance_type)) {
RecordVirtualBytecodeArrayDetails(BytecodeArray::cast(obj));
} else if (obj.IsCode()) {
} else if (InstanceTypeChecker::IsCode(instance_type)) {
RecordVirtualCodeDetails(Code::cast(obj));
} else if (obj.IsFunctionTemplateInfo()) {
} else if (InstanceTypeChecker::IsFunctionTemplateInfo(instance_type)) {
RecordVirtualFunctionTemplateInfoDetails(
FunctionTemplateInfo::cast(obj));
} else if (obj.IsJSGlobalObject()) {
} else if (InstanceTypeChecker::IsJSGlobalObject(instance_type)) {
RecordVirtualJSGlobalObjectDetails(JSGlobalObject::cast(obj));
} else if (obj.IsJSObject()) {
} else if (InstanceTypeChecker::IsJSObject(instance_type)) {
// This phase needs to come after RecordVirtualAllocationSiteDetails
// to properly split among boilerplates.
RecordVirtualJSObjectDetails(JSObject::cast(obj));
} else if (obj.IsSharedFunctionInfo()) {
} else if (InstanceTypeChecker::IsSharedFunctionInfo(instance_type)) {
RecordVirtualSharedFunctionInfoDetails(SharedFunctionInfo::cast(obj));
} else if (obj.IsContext()) {
} else if (InstanceTypeChecker::IsContext(instance_type)) {
RecordVirtualContext(Context::cast(obj));
} else if (obj.IsScript()) {
} else if (InstanceTypeChecker::IsScript(instance_type)) {
RecordVirtualScriptDetails(Script::cast(obj));
} else if (obj.IsArrayBoilerplateDescription()) {
} else if (InstanceTypeChecker::IsArrayBoilerplateDescription(
instance_type)) {
RecordVirtualArrayBoilerplateDescription(
ArrayBoilerplateDescription::cast(obj));
} else if (obj.IsFixedArrayExact()) {
} else if (InstanceTypeChecker::IsFixedArrayExact(instance_type)) {
// Has to go last as it triggers too eagerly.
RecordVirtualFixedArrayDetails(FixedArray::cast(obj));
}
break;
case kPhase2:
if (obj.IsExternalString()) {
if (InstanceTypeChecker::IsExternalString(instance_type)) {
// This has to be in Phase2 to avoid conflicting with recording Script
// sources. We still want to run RecordObjectStats after though.
RecordVirtualExternalStringDetails(ExternalString::cast(obj));
}
size_t over_allocated = ObjectStats::kNoOverAllocation;
if (obj.IsJSObject()) {
if (InstanceTypeChecker::IsJSObject(instance_type)) {
over_allocated = map.instance_size() - map.UsedInstanceSize();
}
RecordObjectStats(obj, map.instance_type(), obj.Size(), over_allocated);
RecordObjectStats(obj, instance_type, obj.Size(cage_base()),
over_allocated);
if (collect_field_stats == CollectFieldStats::kYes) {
field_stats_collector_.RecordStats(obj);
}
@ -788,7 +798,7 @@ void ObjectStatsCollectorImpl::CollectStatistics(
void ObjectStatsCollectorImpl::CollectGlobalStatistics() {
// Iterate boilerplates first to disambiguate them from regular JS objects.
Object list = heap_->allocation_sites_list();
while (list.IsAllocationSite()) {
while (list.IsAllocationSite(cage_base())) {
AllocationSite site = AllocationSite::cast(list);
RecordVirtualAllocationSiteDetails(site);
list = site.weak_next();
@ -829,7 +839,7 @@ bool ObjectStatsCollectorImpl::CanRecordFixedArray(FixedArrayBase array) {
}
bool ObjectStatsCollectorImpl::IsCowArray(FixedArrayBase array) {
return array.map() == ReadOnlyRoots(heap_).fixed_cow_array_map();
return array.map(cage_base()) == ReadOnlyRoots(heap_).fixed_cow_array_map();
}
bool ObjectStatsCollectorImpl::SameLiveness(HeapObject obj1, HeapObject obj2) {
@ -868,7 +878,7 @@ void ObjectStatsCollectorImpl::RecordVirtualMapDetails(Map map) {
// This will be logged as MAP_TYPE in Phase2.
}
DescriptorArray array = map.instance_descriptors(isolate());
DescriptorArray array = map.instance_descriptors(cage_base());
if (map.owns_descriptors() &&
array != ReadOnlyRoots(heap_).empty_descriptor_array()) {
// Generally DescriptorArrays have their own instance type already
@ -891,10 +901,10 @@ void ObjectStatsCollectorImpl::RecordVirtualMapDetails(Map map) {
}
if (map.is_prototype_map()) {
if (map.prototype_info().IsPrototypeInfo()) {
if (map.prototype_info().IsPrototypeInfo(cage_base())) {
PrototypeInfo info = PrototypeInfo::cast(map.prototype_info());
Object users = info.prototype_users();
if (users.IsWeakFixedArray()) {
if (users.IsWeakFixedArray(cage_base())) {
RecordSimpleVirtualObjectStats(map, WeakArrayList::cast(users),
ObjectStats::PROTOTYPE_USERS_TYPE);
}
@ -909,7 +919,7 @@ void ObjectStatsCollectorImpl::RecordVirtualScriptDetails(Script script) {
// Log the size of external source code.
Object raw_source = script.source();
if (raw_source.IsExternalString()) {
if (raw_source.IsExternalString(cage_base())) {
// The contents of external strings aren't on the heap, so we have to record
// them manually. The on-heap String object is recorded indepentendely in
// the normal pass.
@ -922,7 +932,7 @@ void ObjectStatsCollectorImpl::RecordVirtualScriptDetails(Script script) {
? ObjectStats::SCRIPT_SOURCE_EXTERNAL_ONE_BYTE_TYPE
: ObjectStats::SCRIPT_SOURCE_EXTERNAL_TWO_BYTE_TYPE,
off_heap_size);
} else if (raw_source.IsString()) {
} else if (raw_source.IsString(cage_base())) {
String source = String::cast(raw_source);
RecordSimpleVirtualObjectStats(
script, source,
@ -940,7 +950,7 @@ void ObjectStatsCollectorImpl::RecordVirtualExternalStringDetails(
size_t off_heap_size = string.ExternalPayloadSize();
RecordExternalResourceStats(
resource,
string.IsOneByteRepresentation()
string.IsOneByteRepresentation(cage_base())
? ObjectStats::STRING_EXTERNAL_RESOURCE_ONE_BYTE_TYPE
: ObjectStats::STRING_EXTERNAL_RESOURCE_TWO_BYTE_TYPE,
off_heap_size);
@ -967,7 +977,7 @@ void ObjectStatsCollectorImpl::
HeapObject parent, HeapObject object,
ObjectStats::VirtualInstanceType type) {
if (!RecordSimpleVirtualObjectStats(parent, object, type)) return;
if (object.IsFixedArrayExact()) {
if (object.IsFixedArrayExact(cage_base())) {
FixedArray array = FixedArray::cast(object);
for (int i = 0; i < array.length(); i++) {
Object entry = array.get(i);
@ -988,7 +998,7 @@ void ObjectStatsCollectorImpl::RecordVirtualBytecodeArrayDetails(
FixedArray constant_pool = FixedArray::cast(bytecode.constant_pool());
for (int i = 0; i < constant_pool.length(); i++) {
Object entry = constant_pool.get(i);
if (entry.IsFixedArrayExact()) {
if (entry.IsFixedArrayExact(cage_base())) {
RecordVirtualObjectsForConstantPoolOrEmbeddedObjects(
constant_pool, HeapObject::cast(entry),
ObjectStats::EMBEDDED_OBJECT_TYPE);
@ -1041,11 +1051,10 @@ void ObjectStatsCollectorImpl::RecordVirtualCodeDetails(Code code) {
}
}
int const mode_mask = RelocInfo::EmbeddedObjectModeMask();
PtrComprCageBase cage_base(heap_->isolate());
for (RelocIterator it(code, mode_mask); !it.done(); it.next()) {
DCHECK(RelocInfo::IsEmbeddedObjectMode(it.rinfo()->rmode()));
Object target = it.rinfo()->target_object(cage_base);
if (target.IsFixedArrayExact()) {
Object target = it.rinfo()->target_object(cage_base());
if (target.IsFixedArrayExact(cage_base())) {
RecordVirtualObjectsForConstantPoolOrEmbeddedObjects(
code, HeapObject::cast(target), ObjectStats::EMBEDDED_OBJECT_TYPE);
}
@ -1055,7 +1064,7 @@ void ObjectStatsCollectorImpl::RecordVirtualCodeDetails(Code code) {
void ObjectStatsCollectorImpl::RecordVirtualContext(Context context) {
if (context.IsNativeContext()) {
RecordObjectStats(context, NATIVE_CONTEXT_TYPE, context.Size());
if (context.retained_maps().IsWeakArrayList()) {
if (context.retained_maps().IsWeakArrayList(cage_base())) {
RecordSimpleVirtualObjectStats(
context, WeakArrayList::cast(context.retained_maps()),
ObjectStats::RETAINED_MAPS_TYPE);

View File

@ -2,7 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
// Test various debug flags
// Flags: --allow-natives-syntax --trace-gc-object-stats --gc-global
// Flags: --trace-zone-stats --expose-gc --trace-gc
var largeArray = [];
largeArray[0xFFFF00] = 123;
@ -25,7 +28,22 @@ function slowSloppyArguments2(a, b) {
return arguments;
}
let proto_obj = { fn1() { return 1 } }
let obj_with_enum_cache = {
__proto__: proto_obj,
a: 1,
b: 2,
c: "c"
};
for (let k in obj_with_enum_cache) {
// do something
obj_with_enum_cache.a += obj_with_enum_cache.fn1();
}
let string_1 = "aasdfasdfasdfasdf asd fa sdf asdf as dfa sdf asd f"
let string_2 = "aasdfasdfasdfasdf asd fa sdf UC16\u2028asdf as dfa sdf asd f"
var objects = [
this,
true, false, null, undefined,
@ -33,8 +51,10 @@ var objects = [
9007199254740991.0, 9007199254740991.0 + 10,
-9007199254740992.0, -9007199254740992.0 - 10,
Infinity, -Infinity, NaN,
"aasdfasdfasdfasdf", "a"+"b",
string_1, string_1+"b", string_1.slice(1),
string_2, string_2+"b", string_2.slice(1),
{}, {1:1}, {a:1}, {1:1, 2:2}, Object.create(null),
obj_with_enum_cache,
[], [{}, {}], [1, 1, 1], [1.1, 1.1, 1.1, 1.1, 2], largeArray,
new Proxy({},{}),
new Date(), new String(" a"),
@ -53,3 +73,6 @@ var objects = [
];
for (var o of objects) %DebugPrint(o);
// Trigger some gcs to trigger heap and zone stats
for (let i = 0; i <= 4; i++) gc();