From ee72b7a11f66c1a6da02bc4f64586ca0f6f9ce4c Mon Sep 17 00:00:00 2001 From: Seth Brenith Date: Fri, 15 Apr 2022 12:53:32 -0700 Subject: [PATCH] Categorize object shape info in heap snapshots MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit One of the biggest categories in heap snapshots is named “(system)”, which gives developers no indication of why all that memory is used or what they might do to reduce it. In this change, I propose that we create a new category for Maps, DescriptorArrays, and related objects, and call this new category “(object shape)” in the devtools. I think that this category name would be more meaningful, while still grouping those objects together so that they mostly stay out of the way. Bug: v8:12769 Doc: https://docs.google.com/document/d/1a-6V_2LIJuRcsppwh6E18g8OSnC9j6gN4ao2gq--BiU Change-Id: I282a7b87c34ca6ed371ff32f3c7332d794ae42ca Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3587974 Reviewed-by: Benedikt Meurer Reviewed-by: Michael Lippautz Reviewed-by: Camillo Bruni Commit-Queue: Seth Brenith Cr-Commit-Position: refs/heads/main@{#80123} --- include/v8-profiler.h | 4 ++- src/profiler/heap-snapshot-generator.cc | 46 ++++++++++++++++++++++++- src/profiler/heap-snapshot-generator.h | 7 +++- 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/include/v8-profiler.h b/include/v8-profiler.h index 2681040732..30b834dcd7 100644 --- a/include/v8-profiler.h +++ b/include/v8-profiler.h @@ -542,7 +542,9 @@ class V8_EXPORT HeapGraphNode { kConsString = 10, // Concatenated string. A pair of pointers to strings. kSlicedString = 11, // Sliced string. A fragment of another string. kSymbol = 12, // A Symbol (ES6). - kBigInt = 13 // BigInt. + kBigInt = 13, // BigInt. + kObjectShape = 14, // Internal data used for tracking the shapes (or + // "hidden classes") of JS objects. }; /** Returns node type (see HeapGraphNode::Type). */ diff --git a/src/profiler/heap-snapshot-generator.cc b/src/profiler/heap-snapshot-generator.cc index c3a28660f9..179566b216 100644 --- a/src/profiler/heap-snapshot-generator.cc +++ b/src/profiler/heap-snapshot-generator.cc @@ -380,6 +380,8 @@ const char* HeapEntry::TypeAsString() const { case kSymbol: return "/symbol/"; case kBigInt: return "/bigint/"; + case kObjectShape: + return "/object shape/"; default: return "???"; } } @@ -955,6 +957,17 @@ HeapEntry::Type V8HeapExplorer::GetSystemEntryType(HeapObject object) { return HeapEntry::kArray; } + // Maps in read-only space are for internal V8 data, not user-defined object + // shapes. + if ((InstanceTypeChecker::IsMap(type) && + !BasicMemoryChunk::FromHeapObject(object)->InReadOnlySpace()) || + InstanceTypeChecker::IsDescriptorArray(type) || + InstanceTypeChecker::IsTransitionArray(type) || + InstanceTypeChecker::IsPrototypeInfo(type) || + InstanceTypeChecker::IsEnumCache(type)) { + return HeapEntry::kObjectShape; + } + return HeapEntry::kHidden; } @@ -1098,6 +1111,8 @@ void V8HeapExplorer::ExtractReferences(HeapEntry* entry, HeapObject obj) { ExtractFeedbackCellReferences(entry, FeedbackCell::cast(obj)); } else if (obj.IsPropertyCell()) { ExtractPropertyCellReferences(entry, PropertyCell::cast(obj)); + } else if (obj.IsPrototypeInfo()) { + ExtractPrototypeInfoReferences(entry, PrototypeInfo::cast(obj)); } else if (obj.IsAllocationSite()) { ExtractAllocationSiteReferences(entry, AllocationSite::cast(obj)); } else if (obj.IsArrayBoilerplateDescription()) { @@ -1110,6 +1125,10 @@ void V8HeapExplorer::ExtractReferences(HeapEntry* entry, HeapObject obj) { ExtractFeedbackVectorReferences(entry, FeedbackVector::cast(obj)); } else if (obj.IsDescriptorArray()) { ExtractDescriptorArrayReferences(entry, DescriptorArray::cast(obj)); + } else if (obj.IsEnumCache()) { + ExtractEnumCacheReferences(entry, EnumCache::cast(obj)); + } else if (obj.IsTransitionArray()) { + ExtractTransitionArrayReferences(entry, TransitionArray::cast(obj)); } else if (obj.IsWeakFixedArray()) { ExtractWeakArrayReferences(WeakFixedArray::kHeaderSize, entry, WeakFixedArray::cast(obj)); @@ -1412,6 +1431,8 @@ void V8HeapExplorer::ExtractMapReferences(HeapEntry* entry, Map map) { TagObject(map.dependent_code(), "(dependent code)"); SetInternalReference(entry, "dependent_code", map.dependent_code(), Map::kDependentCodeOffset); + TagObject(map.prototype_validity_cell(), "(prototype validity cell)", + HeapEntry::kObjectShape); } void V8HeapExplorer::ExtractSharedFunctionInfoReferences( @@ -1556,6 +1577,14 @@ void V8HeapExplorer::ExtractPropertyCellReferences(HeapEntry* entry, PropertyCell::kDependentCodeOffset); } +void V8HeapExplorer::ExtractPrototypeInfoReferences(HeapEntry* entry, + PrototypeInfo info) { + TagObject(info.prototype_chain_enum_cache(), "(prototype chain enum cache)", + HeapEntry::kObjectShape); + TagObject(info.prototype_users(), "(prototype users)", + HeapEntry::kObjectShape); +} + void V8HeapExplorer::ExtractAllocationSiteReferences(HeapEntry* entry, AllocationSite site) { SetInternalReference(entry, "transition_info", @@ -1723,6 +1752,20 @@ void V8HeapExplorer::ExtractDescriptorArrayReferences(HeapEntry* entry, } } +void V8HeapExplorer::ExtractEnumCacheReferences(HeapEntry* entry, + EnumCache cache) { + TagObject(cache.keys(), "(enum cache)", HeapEntry::kObjectShape); + TagObject(cache.indices(), "(enum cache)", HeapEntry::kObjectShape); +} + +void V8HeapExplorer::ExtractTransitionArrayReferences( + HeapEntry* entry, TransitionArray transitions) { + if (transitions.HasPrototypeTransitions()) { + TagObject(transitions.GetPrototypeTransitions(), "(prototype transitions)", + HeapEntry::kObjectShape); + } +} + template void V8HeapExplorer::ExtractWeakArrayReferences(int header_size, HeapEntry* entry, T array) { @@ -3015,7 +3058,8 @@ void HeapSnapshotJSONSerializer::SerializeSnapshot() { JSON_S("concatenated string") "," JSON_S("sliced string") "," JSON_S("symbol") "," - JSON_S("bigint")) "," + JSON_S("bigint") "," + JSON_S("object shape")) "," JSON_S("string") "," JSON_S("number") "," JSON_S("number") "," diff --git a/src/profiler/heap-snapshot-generator.h b/src/profiler/heap-snapshot-generator.h index 56e64944e5..9ec6353706 100644 --- a/src/profiler/heap-snapshot-generator.h +++ b/src/profiler/heap-snapshot-generator.h @@ -118,7 +118,8 @@ class HeapEntry { kConsString = v8::HeapGraphNode::kConsString, kSlicedString = v8::HeapGraphNode::kSlicedString, kSymbol = v8::HeapGraphNode::kSymbol, - kBigInt = v8::HeapGraphNode::kBigInt + kBigInt = v8::HeapGraphNode::kBigInt, + kObjectShape = v8::HeapGraphNode::kObjectShape, }; HeapEntry(HeapSnapshot* snapshot, int index, Type type, const char* name, @@ -431,6 +432,7 @@ class V8_EXPORT_PRIVATE V8HeapExplorer : public HeapEntriesAllocator { void ExtractFeedbackCellReferences(HeapEntry* entry, FeedbackCell feedback_cell); void ExtractPropertyCellReferences(HeapEntry* entry, PropertyCell cell); + void ExtractPrototypeInfoReferences(HeapEntry* entry, PrototypeInfo info); void ExtractAllocationSiteReferences(HeapEntry* entry, AllocationSite site); void ExtractArrayBoilerplateDescriptionReferences( HeapEntry* entry, ArrayBoilerplateDescription value); @@ -448,6 +450,9 @@ class V8_EXPORT_PRIVATE V8HeapExplorer : public HeapEntriesAllocator { FeedbackVector feedback_vector); void ExtractDescriptorArrayReferences(HeapEntry* entry, DescriptorArray array); + void ExtractEnumCacheReferences(HeapEntry* entry, EnumCache cache); + void ExtractTransitionArrayReferences(HeapEntry* entry, + TransitionArray transitions); template void ExtractWeakArrayReferences(int header_size, HeapEntry* entry, T array); void ExtractPropertyReferences(JSObject js_obj, HeapEntry* entry);