WebKit Bugzilla
Attachment 362394 Details for
Bug 172848
: Web Inspector: Improve ES6 Class instances in Heap Snapshot instances view
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
[PATCH] Proposed Fix
heap4.patch (text/plain), 47.04 KB, created by
Joseph Pecoraro
on 2019-02-19 10:54:05 PST
(
hide
)
Description:
[PATCH] Proposed Fix
Filename:
MIME Type:
Creator:
Joseph Pecoraro
Created:
2019-02-19 10:54:05 PST
Size:
47.04 KB
patch
obsolete
>diff --git a/JSTests/ChangeLog b/JSTests/ChangeLog >index b07397972dd..cea120af3f7 100644 >--- a/JSTests/ChangeLog >+++ b/JSTests/ChangeLog >@@ -1,3 +1,28 @@ >+2019-02-15 Joseph Pecoraro <pecoraro@apple.com> >+ >+ Web Inspector: Improve ES6 Class instances in Heap Snapshot instances view >+ https://bugs.webkit.org/show_bug.cgi?id=172848 >+ <rdar://problem/25709212> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * typeProfiler/inheritance.js: >+ Rewrite the test slightly for clarity. The hoisting was confusing. >+ >+ * heapProfiler/class-names.js: Added. >+ (MyES5Class): >+ (MyES6Class): >+ (MyES6Subclass): >+ Test object types and improved class names. >+ >+ * heapProfiler/driver/driver.js: >+ (CheapHeapSnapshotNode): >+ (CheapHeapSnapshot): >+ (createCheapHeapSnapshot): >+ (HeapSnapshot): >+ (createHeapSnapshot): >+ Update snapshot parsing from version 1 to version 2. >+ > 2019-02-18 Dominik Infuehr <dinfuehr@igalia.com> > > [ARM] Test gardening: Test running out of executable memory >diff --git a/JSTests/heapProfiler/basic-nodes.js b/JSTests/heapProfiler/basic-nodes.js >index c6339366687..6acd29f1c5e 100644 >--- a/JSTests/heapProfiler/basic-nodes.js >+++ b/JSTests/heapProfiler/basic-nodes.js >@@ -1,3 +1,5 @@ >+var SimpleObject = $vm.SimpleObject; >+ > load("./driver/driver.js"); > > function hasDifferentSizeNodes(nodes) { >diff --git a/JSTests/heapProfiler/class-names.js b/JSTests/heapProfiler/class-names.js >new file mode 100644 >index 00000000000..6ffd7619522 >--- /dev/null >+++ b/JSTests/heapProfiler/class-names.js >@@ -0,0 +1,47 @@ >+load("./driver/driver.js"); >+ >+function MyES5ClassUgly() {}; >+MyES5ClassUgly.displayName = "MyES5ClassDisplayName"; >+MyES5ClassUgly.prototype = { constructor: MyES5ClassUgly }; >+ >+class MyES6Class {}; >+class MyES6Subclass extends MyES6Class {}; >+ >+let classInstances = []; >+for (let i = 0; i < 5; ++i) >+ classInstances.push(new MyES5ClassUgly); >+for (let i = 0; i < 10; ++i) >+ classInstances.push(new MyES6Class); >+for (let i = 0; i < 20; ++i) >+ classInstances.push(new MyES6Subclass); >+ >+let myFunction = function() {}; >+let myMap = new Map; >+ >+(function() { >+ let nodes; >+ let snapshot = createCheapHeapSnapshot(); >+ >+ nodes = snapshot.nodesWithClassName("MyES5ClassDisplayName"); >+ assert(nodes.length === 5, "Snapshot should contain 5 'MyES5ClassDisplayName' (MyES5ClassUgly) instances"); >+ assert(nodes.every((x) => x.isObjectType), "Every MyES5Class instance should have had its ObjectType flag set"); >+ >+ nodes = snapshot.nodesWithClassName("MyES6Class"); >+ assert(nodes.length === 10, "Snapshot should contain 10 'MyES6Class' instances"); >+ assert(nodes.every((x) => x.isObjectType), "Every MyES6Class instance should have had its ObjectType flag set"); >+ >+ nodes = snapshot.nodesWithClassName("MyES6Subclass"); >+ assert(nodes.length === 20, "Snapshot should contain 20 'MyES6Subclass' instances"); >+ assert(nodes.every((x) => x.isObjectType), "Every MyES6Subclass instance should have its ObjectType flag set"); >+ >+ nodes = snapshot.nodesWithClassName("Function"); >+ assert(nodes.length > 0, "Should be at least 1 Function instance"); >+ assert(nodes.every((x) => !x.isObjectType), "No Function instance should have its ObjectType flag set"); >+ >+ nodes = snapshot.nodesWithClassName("Map"); >+ assert(nodes.length > 0, "Should be at least 1 Map instance"); >+ assert(nodes.every((x) => !x.isObjectType), "No Map instance should have its ObjectType flag set"); >+ >+ nodes = snapshot.nodesWithClassName("Object"); >+ assert(nodes.every((x) => x.isObjectType), "Every Object should also have its ObjectType flag set"); >+})(); >diff --git a/JSTests/heapProfiler/driver/driver.js b/JSTests/heapProfiler/driver/driver.js >index 58ee5a80f4f..31666cf4055 100644 >--- a/JSTests/heapProfiler/driver/driver.js >+++ b/JSTests/heapProfiler/driver/driver.js >@@ -9,15 +9,19 @@ function assert(condition, reason) { > // Contains two large lists of all node data and all edge data. > // Lazily creates node and edge objects off of indexes into these lists. > >-// [<0:id>, <1:size>, <2:classNameTableIndex>, <3:internal>, <4:firstEdgeIndex>]; >+// [<0:id>, <1:size>, <2:classNameTableIndex>, <3:flags>, <4:firstEdgeIndex>]; > const nodeFieldCount = 5; > const nodeIdOffset = 0; > const nodeSizeOffset = 1; > const nodeClassNameOffset = 2; >-const nodeInternalOffset = 3; >+const nodeFlagsOffset = 3; > const nodeFirstEdgeOffset = 4; > const nodeNoEdgeValue = 0xffffffff; // UINT_MAX > >+// Node Flags. >+const internalFlagMask = (1 << 0); >+const objectTypeMask = (1 << 1); >+ > // [<0:fromId>, <1:toId>, <2:typeTableIndex>, <3:edgeDataIndexOrEdgeNameIndex>] > const edgeFieldCount = 4; > const edgeFromIdOffset = 0; >@@ -35,7 +39,10 @@ CheapHeapSnapshotNode = class CheapHeapSnapshotNode > this.id = nodes[nodeIndex + nodeIdOffset]; > this.size = nodes[nodeIndex + nodeSizeOffset]; > this.className = snapshot.classNameFromTableIndex(nodes[nodeIndex + nodeClassNameOffset]); >- this.internal = nodes[nodeIndex + nodeInternalOffset] ? true : false; >+ >+ let flags = nodes[nodeIndex + nodeFlagsOffset]; >+ this.internal = flags & internalFlagMask ? true : false; >+ this.isObjectType = flags & objectTypeMask ? true : false; > > this.outgoingEdges = []; > let firstEdgeIndex = nodes[nodeIndex + nodeFirstEdgeOffset]; >@@ -91,7 +98,7 @@ CheapHeapSnapshot = class CheapHeapSnapshot > this._nodes[n++] = nodes[i++]; // id > this._nodes[n++] = nodes[i++]; // size > this._nodes[n++] = nodes[i++]; // classNameTableIndex >- this._nodes[n++] = nodes[i++]; // internal >+ this._nodes[n++] = nodes[i++]; // flags > this._nodes[n++] = nodeNoEdgeValue; > } > >@@ -154,7 +161,7 @@ function createCheapHeapSnapshot() { > let json = generateHeapSnapshot(); > > let {version, nodes, nodeClassNames, edges, edgeTypes} = json; >- assert(version === 1, "Heap Snapshot payload should be version 1"); >+ assert(version === 2, "Heap Snapshot payload should be version 2"); > assert(nodes.length, "Heap Snapshot should have nodes"); > assert(nodeClassNames.length, "Heap Snapshot should have nodeClassNames"); > assert(edges.length, "Heap Snapshot should have edges"); >@@ -210,7 +217,8 @@ HeapSnapshot = class HeapSnapshot > let id = nodes[i++]; > let size = nodes[i++]; > let classNameIndex = nodes[i++]; >- let internal = nodes[i++]; >+ let flags = nodes[i++]; >+ let internal = flags & internalFlagMask ? true : false; > > let node = new HeapSnapshotNode(id, nodeClassNames[classNameIndex], size, internal); > this.nodeMap.set(id, node); >@@ -256,7 +264,7 @@ function createHeapSnapshot() { > let json = generateHeapSnapshot(); > > let {version, nodes, nodeClassNames, edges, edgeTypes} = json; >- assert(version === 1, "Heap Snapshot payload should be version 1"); >+ assert(version === 2, "Heap Snapshot payload should be version 2"); > assert(nodes.length, "Heap Snapshot should have nodes"); > assert(nodeClassNames.length, "Heap Snapshot should have nodeClassNames"); > assert(edges.length, "Heap Snapshot should have edges"); >diff --git a/JSTests/heapProfiler/variable-edge-types.js b/JSTests/heapProfiler/variable-edge-types.js >index 92a675ec79f..730f17cb261 100644 >--- a/JSTests/heapProfiler/variable-edge-types.js >+++ b/JSTests/heapProfiler/variable-edge-types.js >@@ -1,3 +1,5 @@ >+var SimpleObject = $vm.SimpleObject; >+ > load("./driver/driver.js"); > > let globalScopeVariable = "globalScopeVariableValue"; >diff --git a/JSTests/typeProfiler/inheritance.js b/JSTests/typeProfiler/inheritance.js >index 9bc64510f4e..03855b51286 100644 >--- a/JSTests/typeProfiler/inheritance.js >+++ b/JSTests/typeProfiler/inheritance.js >@@ -5,6 +5,10 @@ load("./driver/driver.js"); > function wrapper() > { > >+function A() { }; >+function B() { }; >+function C() { }; >+ > var theA = new A; > var theB = new B; > var theC = new C; >@@ -16,9 +20,8 @@ secondHierarchyTest = Object.create(theC); > > var secondB = Object.create(theB); > >-function A() { }; >-function B() { }; B.prototype.__proto__ = A.prototype; >-function C() { }; C.prototype.__proto__ = A.prototype; >+B.prototype.__proto__ = A.prototype; >+C.prototype.__proto__ = A.prototype; > > } > wrapper(); >diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog >index 6c34ce891b5..ec363f06d21 100644 >--- a/LayoutTests/ChangeLog >+++ b/LayoutTests/ChangeLog >@@ -1,3 +1,15 @@ >+2019-02-15 Joseph Pecoraro <pecoraro@apple.com> >+ >+ Web Inspector: Improve ES6 Class instances in Heap Snapshot instances view >+ https://bugs.webkit.org/show_bug.cgi?id=172848 >+ <rdar://problem/25709212> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * inspector/unit-tests/heap-snapshot-expected.txt: >+ * inspector/unit-tests/heap-snapshot.html: >+ Update for the new node flag. >+ > 2019-02-18 Joseph Pecoraro <pecoraro@apple.com> > > Web Inspector: Better categorize CPU usage per-thread / worker >diff --git a/LayoutTests/inspector/unit-tests/heap-snapshot-expected.txt b/LayoutTests/inspector/unit-tests/heap-snapshot-expected.txt >index 5ef12a88212..af5f7090614 100644 >--- a/LayoutTests/inspector/unit-tests/heap-snapshot-expected.txt >+++ b/LayoutTests/inspector/unit-tests/heap-snapshot-expected.txt >@@ -23,6 +23,7 @@ PASS: Node className should be 'Window'. > PASS: Node identifier should match. > PASS: Node size should match. > PASS: Node internal state should match. >+PASS: Node isObjectType state should match. > PASS: Node gcRoot state should match. > PASS: Node retainedSize should at least be the size. > >diff --git a/LayoutTests/inspector/unit-tests/heap-snapshot.html b/LayoutTests/inspector/unit-tests/heap-snapshot.html >index 60bf28963d4..197c0b52093 100644 >--- a/LayoutTests/inspector/unit-tests/heap-snapshot.html >+++ b/LayoutTests/inspector/unit-tests/heap-snapshot.html >@@ -9,12 +9,13 @@ function test() > > WI.TestHeapSnapshotNode = class TestHeapSnapshotNode > { >- constructor(identifier, className, size, internal) >+ constructor(identifier, className, size, flags) > { > this.id = identifier; > this.className = className; > this.size = size; >- this.internal = internal; >+ this.internal = flags & (1 << 0) ? true : false; >+ this.isObjectType = flags & (1 << 1) ? true : false; > this.gcRoot = false; > this.outgoingEdges = []; > this.incomingEdges = []; >@@ -59,9 +60,9 @@ function test() > let id = nodes[i++]; > let size = nodes[i++]; > let classNameIndex = nodes[i++]; >- let internal = nodes[i++]; >+ let flags = nodes[i++]; > >- let node = new WI.TestHeapSnapshotNode(id, nodeClassNames[classNameIndex], size, !!internal); >+ let node = new WI.TestHeapSnapshotNode(id, nodeClassNames[classNameIndex], size, flags); > nodeMap.set(id, node); > processedNodes.push(node); > } >@@ -127,6 +128,7 @@ function test() > && node1.size === node2.size > && node1.className === node2.className > && node1.internal === node2.internal >+ && node1.isObjectType === node2.isObjectType > && node1.gcRoot === node2.gcRoot; > } > >@@ -189,6 +191,7 @@ function test() > InspectorTest.expectThat(heapSnapshotNode.id === testSnapshotNodeForWindowObject.id, "Node identifier should match.") > InspectorTest.expectThat(heapSnapshotNode.size === testSnapshotNodeForWindowObject.size, "Node size should match."); > InspectorTest.expectThat(heapSnapshotNode.internal === testSnapshotNodeForWindowObject.internal, "Node internal state should match."); >+ InspectorTest.expectThat(heapSnapshotNode.isObjectType === testSnapshotNodeForWindowObject.isObjectType, "Node isObjectType state should match."); > InspectorTest.expectThat(heapSnapshotNode.gcRoot === testSnapshotNodeForWindowObject.gcRoot, "Node gcRoot state should match."); > InspectorTest.expectThat(heapSnapshotNode.retainedSize >= heapSnapshotNode.size, "Node retainedSize should at least be the size."); > resolve(); >diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog >index 74f02a18ecc..1c6920e92c0 100644 >--- a/Source/JavaScriptCore/ChangeLog >+++ b/Source/JavaScriptCore/ChangeLog >@@ -1,3 +1,31 @@ >+2019-02-15 Joseph Pecoraro <pecoraro@apple.com> >+ >+ Web Inspector: Improve ES6 Class instances in Heap Snapshot instances view >+ https://bugs.webkit.org/show_bug.cgi?id=172848 >+ <rdar://problem/25709212> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * heap/HeapSnapshotBuilder.h: >+ * heap/HeapSnapshotBuilder.cpp: >+ Update the snapshot version. Change the node's 0 | 1 internal value >+ to be a 32bit bit flag. This is nice in that it is both compatible >+ with the previous snapshot version and the same size. We can use more >+ flags in the future. >+ >+ (JSC::HeapSnapshotBuilder::json): >+ In cases where the classInfo gives us "Object" check for a better >+ class name by checking (o).__proto__.constructor.name. We avoid this >+ check in cases where (o).hasOwnProperty("constructor") which is the >+ case for most Foo.prototype objects. Otherwise this would get the >+ name of the Foo superclass for the Foo.prototype object. >+ >+ * runtime/JSObject.cpp: >+ (JSC::JSObject::calculatedClassName): >+ Handle some possible edge cases that were not handled before, such as >+ a JSObject without a GlobalObject or an object which doesn't >+ have a default getPrototype. Try to make the code a little clearer. >+ > 2019-02-18 Joseph Pecoraro <pecoraro@apple.com> > > Web Inspector: Better categorize CPU usage per-thread / worker >diff --git a/Source/JavaScriptCore/heap/HeapSnapshotBuilder.cpp b/Source/JavaScriptCore/heap/HeapSnapshotBuilder.cpp >index cd91f800f80..bab49539953 100644 >--- a/Source/JavaScriptCore/heap/HeapSnapshotBuilder.cpp >+++ b/Source/JavaScriptCore/heap/HeapSnapshotBuilder.cpp >@@ -1,5 +1,5 @@ > /* >- * Copyright (C) 2016 Apple Inc. All rights reserved. >+ * Copyright (C) 2016-2019 Apple Inc. All rights reserved. > * > * Redistribution and use in source and binary forms, with or without > * modification, are permitted provided that the following conditions >@@ -20,7 +20,7 @@ > * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY > * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT > * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. > */ > > #include "config.h" >@@ -79,7 +79,7 @@ void HeapSnapshotBuilder::buildSnapshot() > void HeapSnapshotBuilder::appendNode(JSCell* cell) > { > ASSERT(m_profiler.activeSnapshotBuilder() == this); >- >+ > ASSERT(Heap::isMarked(cell)); > > NodeIdentifier identifier; >@@ -177,12 +177,12 @@ bool HeapSnapshotBuilder::previousSnapshotHasNodeForCell(JSCell* cell, NodeIdent > // Inspector snapshots: > // > // { >-// "version": 1.0, >+// "version": 2, > // "type": "Inspector", >-// // [<address>, <labelIndex>, <wrappedEddress>] only present in GCDebuggingSnapshot-type snapshots >+// // [<address>, <labelIndex>, <wrappedAddress>] only present in GCDebuggingSnapshot-type snapshots > // "nodes": [ >-// <nodeId>, <sizeInBytes>, <nodeClassNameIndex>, <internal> >-// <nodeId>, <sizeInBytes>, <nodeClassNameIndex>, <internal> >+// <nodeId>, <sizeInBytes>, <nodeClassNameIndex>, <flags> >+// <nodeId>, <sizeInBytes>, <nodeClassNameIndex>, <flags> > // ... > // ], > // "nodeClassNames": [ >@@ -204,11 +204,11 @@ bool HeapSnapshotBuilder::previousSnapshotHasNodeForCell(JSCell* cell, NodeIdent > // GC heap debugger snapshots: > // > // { >-// "version": 1.0, >+// "version": 2, > // "type": "GCDebugging", > // "nodes": [ >-// <nodeId>, <sizeInBytes>, <nodeClassNameIndex>, <internal>, <labelIndex>, <cellEddress>, <wrappedEddress>, >-// <nodeId>, <sizeInBytes>, <nodeClassNameIndex>, <internal>, <labelIndex>, <cellEddress>, <wrappedEddress>, >+// <nodeId>, <sizeInBytes>, <nodeClassNameIndex>, <flags>, <labelIndex>, <cellEddress>, <wrappedAddress>, >+// <nodeId>, <sizeInBytes>, <nodeClassNameIndex>, <flags>, <labelIndex>, <cellEddress>, <wrappedAddress>, > // ... > // ], > // "nodeClassNames": [ >@@ -240,8 +240,10 @@ bool HeapSnapshotBuilder::previousSnapshotHasNodeForCell(JSCell* cell, NodeIdent > // <nodeClassNameIndex> > // - index into the "nodeClassNames" list. > // >-// <internal> >-// - 0 = false, 1 = true. >+// <flags> >+// - 0b0000 - no flags >+// - 0b0001 - internal instance >+// - 0b0010 - Object subclassification > // > // <edgeTypeIndex> > // - index into the "edgeTypes" list. >@@ -254,6 +256,11 @@ bool HeapSnapshotBuilder::previousSnapshotHasNodeForCell(JSCell* cell, NodeIdent > // <rootReasonIndex> > // - index into the "labels" list. > >+typedef enum { >+ Internal = 1 << 0, >+ ObjectSubtype = 1 << 1, >+} NodeFlags; >+ > static uint8_t edgeTypeToNumber(EdgeType type) > { > return static_cast<uint8_t>(type); >@@ -358,8 +365,8 @@ String HeapSnapshotBuilder::json(Function<bool (const HeapSnapshotNode&)> allowN > HashMap<JSCell*, NodeIdentifier> allowedNodeIdentifiers; > > // Build a list of used class names. >- HashMap<const char*, unsigned> classNameIndexes; >- classNameIndexes.set("<root>", 0); >+ HashMap<String, unsigned> classNameIndexes; >+ classNameIndexes.set("<root>"_s, 0); > unsigned nextClassNameIndex = 1; > > // Build a list of labels (this is just a string table). >@@ -378,19 +385,37 @@ String HeapSnapshotBuilder::json(Function<bool (const HeapSnapshotNode&)> allowN > if (!allowNodeCallback(node)) > return; > >+ unsigned flags = 0; >+ > allowedNodeIdentifiers.set(node.cell, node.identifier); > >- auto result = classNameIndexes.add(node.cell->classInfo(vm)->className, nextClassNameIndex); >+ String className = node.cell->classInfo(vm)->className; >+ if (node.cell->isObject() && className == JSObject::info()->className) { >+ flags |= ObjectSubtype; >+ >+ // Skip calculating a class name if this object has a `constructor` own property. >+ // These cases are typically F.prototype objects and we want to treat these as >+ // "Object" in snapshots and not get the name of the prototype's parent. >+ JSObject* object = asObject(node.cell); >+ if (JSGlobalObject* globalObject = object->globalObject(vm)) { >+ ExecState* exec = globalObject->globalExec(); >+ PropertySlot slot(object, PropertySlot::InternalMethodType::VMInquiry); >+ if (!object->getOwnPropertySlot(object, exec, vm.propertyNames->constructor, slot)) >+ className = JSObject::calculatedClassName(object); >+ } >+ } >+ >+ auto result = classNameIndexes.add(className, nextClassNameIndex); > if (result.isNewEntry) > nextClassNameIndex++; > unsigned classNameIndex = result.iterator->value; > >- bool isInternal = false; > void* wrappedAddress = 0; > unsigned labelIndex = 0; > if (!node.cell->isString()) { > Structure* structure = node.cell->structure(vm); >- isInternal = !structure || !structure->globalObject(); >+ if (!structure || !structure->globalObject()) >+ flags |= Internal; > > if (m_snapshotType == SnapshotType::GCDebuggingSnapshot) { > String nodeLabel; >@@ -404,7 +429,7 @@ String HeapSnapshotBuilder::json(Function<bool (const HeapSnapshotNode&)> allowN > nodeLabel = function->calculatedDisplayName(vm); > } > } >- >+ > String description = descriptionForCell(node.cell); > if (description.length()) { > if (nodeLabel.length()) >@@ -418,12 +443,12 @@ String HeapSnapshotBuilder::json(Function<bool (const HeapSnapshotNode&)> allowN > nextLabelIndex++; > labelIndex = result.iterator->value; > } >- >+ > wrappedAddress = m_wrappedObjectPointers.get(node.cell); > } > } > >- // <nodeId>, <sizeInBytes>, <nodeClassNameIndex>, <internal>, [<labelIndex>, <cellEddress>, <wrappedEddress>] >+ // <nodeId>, <sizeInBytes>, <nodeClassNameIndex>, <flags>, [<labelIndex>, <cellEddress>, <wrappedAddress>] > json.append(','); > json.appendNumber(node.identifier); > json.append(','); >@@ -431,7 +456,7 @@ String HeapSnapshotBuilder::json(Function<bool (const HeapSnapshotNode&)> allowN > json.append(','); > json.appendNumber(classNameIndex); > json.append(','); >- json.append(isInternal ? '1' : '0'); >+ json.appendNumber(flags); > if (m_snapshotType == SnapshotType::GCDebuggingSnapshot) { > json.append(','); > json.appendNumber(labelIndex); >@@ -479,7 +504,7 @@ String HeapSnapshotBuilder::json(Function<bool (const HeapSnapshotNode&)> allowN > json.append('{'); > > // version >- json.appendLiteral("\"version\":1"); >+ json.appendLiteral("\"version\":2"); > > // type > json.append(','); >@@ -506,7 +531,7 @@ String HeapSnapshotBuilder::json(Function<bool (const HeapSnapshotNode&)> allowN > json.append(','); > json.appendLiteral("\"nodeClassNames\":"); > json.append('['); >- Vector<const char *> orderedClassNames(classNameIndexes.size()); >+ Vector<String> orderedClassNames(classNameIndexes.size()); > for (auto& entry : classNameIndexes) > orderedClassNames[entry.value] = entry.key; > classNameIndexes.clear(); >@@ -603,7 +628,7 @@ String HeapSnapshotBuilder::json(Function<bool (const HeapSnapshotNode&)> allowN > json.append(','); > json.appendLiteral("\"roots\":"); > json.append('['); >- >+ > HeapSnapshot* snapshot = m_profiler.mostRecentSnapshot(); > > bool firstNode = true; >@@ -619,7 +644,7 @@ String HeapSnapshotBuilder::json(Function<bool (const HeapSnapshotNode&)> allowN > > firstNode = false; > json.appendNumber(snapshotNode.value().identifier); >- >+ > // Maybe we should just always encode the root names. > const char* rootName = rootTypeToString(it.value.markReason); > auto result = labelIndexes.add(rootName, nextLabelIndex); >diff --git a/Source/JavaScriptCore/runtime/JSObject.cpp b/Source/JavaScriptCore/runtime/JSObject.cpp >index 1b5c9173a0c..726f876082d 100644 >--- a/Source/JavaScriptCore/runtime/JSObject.cpp >+++ b/Source/JavaScriptCore/runtime/JSObject.cpp >@@ -524,35 +524,59 @@ String JSObject::toStringName(const JSObject* object, ExecState* exec) > > String JSObject::calculatedClassName(JSObject* object) > { >- String prototypeFunctionName; >- auto globalObject = object->globalObject(); >+ String constructorFunctionName; >+ auto structure = object->structure(); >+ auto globalObject = structure->globalObject(); > VM& vm = globalObject->vm(); > auto scope = DECLARE_CATCH_SCOPE(vm); >+ auto exec = globalObject->globalExec(); > >- ExecState* exec = globalObject->globalExec(); >- PropertySlot slot(object->getPrototypeDirect(vm), PropertySlot::InternalMethodType::VMInquiry); >- PropertyName constructor(vm.propertyNames->constructor); >- if (object->getPropertySlot(exec, constructor, slot)) { >- EXCEPTION_ASSERT(!scope.exception()); >+ // Check for a display name of obj.constructor. >+ // This is useful to get `Foo` for the `(class Foo).prototype` object. >+ PropertySlot slot(object, PropertySlot::InternalMethodType::VMInquiry); >+ if (object->getOwnPropertySlot(object, exec, vm.propertyNames->constructor, slot)) { > if (slot.isValue()) { >- JSValue constructorValue = slot.getValue(exec, constructor); >- if (constructorValue.isCell()) { >- if (JSCell* constructorCell = constructorValue.asCell()) { >- if (JSObject* ctorObject = constructorCell->getObject()) { >- if (JSFunction* constructorFunction = jsDynamicCast<JSFunction*>(vm, ctorObject)) >- prototypeFunctionName = constructorFunction->calculatedDisplayName(vm); >- else if (InternalFunction* constructorFunction = jsDynamicCast<InternalFunction*>(vm, ctorObject)) >- prototypeFunctionName = constructorFunction->calculatedDisplayName(vm); >+ if (JSObject* ctorObject = jsDynamicCast<JSObject*>(vm, slot.getValue(exec, vm.propertyNames->constructor))) { >+ if (JSFunction* constructorFunction = jsDynamicCast<JSFunction*>(vm, ctorObject)) >+ constructorFunctionName = constructorFunction->calculatedDisplayName(vm); >+ else if (InternalFunction* constructorFunction = jsDynamicCast<InternalFunction*>(vm, ctorObject)) >+ constructorFunctionName = constructorFunction->calculatedDisplayName(vm); >+ } >+ } >+ } >+ >+ EXCEPTION_ASSERT(!scope.exception() || constructorFunctionName.isNull()); >+ if (UNLIKELY(scope.exception())) >+ scope.clearException(); >+ >+ // Get the display name of obj.__proto__.constructor. >+ // This is useful to get `Foo` for a `new Foo` object. >+ if (constructorFunctionName.isNull()) { >+ MethodTable::GetPrototypeFunctionPtr defaultGetPrototype = JSObject::getPrototype; >+ if (LIKELY(structure->classInfo()->methodTable.getPrototype == defaultGetPrototype)) { >+ JSValue protoValue = object->getPrototypeDirect(vm); >+ if (protoValue.isObject()) { >+ JSObject* protoObject = asObject(protoValue); >+ PropertySlot slot(protoValue, PropertySlot::InternalMethodType::VMInquiry); >+ if (protoObject->getPropertySlot(exec, vm.propertyNames->constructor, slot)) { >+ if (slot.isValue()) { >+ if (JSObject* ctorObject = jsDynamicCast<JSObject*>(vm, slot.getValue(exec, vm.propertyNames->constructor))) { >+ if (JSFunction* constructorFunction = jsDynamicCast<JSFunction*>(vm, ctorObject)) >+ constructorFunctionName = constructorFunction->calculatedDisplayName(vm); >+ else if (InternalFunction* constructorFunction = jsDynamicCast<InternalFunction*>(vm, ctorObject)) >+ constructorFunctionName = constructorFunction->calculatedDisplayName(vm); >+ } > } > } > } > } > } >- EXCEPTION_ASSERT(!scope.exception() || prototypeFunctionName.isNull()); >+ >+ EXCEPTION_ASSERT(!scope.exception() || constructorFunctionName.isNull()); > if (UNLIKELY(scope.exception())) > scope.clearException(); > >- if (prototypeFunctionName.isNull() || prototypeFunctionName == "Object") { >+ if (constructorFunctionName.isNull() || constructorFunctionName == "Object") { > String tableClassName = object->methodTable(vm)->className(object, vm); > if (!tableClassName.isNull() && tableClassName != "Object") > return tableClassName; >@@ -561,11 +585,11 @@ String JSObject::calculatedClassName(JSObject* object) > if (!classInfoName.isNull()) > return classInfoName; > >- if (prototypeFunctionName.isNull()) >+ if (constructorFunctionName.isNull()) > return "Object"_s; > } > >- return prototypeFunctionName; >+ return constructorFunctionName; > } > > bool JSObject::getOwnPropertySlotByIndex(JSObject* thisObject, ExecState* exec, unsigned i, PropertySlot& slot) >diff --git a/Source/WebInspectorUI/ChangeLog b/Source/WebInspectorUI/ChangeLog >index 71d3a1f07cf..b9ecc15a734 100644 >--- a/Source/WebInspectorUI/ChangeLog >+++ b/Source/WebInspectorUI/ChangeLog >@@ -1,3 +1,41 @@ >+2019-02-15 Joseph Pecoraro <pecoraro@apple.com> >+ >+ Web Inspector: Improve ES6 Class instances in Heap Snapshot instances view >+ https://bugs.webkit.org/show_bug.cgi?id=172848 >+ <rdar://problem/25709212> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * UserInterface/Workers/HeapSnapshot/HeapSnapshot.js: >+ (HeapSnapshot): >+ Support the new snapshot version. The only thing that changes are the >+ node flags, and its actually completely compatible with version 1. >+ >+ (HeapSnapshot.updateCategoriesAndMetadata): >+ List the count of object type instances in each class category. >+ >+ (HeapSnapshot.prototype.serializeNode): >+ Include whether or not the node is an object type. >+ >+ * UserInterface/Proxies/HeapSnapshotNodeProxy.js: >+ (WebInspector.HeapSnapshotNodeProxy): >+ (WebInspector.HeapSnapshotNodeProxy.deserialize): >+ Add a new Node isObjectType property based on the new data. >+ >+ * UserInterface/Views/HeapSnapshotClassDataGridNode.js: >+ (WebInspector.HeapSnapshotClassDataGridNode.prototype.createCellContent): >+ * UserInterface/Views/HeapSnapshotClusterContentView.js: >+ (WebInspector.HeapSnapshotClusterContentView.iconStyleClassNameForClassName): >+ If a class contains 50% or more object type instances then treat it as such >+ instead of defaulting to native. >+ >+ * UserInterface/Views/HeapSnapshotDataGridTree.js: >+ (WebInspector.HeapSnapshotInstancesDataGridTree.prototype.populateTopLevel): >+ * UserInterface/Views/HeapSnapshotInstanceDataGridNode.js: >+ (WebInspector.HeapSnapshotInstanceDataGridNode.prototype.createCellContent): >+ We can be more specific than the default if the individual instance is >+ known to be an object type. >+ > 2019-02-18 Joseph Pecoraro <pecoraro@apple.com> > > Web Inspector: CPU Usage Timeline - Thread Breakdown >diff --git a/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotNodeProxy.js b/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotNodeProxy.js >index 6e50452c950..d44408a96e6 100644 >--- a/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotNodeProxy.js >+++ b/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotNodeProxy.js >@@ -25,15 +25,16 @@ > > WI.HeapSnapshotNodeProxy = class HeapSnapshotNodeProxy > { >- constructor(snapshotObjectId, identifier, className, size, retainedSize, internal, gcRoot, dead, dominatorNodeIdentifier, hasChildren) >+ constructor(snapshotObjectId, {id, className, size, retainedSize, internal, isObjectType, gcRoot, dead, dominatorNodeIdentifier, hasChildren}) > { > this._proxyObjectId = snapshotObjectId; > >- this.id = identifier; >+ this.id = id; > this.className = className; > this.size = size; > this.retainedSize = retainedSize; > this.internal = internal; >+ this.isObjectType = isObjectType; > this.gcRoot = gcRoot; > this.dead = dead; > this.dominatorNodeIdentifier = dominatorNodeIdentifier; >@@ -44,8 +45,7 @@ WI.HeapSnapshotNodeProxy = class HeapSnapshotNodeProxy > > static deserialize(objectId, serializedNode) > { >- let {id, className, size, retainedSize, internal, gcRoot, dead, dominatorNodeIdentifier, hasChildren} = serializedNode; >- return new WI.HeapSnapshotNodeProxy(objectId, id, className, size, retainedSize, internal, gcRoot, dead, dominatorNodeIdentifier, hasChildren); >+ return new WI.HeapSnapshotNodeProxy(objectId, serializedNode); > } > > // Proxied >diff --git a/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotClassDataGridNode.js b/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotClassDataGridNode.js >index 369fb00edd5..5791ac1722d 100644 >--- a/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotClassDataGridNode.js >+++ b/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotClassDataGridNode.js >@@ -60,10 +60,11 @@ WI.HeapSnapshotClassDataGridNode = class HeapSnapshotClassDataGridNode extends W > return Number.bytesToString(this._data.size); > > if (columnIdentifier === "className") { >- let {className} = this._data; >+ const internal = false; >+ let {className, isObjectSubcategory} = this._data; > let fragment = document.createDocumentFragment(); > let iconElement = fragment.appendChild(document.createElement("img")); >- iconElement.classList.add("icon", WI.HeapSnapshotClusterContentView.iconStyleClassNameForClassName(className)); >+ iconElement.classList.add("icon", WI.HeapSnapshotClusterContentView.iconStyleClassNameForClassName(className, internal, isObjectSubcategory)); > let nameElement = fragment.appendChild(document.createElement("span")); > nameElement.classList.add("class-name"); > nameElement.textContent = className; >diff --git a/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotClusterContentView.js b/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotClusterContentView.js >index d2091ad7fe4..1e0d72855d5 100644 >--- a/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotClusterContentView.js >+++ b/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotClusterContentView.js >@@ -58,10 +58,12 @@ WI.HeapSnapshotClusterContentView = class HeapSnapshotClusterContentView extends > > // Static > >- static iconStyleClassNameForClassName(className, internal) >+ static iconStyleClassNameForClassName(className, internal, isObjectType) > { > if (internal) > return "native"; >+ if (isObjectType) >+ return "object"; > > switch (className) { > case "Object": >diff --git a/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotDataGridTree.js b/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotDataGridTree.js >index d1425141976..dbac6a7d806 100644 >--- a/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotDataGridTree.js >+++ b/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotDataGridTree.js >@@ -218,7 +218,9 @@ WI.HeapSnapshotInstancesDataGridTree = class HeapSnapshotInstancesDataGridTree e > // Populate the first level with the different classes. > let skipInternalOnlyObjects = !WI.settings.debugShowInternalObjectsInHeapSnapshot.value; > >- for (let [className, {size, retainedSize, count, internalCount, deadCount}] of this.heapSnapshot.categories) { >+ for (let [className, {size, retainedSize, count, internalCount, deadCount, objectCount}] of this.heapSnapshot.categories) { >+ console.assert(count > 0); >+ > // Possibly skip internal only classes. > if (skipInternalOnlyObjects && count === internalCount) > continue; >@@ -228,7 +230,11 @@ WI.HeapSnapshotInstancesDataGridTree = class HeapSnapshotInstancesDataGridTree e > if (!liveCount) > continue; > >- this.appendChild(new WI.HeapSnapshotClassDataGridNode({className, size, retainedSize, count: liveCount}, this)); >+ // If over half of the objects with this class name are Object sub-types, treat this as an Object category. >+ // This can happen if the page has a JavaScript Class with the same name as a native class. >+ let isObjectSubcategory = (objectCount / count) > 0.5; >+ >+ this.appendChild(new WI.HeapSnapshotClassDataGridNode({className, size, retainedSize, isObjectSubcategory, count: liveCount}, this)); > } > > this.didPopulate(); >diff --git a/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstanceDataGridNode.js b/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstanceDataGridNode.js >index 566f350c017..3248826793c 100644 >--- a/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstanceDataGridNode.js >+++ b/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstanceDataGridNode.js >@@ -146,12 +146,12 @@ WI.HeapSnapshotInstanceDataGridNode = class HeapSnapshotInstanceDataGridNode ext > return Number.bytesToString(this._node.size); > > if (columnIdentifier === "className") { >- let {className, id, internal} = this._node; >+ let {className, id, internal, isObjectType} = this._node; > let containerElement = document.createElement("span"); > containerElement.addEventListener("contextmenu", this._contextMenuHandler.bind(this)); > > let iconElement = containerElement.appendChild(document.createElement("img")); >- iconElement.classList.add("icon", WI.HeapSnapshotClusterContentView.iconStyleClassNameForClassName(className, internal)); >+ iconElement.classList.add("icon", WI.HeapSnapshotClusterContentView.iconStyleClassNameForClassName(className, internal, isObjectType)); > > if (this._edge) { > let nameElement = containerElement.appendChild(document.createElement("span")); >@@ -410,7 +410,7 @@ WI.HeapSnapshotInstanceDataGridNode = class HeapSnapshotInstanceDataGridNode ext > containerElement.classList.add("node"); > > let iconElement = containerElement.appendChild(document.createElement("img")); >- iconElement.classList.add("icon", WI.HeapSnapshotClusterContentView.iconStyleClassNameForClassName(node.className, node.internal)); >+ iconElement.classList.add("icon", WI.HeapSnapshotClusterContentView.iconStyleClassNameForClassName(node.className, node.internal, node.isObjectType)); > > let classNameElement = containerElement.appendChild(document.createElement("span")); > classNameElement.textContent = sanitizeClassName(node.className) + " "; >diff --git a/Source/WebInspectorUI/UserInterface/Workers/HeapSnapshot/HeapSnapshot.js b/Source/WebInspectorUI/UserInterface/Workers/HeapSnapshot/HeapSnapshot.js >index af6fdff9a6e..16b45044d3c 100644 >--- a/Source/WebInspectorUI/UserInterface/Workers/HeapSnapshot/HeapSnapshot.js >+++ b/Source/WebInspectorUI/UserInterface/Workers/HeapSnapshot/HeapSnapshot.js >@@ -30,14 +30,18 @@ > */ > > // nodes >-// [<0:id>, <1:size>, <2:classNameTableIndex>, <3:internal>] >+// [<0:id>, <1:size>, <2:classNameTableIndex>, <3:flags>] > const nodeFieldCount = 4; > const nodeIdOffset = 0; > const nodeSizeOffset = 1; > const nodeClassNameOffset = 2; >-const nodeInternalOffset = 3; >+const nodeFlagsOffset = 3; > const gcDebuggingNodeFieldCount = 7; > >+// node flags >+const internalFlagsMask = (1 << 0); >+const objectTypeMask = (1 << 1); >+ > // edges > // [<0:fromId>, <1:toId>, <2:typeTableIndex>, <3:edgeDataIndexOrEdgeNameIndex>] > const edgeFieldCount = 4; >@@ -51,6 +55,10 @@ const rootNodeIndex = 0; > const rootNodeOrdinal = 0; > const rootNodeIdentifier = 0; > >+// Version Differences: >+// - In Version 1, node[3] now named <flags> was the value 0 or 1 indicating not-internal or internal. >+// - In Version 2, this became a bitmask so multiple flags could be included without modifying the size. >+// > // Terminology: > // - `nodeIndex` is an index into the `nodes` list. > // - `nodeOrdinal` is the order of the node in the `nodes` list. (nodeIndex / nodeFieldCount). >@@ -85,7 +93,7 @@ HeapSnapshot = class HeapSnapshot > snapshotDataString = null; > > let {version, type, nodes, nodeClassNames, edges, edgeTypes, edgeNames} = json; >- console.assert(version === 1, "Expect JavaScriptCore Heap Snapshot version 1"); >+ console.assert(version === 1 || version === 2, "Expect JavaScriptCore Heap Snapshot version 1 or 2"); > console.assert(!type || (type === "Inspector" || type === "GCDebugging"), "Expect an Inspector / GCDebugging Heap Snapshot"); > > this._nodeFieldCount = type === "GCDebugging" ? gcDebuggingNodeFieldCount : nodeFieldCount; >@@ -165,18 +173,20 @@ HeapSnapshot = class HeapSnapshot > let className = nodeClassNamesTable[classNameTableIndex]; > let size = nodes[nodeIndex + nodeSizeOffset]; > let retainedSize = nodeOrdinalToRetainedSizes[nodeOrdinal]; >- let internal = nodes[nodeIndex + nodeInternalOffset] ? true : false; >+ let flags = nodes[nodeIndex + nodeFlagsOffset]; > let dead = nodeOrdinalIsDead[nodeOrdinal] ? true : false; > > let category = categories[className]; > if (!category) >- category = categories[className] = {className, size: 0, retainedSize: 0, count: 0, internalCount: 0, deadCount: 0}; >+ category = categories[className] = {className, size: 0, retainedSize: 0, count: 0, internalCount: 0, deadCount: 0, objectCount: 0}; > > category.size += size; > category.retainedSize += retainedSize; > category.count += 1; >- if (internal) >+ if (flags & internalFlagsMask) > category.internalCount += 1; >+ if (flags & objectTypeMask) >+ category.objectCount += 1; > if (dead) > category.deadCount += 1; > else >@@ -422,6 +432,7 @@ HeapSnapshot = class HeapSnapshot > let nodeOrdinal = nodeIndex / this._nodeFieldCount; > let edgeIndex = this._nodeOrdinalToFirstOutgoingEdge[nodeOrdinal]; > let hasChildren = this._edges[edgeIndex + edgeFromIdOffset] === nodeIdentifier; >+ let nodeFlags = this._nodes[nodeIndex + nodeFlagsOffset]; > > let dominatorNodeOrdinal = this._nodeOrdinalToDominatorNodeOrdinal[nodeOrdinal]; > let dominatorNodeIndex = dominatorNodeOrdinal * this._nodeFieldCount; >@@ -432,7 +443,8 @@ HeapSnapshot = class HeapSnapshot > className: this._nodeClassNamesTable[this._nodes[nodeIndex + nodeClassNameOffset]], > size: this._nodes[nodeIndex + nodeSizeOffset], > retainedSize: this._nodeOrdinalToRetainedSizes[nodeOrdinal], >- internal: this._nodes[nodeIndex + nodeInternalOffset] ? true : false, >+ internal: nodeFlags & internalFlagsMask ? true : false, >+ isObjectType: nodeFlags & objectTypeMask ? true : false, > gcRoot: this._nodeOrdinalIsGCRoot[nodeOrdinal] ? true : false, > dead: this._nodeOrdinalIsDead[nodeOrdinal] ? true : false, > dominatorNodeIdentifier, >@@ -759,7 +771,7 @@ HeapSnapshot = class HeapSnapshot > for (let incomingEdgeIndex = incomingEdgeIndexEnd - 1; incomingEdgeIndex >= incomingEdgeIndexStart; --incomingEdgeIndex) { > let fromNodeOrdinal = this._incomingNodes[incomingEdgeIndex]; > let fromNodeIndex = fromNodeOrdinal * this._nodeFieldCount; >- let fromNodeIsInternal = this._nodes[fromNodeIndex + nodeInternalOffset]; >+ let fromNodeIsInternal = this._nodes[fromNodeIndex + nodeFlagsOffset] & internalFlagsMask; > if (fromNodeIsInternal) > continue; > >diff --git a/Tools/GCHeapInspector/heap-analysis/HeapSnapshot.js b/Tools/GCHeapInspector/heap-analysis/HeapSnapshot.js >index 727598845a6..db2b2b7aaeb 100644 >--- a/Tools/GCHeapInspector/heap-analysis/HeapSnapshot.js >+++ b/Tools/GCHeapInspector/heap-analysis/HeapSnapshot.js >@@ -30,16 +30,20 @@ > */ > > // nodes >-// [<0:id>, <1:size>, <2:classNameTableIndex>, <3:internal>, <4:labelIndex>, <5:address>, <6:wrapped address>] >-let nodeFieldCount = 7; >+// [<0:id>, <1:size>, <2:classNameTableIndex>, <3:flags>, <4:labelIndex>, <5:address>, <6:wrapped address>] >+const nodeFieldCount = 7; > const nodeIdOffset = 0; > const nodeSizeOffset = 1; > const nodeClassNameOffset = 2; >-const nodeInternalOffset = 3; >+const nodeFlagsOffset = 3; > const nodeLabelOffset = 4; > const nodeAddressOffset = 5; > const nodeWrappedAddressOffset = 6; > >+// node flags >+const internalFlagsMask = (1 << 0); >+const objectTypeMask = (1 << 1); >+ > // edges > // [<0:fromId>, <1:toId>, <2:typeTableIndex>, <3:edgeDataIndexOrEdgeNameIndex>] > const edgeFieldCount = 4; >@@ -93,7 +97,7 @@ HeapSnapshot = class HeapSnapshot > snapshotDataString = null; > > let {version, type, nodes, nodeClassNames, edges, edgeTypes, edgeNames, roots, labels} = json; >- console.assert(version === 1, "Expect JavaScriptCore Heap Snapshot version 1"); >+ console.assert(version === 1 || version === 2, "Expect JavaScriptCore Heap Snapshot version 1 or 2"); > console.assert(type === "GCDebugging", "Expect a GCDebugging-type snapshot"); > > this._nodes = nodes; >@@ -199,18 +203,20 @@ HeapSnapshot = class HeapSnapshot > let className = nodeClassNamesTable[classNameTableIndex]; > let size = nodes[nodeIndex + nodeSizeOffset]; > let retainedSize = nodeOrdinalToRetainedSizes[nodeOrdinal]; >- let internal = nodes[nodeIndex + nodeInternalOffset] ? true : false; >+ let flags = nodes[nodeIndex + nodeFlagsOffset]; > let dead = nodeOrdinalIsDead[nodeOrdinal] ? true : false; > > let category = categories[className]; > if (!category) >- category = categories[className] = {className, size: 0, retainedSize: 0, count: 0, internalCount: 0, deadCount: 0}; >+ category = categories[className] = {className, size: 0, retainedSize: 0, count: 0, internalCount: 0, deadCount: 0, objectCount: 0}; > > category.size += size; > category.retainedSize += retainedSize; > category.count += 1; >- if (internal) >+ if (flags & internalFlagsMask) > category.internalCount += 1; >+ if (flags & objectTypeMask) >+ category.objectCount += 1; > if (dead) > category.deadCount += 1; > else >@@ -478,6 +484,7 @@ HeapSnapshot = class HeapSnapshot > let nodeOrdinal = nodeIndex / nodeFieldCount; > let edgeIndex = this._nodeOrdinalToFirstOutgoingEdge[nodeOrdinal]; > let hasChildren = this._edges[edgeIndex + edgeFromIdOffset] === nodeIdentifier; >+ let nodeFlags = this._nodes[nodeIndex + nodeFlagsOffset]; > > let dominatorNodeOrdinal = this._nodeOrdinalToDominatorNodeOrdinal[nodeOrdinal]; > let dominatorNodeIndex = dominatorNodeOrdinal * nodeFieldCount; >@@ -488,7 +495,8 @@ HeapSnapshot = class HeapSnapshot > className: this._nodeClassNamesTable[this._nodes[nodeIndex + nodeClassNameOffset]], > size: this._nodes[nodeIndex + nodeSizeOffset], > retainedSize: this._nodeOrdinalToRetainedSizes[nodeOrdinal], >- internal: this._nodes[nodeIndex + nodeInternalOffset] ? true : false, >+ internal: nodeFlags & internalFlagsMask ? true : false, >+ isObjectType: nodeFlags & objectTypeMask ? true : false, > gcRoot: this._nodeOrdinalIsGCRoot[nodeOrdinal] ? true : false, > markedRoot : this._rootIdentifierToReasons.has(nodeIdentifier), > dead: this._nodeOrdinalIsDead[nodeOrdinal] ? true : false, >@@ -829,9 +837,6 @@ HeapSnapshot = class HeapSnapshot > for (let incomingEdgeIndex = incomingEdgeIndexEnd - 1; incomingEdgeIndex >= incomingEdgeIndexStart; --incomingEdgeIndex) { > let fromNodeOrdinal = this._incomingNodes[incomingEdgeIndex]; > let fromNodeIndex = fromNodeOrdinal * nodeFieldCount; >- // let fromNodeIsInternal = this._nodes[fromNodeIndex + nodeInternalOffset]; >- // if (fromNodeIsInternal) >- // continue; > > let edgeIndex = this._incomingEdges[incomingEdgeIndex]; > currentPath.push({edge: edgeIndex});
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Flags:
mark.lam
:
review+
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 172848
:
311802
|
311803
|
311804
|
311805
|
311883
|
311903
|
311904
|
311905
|
362211
|
362359
| 362394