| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| (async function() { |
| TestRunner.addResult(`This test checks HeapSnapshots module.\n`); |
| await TestRunner.loadModule('heap_profiler_test_runner'); |
| await TestRunner.showPanel('heap_profiler'); |
| |
| function createTestEnvironmentInWorker() { |
| if (!this.TestRunner) |
| TestRunner = {}; |
| |
| if (!this.HeapProfilerTestRunner) |
| HeapProfilerTestRunner = {}; |
| |
| TestRunner.assertEquals = function(expected, found, message) { |
| if (expected === found) |
| return; |
| |
| var error; |
| if (message) |
| error = 'Failure (' + message + '):'; |
| else |
| error = 'Failure:'; |
| throw new Error(error + ' expected <' + expected + '> found <' + found + '>'); |
| }; |
| } |
| |
| function runTestSuiteInWorker() { |
| var testSuite = [ |
| function postOrderIndexBug() { |
| var builder = new HeapProfilerTestRunner.HeapSnapshotBuilder(); |
| var node1 = new HeapProfilerTestRunner.HeapNode('Node1'); |
| var node2 = new HeapProfilerTestRunner.HeapNode('Node2'); |
| builder.rootNode.linkNode(node1, HeapProfilerTestRunner.HeapEdge.Type.internal); |
| builder.rootNode.linkNode(node2, HeapProfilerTestRunner.HeapEdge.Type.internal); |
| node2.linkNode(node1, HeapProfilerTestRunner.HeapEdge.Type.internal); |
| var snapshot = builder.createJSHeapSnapshot(); |
| var postOrderIndexes = snapshot._buildPostOrderIndex().nodeOrdinal2PostOrderIndex; |
| var nodeOrdinals = snapshot._buildPostOrderIndex().postOrderIndex2NodeOrdinal; |
| TestRunner.assertEquals( |
| JSON.stringify(new Uint32Array([2, 0, 1])), JSON.stringify(postOrderIndexes), 'postOrderIndexes'); |
| TestRunner.assertEquals( |
| JSON.stringify(new Uint32Array([1, 2, 0])), JSON.stringify(nodeOrdinals), 'nodeOrdinals'); |
| }, |
| |
| function heapSnapshotNodeSimpleTest() { |
| var snapshot = HeapProfilerTestRunner.createJSHeapSnapshotMockObject(); |
| var nodeRoot = snapshot.createNode(snapshot._rootNodeIndex); |
| TestRunner.assertEquals('', nodeRoot.name(), 'root name'); |
| TestRunner.assertEquals('hidden', nodeRoot.type(), 'root type'); |
| TestRunner.assertEquals(2, nodeRoot.edgesCount(), 'root edges'); |
| var nodeE = snapshot.createNode(15); |
| TestRunner.assertEquals('E', nodeE.name(), 'E name'); |
| TestRunner.assertEquals('object', nodeE.type(), 'E type'); |
| TestRunner.assertEquals(0, nodeE.edgesCount(), 'E edges'); |
| }, |
| |
| function heapSnapshotNodeIteratorTest() { |
| var snapshot = HeapProfilerTestRunner.createJSHeapSnapshotMockObject(); |
| var nodeRoot = snapshot.createNode(snapshot._rootNodeIndex); |
| var iterator = new HeapSnapshotWorker.HeapSnapshotNodeIterator(nodeRoot); |
| var names = []; |
| for (; iterator.hasNext(); iterator.next()) |
| names.push(iterator.item().name()); |
| TestRunner.assertEquals(',A,B,C,D,E', names.join(','), 'node iterator'); |
| }, |
| |
| function heapSnapshotEdgeSimpleTest() { |
| var snapshot = HeapProfilerTestRunner.createJSHeapSnapshotMockObject(); |
| var nodeRoot = snapshot.createNode(snapshot._rootNodeIndex); |
| var edgeIterator = new HeapSnapshotWorker.HeapSnapshotEdgeIterator(nodeRoot); |
| TestRunner.assertEquals(true, edgeIterator.hasNext(), 'has edges'); |
| var edge = edgeIterator.item(); |
| TestRunner.assertEquals('shortcut', edge.type(), 'edge type'); |
| TestRunner.assertEquals('a', edge.name(), 'edge name'); |
| TestRunner.assertEquals('A', edge.node().name(), 'edge node name'); |
| |
| var edgesCount = 0; |
| for (; edgeIterator.hasNext(); edgeIterator.next()) |
| ++edgesCount; |
| TestRunner.assertEquals(nodeRoot.edgesCount(), edgesCount, 'edges count'); |
| }, |
| |
| function heapSnapshotEdgeIteratorTest() { |
| var snapshot = HeapProfilerTestRunner.createJSHeapSnapshotMockObject(); |
| var nodeRoot = snapshot.createNode(snapshot._rootNodeIndex); |
| var names = []; |
| for (var iterator = nodeRoot.edges(); iterator.hasNext(); iterator.next()) |
| names.push(iterator.item().name()); |
| TestRunner.assertEquals('a,b', names.join(','), 'edge iterator'); |
| var nodeE = snapshot.createNode(15); |
| TestRunner.assertEquals(false, nodeE.edges().hasNext(), 'empty edge iterator'); |
| }, |
| |
| function heapSnapshotNodeAndEdgeTest() { |
| var snapshotMock = HeapProfilerTestRunner.createJSHeapSnapshotMockObject(); |
| var nodeRoot = snapshotMock.createNode(snapshotMock._rootNodeIndex); |
| var names = []; |
| |
| function depthFirstTraversal(node) { |
| names.push(node.name()); |
| for (var edges = node.edges(); edges.hasNext(); edges.next()) { |
| names.push(edges.item().name()); |
| depthFirstTraversal(edges.item().node()); |
| } |
| } |
| |
| depthFirstTraversal(nodeRoot); |
| var reference = ',a,A,1,B,bc,C,ce,E,bd,D,ac,C,ce,E,b,B,bc,C,ce,E,bd,D'; |
| TestRunner.assertEquals(reference, names.join(','), 'mock traversal'); |
| |
| // Now check against a real HeapSnapshot instance. |
| names = []; |
| var snapshot = new HeapSnapshotWorker.JSHeapSnapshot( |
| HeapProfilerTestRunner.createHeapSnapshotMock(), new HeapSnapshotWorker.HeapSnapshotProgress()); |
| depthFirstTraversal(snapshot.rootNode()); |
| TestRunner.assertEquals(reference, names.join(','), 'snapshot traversal'); |
| }, |
| |
| function heapSnapshotSimpleTest() { |
| var snapshot = new HeapSnapshotWorker.JSHeapSnapshot( |
| HeapProfilerTestRunner.createHeapSnapshotMock(), new HeapSnapshotWorker.HeapSnapshotProgress()); |
| TestRunner.assertEquals(6, snapshot.nodeCount, 'node count'); |
| TestRunner.assertEquals(20, snapshot.totalSize, 'total size'); |
| }, |
| |
| function heapSnapshotContainmentEdgeIndexesTest() { |
| var snapshot = new HeapSnapshotWorker.JSHeapSnapshot( |
| HeapProfilerTestRunner.createHeapSnapshotMock(), new HeapSnapshotWorker.HeapSnapshotProgress()); |
| var actual = snapshot._firstEdgeIndexes; |
| var expected = [0, 6, 12, 18, 21, 21, 21]; |
| TestRunner.assertEquals(expected.length, actual.length, 'Edge indexes size'); |
| for (var i = 0; i < expected.length; ++i) |
| TestRunner.assertEquals(expected[i], actual[i], 'Edge indexes'); |
| }, |
| |
| function heapSnapshotPostOrderIndexTest() { |
| var snapshot = new HeapSnapshotWorker.JSHeapSnapshot( |
| HeapProfilerTestRunner.createHeapSnapshotMock(), new HeapSnapshotWorker.HeapSnapshotProgress()); |
| var postOrderIndex2NodeOrdinal = snapshot._buildPostOrderIndex().postOrderIndex2NodeOrdinal; |
| var expected = [5, 3, 4, 2, 1, 0]; |
| for (var i = 0; i < expected.length; ++i) |
| TestRunner.assertEquals(expected[i], postOrderIndex2NodeOrdinal[i], 'Post ordered indexes'); |
| }, |
| |
| function heapSnapshotDominatorsTreeTest() { |
| var snapshot = new HeapSnapshotWorker.JSHeapSnapshot( |
| HeapProfilerTestRunner.createHeapSnapshotMock(), new HeapSnapshotWorker.HeapSnapshotProgress()); |
| var result = snapshot._buildPostOrderIndex(); |
| var dominatorsTree = |
| snapshot._buildDominatorTree(result.postOrderIndex2NodeOrdinal, result.nodeOrdinal2PostOrderIndex); |
| var expected = [0, 0, 0, 0, 2, 3]; |
| for (var i = 0; i < expected.length; ++i) |
| TestRunner.assertEquals(expected[i], dominatorsTree[i], 'Dominators Tree'); |
| }, |
| |
| function heapSnapshotLocations() { |
| var snapshot = new HeapSnapshotWorker.JSHeapSnapshot( |
| HeapProfilerTestRunner.createHeapSnapshotMock(), new HeapSnapshotWorker.HeapSnapshotProgress()); |
| const expected = new Map([ |
| [0, new HeapSnapshotModel.Location(1, 2, 3)], |
| [18, new HeapSnapshotModel.Location(2, 3, 4)], |
| ]); |
| |
| expected.forEach((expected_location, index) => { |
| const location = snapshot.getLocation(index); |
| TestRunner.assertEquals(expected_location.scriptId, location.scriptId, 'Locations scriptId'); |
| TestRunner.assertEquals(expected_location.lineNumber, location.lineNumber, 'Locations lineNumber'); |
| TestRunner.assertEquals(expected_location.columnNumber, location.columnNumber, 'Locations columnNumber'); |
| }); |
| }, |
| |
| function heapSnapshotRetainedSizeTest() { |
| var snapshot = new HeapSnapshotWorker.JSHeapSnapshot( |
| HeapProfilerTestRunner.createHeapSnapshotMock(), new HeapSnapshotWorker.HeapSnapshotProgress()); |
| var actualRetainedSizes = new Array(snapshot.nodeCount); |
| for (var nodeOrdinal = 0; nodeOrdinal < snapshot.nodeCount; ++nodeOrdinal) |
| actualRetainedSizes[nodeOrdinal] = snapshot._retainedSizes[nodeOrdinal]; |
| var expectedRetainedSizes = [20, 2, 8, 10, 5, 6]; |
| TestRunner.assertEquals( |
| JSON.stringify(expectedRetainedSizes), JSON.stringify(actualRetainedSizes), 'Retained sizes'); |
| }, |
| |
| function heapSnapshotLargeRetainedSize(next) { |
| var builder = new HeapProfilerTestRunner.HeapSnapshotBuilder(); |
| var node = builder.rootNode; |
| |
| var iterations = 6; |
| var nodeSize = 1000 * 1000 * 1000; |
| for (var i = 0; i < 6; i++) { |
| var newNode = new HeapProfilerTestRunner.HeapNode('Node' + i, nodeSize); |
| node.linkNode(newNode, HeapProfilerTestRunner.HeapEdge.Type.element); |
| node = newNode; |
| } |
| |
| var snapshot = builder.createJSHeapSnapshot(); |
| TestRunner.assertEquals( |
| iterations * nodeSize, snapshot.rootNode().retainedSize(), |
| 'Ensure that root node retained size supports values exceeding 2^32 bytes.'); |
| }, |
| |
| function heapSnapshotDominatedNodesTest() { |
| var snapshot = new HeapSnapshotWorker.JSHeapSnapshot( |
| HeapProfilerTestRunner.createHeapSnapshotMock(), new HeapSnapshotWorker.HeapSnapshotProgress()); |
| |
| var expectedDominatedNodes = [21, 14, 7, 28, 35]; |
| var actualDominatedNodes = snapshot._dominatedNodes; |
| TestRunner.assertEquals(expectedDominatedNodes.length, actualDominatedNodes.length, 'Dominated Nodes length'); |
| for (var i = 0; i < expectedDominatedNodes.length; ++i) |
| TestRunner.assertEquals(expectedDominatedNodes[i], actualDominatedNodes[i], 'Dominated Nodes'); |
| |
| var expectedDominatedNodeIndex = [0, 3, 3, 4, 5, 5, 5]; |
| var actualDominatedNodeIndex = snapshot._firstDominatedNodeIndex; |
| TestRunner.assertEquals( |
| expectedDominatedNodeIndex.length, actualDominatedNodeIndex.length, 'Dominated Nodes Index length'); |
| for (var i = 0; i < expectedDominatedNodeIndex.length; ++i) |
| TestRunner.assertEquals(expectedDominatedNodeIndex[i], actualDominatedNodeIndex[i], 'Dominated Nodes Index'); |
| }, |
| |
| function heapSnapshotPageOwnedTest(next) { |
| var builder = new HeapProfilerTestRunner.HeapSnapshotBuilder(); |
| var rootNode = builder.rootNode; |
| |
| var debuggerNode = new HeapProfilerTestRunner.HeapNode('Debugger'); |
| rootNode.linkNode(debuggerNode, HeapProfilerTestRunner.HeapEdge.Type.element); |
| |
| var windowNode = new HeapProfilerTestRunner.HeapNode('Window'); |
| rootNode.linkNode(windowNode, HeapProfilerTestRunner.HeapEdge.Type.shortcut); |
| |
| var pageOwnedNode = new HeapProfilerTestRunner.HeapNode('PageOwnedNode'); |
| windowNode.linkNode(pageOwnedNode, HeapProfilerTestRunner.HeapEdge.Type.element); |
| debuggerNode.linkNode(pageOwnedNode, HeapProfilerTestRunner.HeapEdge.Type.property, 'debugger2pageOwnedNode'); |
| |
| var debuggerOwnedNode = new HeapProfilerTestRunner.HeapNode('debuggerOwnedNode'); |
| debuggerNode.linkNode(debuggerOwnedNode, HeapProfilerTestRunner.HeapEdge.Type.element); |
| |
| var snapshot = builder.createJSHeapSnapshot(); |
| snapshot._flags = new Array(snapshot.nodeCount); |
| for (var i = 0; i < snapshot.nodeCount; ++i) |
| snapshot._flags[i] = 0; |
| snapshot._markPageOwnedNodes(); |
| |
| var expectedFlags = [0, 0, 4, 4, 0]; |
| TestRunner.assertEquals( |
| JSON.stringify(expectedFlags), JSON.stringify(snapshot._flags), |
| 'We are expecting that only window(third element) and PageOwnedNode(forth element) have flag === 4.'); |
| }, |
| |
| function heapSnapshotRetainersTest() { |
| var snapshot = new HeapSnapshotWorker.JSHeapSnapshot( |
| HeapProfilerTestRunner.createHeapSnapshotMock(), new HeapSnapshotWorker.HeapSnapshotProgress()); |
| var expectedRetainers = {'': [], 'A': [''], 'B': ['', 'A'], 'C': ['A', 'B'], 'D': ['B'], 'E': ['C']}; |
| for (var nodes = snapshot._allNodes(); nodes.hasNext(); nodes.next()) { |
| var names = []; |
| for (var retainers = nodes.item().retainers(); retainers.hasNext(); retainers.next()) |
| names.push(retainers.item().node().name()); |
| names.sort(); |
| TestRunner.assertEquals( |
| expectedRetainers[nodes.item().name()].join(','), names.join(','), |
| 'retainers of "' + nodes.item().name() + '"'); |
| } |
| }, |
| |
| function heapSnapshotAggregatesTest() { |
| var snapshot = new HeapSnapshotWorker.JSHeapSnapshot( |
| HeapProfilerTestRunner.createHeapSnapshotMock(), new HeapSnapshotWorker.HeapSnapshotProgress()); |
| var expectedAggregates = { |
| 'A': {count: 1, self: 2, maxRet: 2, type: 'object', name: 'A'}, |
| 'B': {count: 1, self: 3, maxRet: 8, type: 'object', name: 'B'}, |
| 'C': {count: 1, self: 4, maxRet: 10, type: 'object', name: 'C'}, |
| 'D': {count: 1, self: 5, maxRet: 5, type: 'object', name: 'D'}, |
| 'E': {count: 1, self: 6, maxRet: 6, type: 'object', name: 'E'} |
| }; |
| var aggregates = snapshot.aggregates(false); |
| for (var name in aggregates) { |
| var aggregate = aggregates[name]; |
| var expectedAggregate = expectedAggregates[name]; |
| for (var parameter in expectedAggregate) |
| TestRunner.assertEquals( |
| expectedAggregate[parameter], aggregate[parameter], 'parameter ' + parameter + ' of "' + name + '"'); |
| } |
| var expectedIndexes = { |
| // Index of corresponding node in the raw snapshot: |
| 'A': [7], // 14 |
| 'B': [14], // 27 |
| 'C': [21], // 40 |
| 'D': [28], // 50 |
| 'E': [35] // 57 |
| }; |
| var indexes = snapshot.aggregates(true); |
| for (var name in aggregates) { |
| var aggregate = aggregates[name]; |
| var expectedIndex = expectedIndexes[name]; |
| TestRunner.assertEquals(expectedIndex.join(','), aggregate.idxs.join(','), 'indexes of "' + name + '"'); |
| } |
| }, |
| |
| function heapSnapshotFlagsTest() { |
| var snapshot = new HeapSnapshotWorker.JSHeapSnapshot( |
| HeapProfilerTestRunner.createHeapSnapshotMockWithDOM(), new HeapSnapshotWorker.HeapSnapshotProgress()); |
| var expectedCanBeQueried = { |
| '': false, |
| 'A': true, |
| 'B': true, |
| 'C': true, |
| 'D': true, |
| 'E': false, |
| 'F': false, |
| 'G': false, |
| 'H': false, |
| 'M': false, |
| 'N': false, |
| 'Window': true |
| }; |
| for (var nodes = snapshot._allNodes(); nodes.hasNext(); nodes.next()) { |
| var node = nodes.item(); |
| TestRunner.assertEquals( |
| expectedCanBeQueried[node.name()], node.canBeQueried(), 'canBeQueried of "' + node.name() + '"'); |
| } |
| }, |
| |
| function heapSnapshotNodesProviderTest() { |
| var snapshot = new HeapSnapshotWorker.JSHeapSnapshot( |
| HeapProfilerTestRunner.createHeapSnapshotMock(), new HeapSnapshotWorker.HeapSnapshotProgress()); |
| |
| var allNodeIndexes = []; |
| for (var i = 0; i < snapshot.nodes.length; i += snapshot._nodeFieldCount) |
| allNodeIndexes.push(i); |
| var provider = new HeapSnapshotWorker.HeapSnapshotNodesProvider(snapshot, allNodeIndexes); |
| // Sort by names in reverse order. |
| provider.sortAndRewind({fieldName1: 'name', ascending1: false, fieldName2: 'id', ascending2: false}); |
| var range = provider.serializeItemsRange(0, 6); |
| TestRunner.assertEquals(6, range.totalLength, 'Node range total length'); |
| TestRunner.assertEquals(0, range.startPosition, 'Node range start position'); |
| TestRunner.assertEquals(6, range.endPosition, 'Node range end position'); |
| var names = range.items.map(item => item.name); |
| TestRunner.assertEquals('E,D,C,B,A,', names.join(','), 'nodes provider names'); |
| }, |
| |
| function heapSnapshotEdgesProviderTest() { |
| var snapshot = new HeapSnapshotWorker.JSHeapSnapshot( |
| HeapProfilerTestRunner.createHeapSnapshotMock(), new HeapSnapshotWorker.HeapSnapshotProgress()); |
| |
| function edgeFilter(edge) { |
| return edge.name() === 'b'; |
| } |
| |
| var provider = snapshot.createEdgesProviderForTest(snapshot.rootNodeIndex, edgeFilter); |
| provider.sortAndRewind({fieldName1: '!edgeName', ascending1: false, fieldName2: 'id', ascending2: false}); |
| var range = provider.serializeItemsRange(0, 10); |
| TestRunner.assertEquals(1, range.totalLength, 'Edge range total length'); |
| TestRunner.assertEquals(0, range.startPosition, 'Edge range start position'); |
| TestRunner.assertEquals(1, range.endPosition, 'Edge range end position'); |
| var names = range.items.map(function(item) { |
| return item.name; |
| }); |
| TestRunner.assertEquals('b', names.join(','), 'edges provider names'); |
| }, |
| |
| async function heapSnapshotLoaderTest() { |
| var source = HeapProfilerTestRunner.createHeapSnapshotMockRaw(); |
| var sourceStringified = JSON.stringify(source); |
| var partSize = sourceStringified.length >> 3; |
| |
| var loader = new HeapSnapshotWorker.HeapSnapshotLoader(); |
| for (var i = 0, l = sourceStringified.length; i < l; i += partSize) |
| loader.write(sourceStringified.slice(i, i + partSize)); |
| loader.close(); |
| await 0; // Make sure loader parses the input. |
| var result = loader.buildSnapshot(false); |
| result.nodes = new Uint32Array(result.nodes); |
| result.containmentEdges = new Uint32Array(result.containmentEdges); |
| function assertSnapshotEquals(reference, actual) { |
| TestRunner.assertEquals(JSON.stringify(reference), JSON.stringify(actual)); |
| } |
| assertSnapshotEquals( |
| new HeapSnapshotWorker.JSHeapSnapshot( |
| HeapProfilerTestRunner.createHeapSnapshotMock(), new HeapSnapshotWorker.HeapSnapshotProgress(), false), |
| result); |
| }, |
| ]; |
| |
| var result = []; |
| for (var i = 0; i < testSuite.length; i++) { |
| var test = testSuite[i]; |
| result.push(''); |
| result.push('Running: ' + test.name); |
| try { |
| test(); |
| } catch (e) { |
| result.push('FAIL: ' + e); |
| } |
| } |
| return result.join('\n'); |
| } |
| |
| var proxy = new Profiler.HeapSnapshotWorkerProxy(function(eventName, arg) { |
| TestRunner.addResult('Unexpected event from worker: ' + eventName); |
| }); |
| var source = '(' + createTestEnvironmentInWorker + ')();' + |
| '(' + HeapProfilerTestRunner.createHeapSnapshotMockFactories + ')();' + |
| '(' + runTestSuiteInWorker + ')();'; |
| proxy.evaluateForTest(source, function(result) { |
| TestRunner.addResult(result); |
| TestRunner.completeTest(); |
| }); |
| })(); |