| <!DOCTYPE html> |
| <!-- |
| Copyright 2015 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. |
| --> |
| |
| <link rel="import" href="/tracing/base/iteration_helpers.html"> |
| <link rel="import" href="/tracing/base/unit.html"> |
| <link rel="import" href="/tracing/core/test_utils.html"> |
| <link rel="import" href="/tracing/importer/import.html"> |
| <link rel="import" href="/tracing/model/global_memory_dump.html"> |
| <link rel="import" href="/tracing/model/memory_allocator_dump.html"> |
| <link rel="import" href="/tracing/model/memory_dump_test_utils.html"> |
| <link rel="import" href="/tracing/model/model.html"> |
| <link rel="import" href="/tracing/model/process_memory_dump.html"> |
| <link rel="import" href="/tracing/value/numeric.html"> |
| |
| <script> |
| 'use strict'; |
| |
| tr.b.unittest.testSuite(function() { |
| var Model = tr.Model; |
| var GlobalMemoryDump = tr.model.GlobalMemoryDump; |
| var ProcessMemoryDump = tr.model.ProcessMemoryDump; |
| var MemoryAllocatorDump = tr.model.MemoryAllocatorDump; |
| var MemoryAllocatorDumpLink = tr.model.MemoryAllocatorDumpLink; |
| var ScalarNumeric = tr.v.ScalarNumeric; |
| var sizeInBytes_smallerIsBetter = |
| tr.b.Unit.byName.sizeInBytes_smallerIsBetter; |
| var newAllocatorDump = tr.model.MemoryDumpTestUtils.newAllocatorDump; |
| var addChildDump = tr.model.MemoryDumpTestUtils.addChildDump; |
| var addOwnershipLink = tr.model.MemoryDumpTestUtils.addOwnershipLink; |
| var checkDumpNumericsAndDiagnostics = |
| tr.model.MemoryDumpTestUtils.checkDumpNumericsAndDiagnostics; |
| var SIZE_DELTA = tr.model.MemoryDumpTestUtils.SIZE_DELTA; |
| var MemoryAllocatorDumpInfoType = tr.model.MemoryAllocatorDumpInfoType; |
| var PROVIDED_SIZE_LESS_THAN_AGGREGATED_CHILDREN = |
| MemoryAllocatorDumpInfoType.PROVIDED_SIZE_LESS_THAN_AGGREGATED_CHILDREN; |
| var PROVIDED_SIZE_LESS_THAN_LARGEST_OWNER = |
| MemoryAllocatorDumpInfoType.PROVIDED_SIZE_LESS_THAN_LARGEST_OWNER; |
| |
| function buildArgPusher(array) { |
| return function(arg) { array.push(arg); }; |
| } |
| |
| function assertEqualUniqueMembers(actualArray, expectedArray) { |
| assert.lengthOf(actualArray, expectedArray.length); |
| assert.sameMembers(actualArray, expectedArray); |
| } |
| |
| function assertUndefinedNumeric(dump, numericName) { |
| var numeric = dump.numerics[numericName]; |
| assert.isUndefined(numeric, 'expected numeric \'' + numericName + |
| '\' of memory allocator dump \'' + dump.fullName + '\' in ' + |
| dump.containerMemoryDump.userFriendlyName + ' to be undefined'); |
| } |
| |
| function assertDefinedNumeric(dump, numericName, expectedUnit, expectedValue, |
| opt_delta) { |
| var numeric = dump.numerics[numericName]; |
| var errorMessagePrefix = 'expected numeric \'' + numericName + |
| '\' of memory allocator dump \'' + dump.fullName + '\' in ' + |
| dump.containerMemoryDump.userFriendlyName + ' to '; |
| |
| assert.instanceOf(numeric, ScalarNumeric, |
| errorMessagePrefix + 'be an instance of ScalarNumeric'); |
| assert.equal(numeric.unit, expectedUnit, |
| errorMessagePrefix + 'have unit \'' + expectedUnit.unitName + |
| '\' but got \'' + numeric.unit.unitName + '\' instead'); |
| |
| var valueErrorMessage = errorMessagePrefix + 'have value \'' + |
| expectedValue + '\' but got \'' + numeric.value + '\''; |
| if (opt_delta !== undefined) { |
| assert.closeTo( |
| numeric.value, expectedValue, opt_delta, valueErrorMessage); |
| } else { |
| assert.equal(numeric.value, expectedValue, valueErrorMessage); |
| } |
| } |
| |
| function assertSizeNumeric(dump, sizeName, expectedValue) { |
| if (expectedValue === undefined) { |
| assertUndefinedNumeric(dump, sizeName); |
| } else { |
| assertDefinedNumeric(dump, sizeName, sizeInBytes_smallerIsBetter, |
| expectedValue, SIZE_DELTA); |
| } |
| } |
| |
| function assertDumpSizes(dump, expectedSize, expectedEffectiveSize, |
| opt_expectedInfos, opt_expectedOwnedBySiblingSizes) { |
| // Check the 'size' numeric. |
| assertSizeNumeric(dump, 'size', expectedSize); |
| |
| // Check the 'effective_size' numeric. |
| assertSizeNumeric(dump, 'effective_size', expectedEffectiveSize); |
| |
| // Check the 'infos' list. |
| var expectedInfos = opt_expectedInfos || []; |
| var actualInfos = dump.infos; |
| assert.lengthOf(actualInfos, expectedInfos.length, |
| 'expected memory allocator dump \'' + dump.fullName + '\' in ' + |
| dump.containerMemoryDump.userFriendlyName + ' to have ' + |
| expectedInfos.length + ' infos but got ' + actualInfos.length); |
| for (var k = 0; k < actualInfos.length; k++) { |
| assert.deepEqual(actualInfos[k], expectedInfos[k], |
| 'info ' + k + ' of memory allocator dump \'' + dump.fullName + |
| '\' in ' + dump.containerMemoryDump.userFriendlyName + |
| ' doesn\'t match the expected info'); |
| } |
| |
| // Checked the 'ownedBySiblingSizes' map. |
| var expectedOwnedBySiblingSizes = opt_expectedOwnedBySiblingSizes || {}; |
| var actualOwnedBySiblingSizes = {}; |
| for (var siblingDump of dump.ownedBySiblingSizes.keys()) { |
| assert.strictEqual(siblingDump.parent, dump.parent); |
| actualOwnedBySiblingSizes[siblingDump.name] = |
| dump.ownedBySiblingSizes.get(siblingDump); |
| } |
| assert.deepEqual(actualOwnedBySiblingSizes, expectedOwnedBySiblingSizes, |
| 'ownedBySiblingSizes of memory allocator dump \'' + dump.fullName + |
| '\' in ' + dump.containerMemoryDump.userFriendlyName + |
| ' doesn\'t contain the expected values'); |
| } |
| |
| function createContainerDumps(processMemoryDumpCount, opt_model) { |
| var model = opt_model; |
| if (model === undefined) |
| model = new Model(); |
| |
| var gmd = new GlobalMemoryDump(model, 0); |
| model.globalMemoryDumps.push(gmd); |
| |
| var pmds = []; |
| for (var i = 0; i < processMemoryDumpCount; i++) { |
| var process = model.getOrCreateProcess(i); |
| var pmd = new ProcessMemoryDump(gmd, process, 0); |
| gmd.processMemoryDumps[i] = pmd; |
| process.memoryDumps.push(pmd); |
| pmds.push(pmd); |
| } |
| |
| return [gmd].concat(pmds); |
| } |
| |
| /** |
| * Build container memory dumps from tree recipes. This function returns |
| * a list containing a global memory dump and zero or more process memory |
| * dumps constructed from the provided function argument as follows: |
| * |
| * allTreeRecipes (argument): |
| * |
| * [ |
| * [tree recipe GA, tree recipe GB, ...], |
| * [tree recipe P1A, tree recipe P1B, ...], |
| * [tree recipe P2A, tree recipe P2B ...], |
| * ... |
| * ] |
| * |
| * return value: |
| * |
| * [ |
| * GlobalMemoryDump with root MemoryAllocatorDump(s) [GA, GB, ...], |
| * ProcessMemoryDump with root MemoryAllocatorDump(s) [P1A, P1B, ...], |
| * ProcessMemoryDump with root MemoryAllocatorDump(s) [P2A, P2B, ...], |
| * ... |
| * ] |
| * |
| * where a tree recipe is an object (a recursive data structure) with the |
| * following fields: |
| * |
| * name: Name of the resulting MAD. |
| * guid: GUID of the resulting MAD (can be undefined). |
| * owns: GUID of another MAD owned by the resulting MAD (no owned MAD if |
| * undefined). |
| * importance: Importance of the above ownership (can be undefined). |
| * size: Value of the 'size' numeric of the resulting MAD (no 'size' |
| * numeric if undefined). |
| * numerics: Extra numerics of the resulting MAD (dictionary). |
| * diagnostics: Extra diagnostics of the resulting MAD (dictionary). |
| * weak: Whether the resulting MAD should be weak (undefined implies |
| * non-weak). |
| * children: List of tree recipes for child MADs (no children if undefined). |
| * skip_build: If this optional property is set to true, this function will |
| * skip the corresponding tree recipe node and will not create a MAD |
| * for it (not allowed in root recipes). |
| * |
| * Other fields (most importantly 'expected_size') of a tree recipe are |
| * ignored by this function. |
| */ |
| function buildDumpTrees(allTreeRecipes, opt_model) { |
| assert.isAbove(allTreeRecipes.length, 0); |
| |
| var ownerDumps = {}; // owned GUID -> {dump: owner, importance: optional}. |
| var ownableDumps = {}; // ownable GUID -> ownable dump. |
| |
| function buildAndAddDumpTrees(containerDump, treeRecipes) { |
| if (treeRecipes === undefined) |
| return; |
| |
| function buildDumpTreeRecursively(treeRecipe, namePrefix) { |
| var skipBuild = treeRecipe['skip_build']; |
| var name = treeRecipe['name']; |
| var guid = treeRecipe['guid']; |
| var owns = treeRecipe['owns']; |
| var size = treeRecipe['size']; |
| var numerics = treeRecipe['numerics']; |
| var diagnostics = treeRecipe['diagnostics']; |
| var importance = treeRecipe['importance']; |
| var weak = treeRecipe['weak']; |
| |
| assert.notStrictEqual(skipBuild, true); |
| assert.isDefined(name); |
| |
| var fullName = namePrefix + name; |
| var dump = new MemoryAllocatorDump(containerDump, fullName, guid); |
| |
| if (size !== undefined) { |
| dump.addNumeric( |
| 'size', new ScalarNumeric(sizeInBytes_smallerIsBetter, size)); |
| } |
| if (guid !== undefined) { |
| assert.notProperty(guid, ownableDumps); |
| ownableDumps[guid] = dump; |
| } |
| if (owns !== undefined) { |
| if (!(owns in ownerDumps)) |
| ownerDumps[owns] = []; |
| ownerDumps[owns].push({dump: dump, importance: importance}); |
| } else { |
| assert.isUndefined(importance); // Test sanity check. |
| } |
| |
| if (treeRecipe.children !== undefined) { |
| treeRecipe.children.forEach(function(childTreeRecipe) { |
| // Virtual children are added during size calculation. |
| if (childTreeRecipe['skip_build'] === true) |
| return; |
| var childDump = |
| buildDumpTreeRecursively(childTreeRecipe, fullName + '/'); |
| childDump.parent = dump; |
| dump.children.push(childDump); |
| }); |
| } |
| |
| if (numerics !== undefined) |
| tr.b.iterItems(numerics, dump.addNumeric, dump); |
| if (diagnostics !== undefined) |
| tr.b.iterItems(diagnostics, dump.addDiagnostic, dump); |
| |
| if (weak) |
| dump.weak = true; |
| |
| return dump; |
| } |
| |
| var memoryAllocatorDumps = treeRecipes.map(function(treeRecipe) { |
| return buildDumpTreeRecursively(treeRecipe, ''); |
| }); |
| containerDump.memoryAllocatorDumps = memoryAllocatorDumps; |
| } |
| |
| // Recursively build memory allocator dump trees for all container dumps. |
| var containerDumps = createContainerDumps( |
| allTreeRecipes.length - 1, opt_model); |
| for (var i = 0; i < allTreeRecipes.length; i++) |
| buildAndAddDumpTrees(containerDumps[i], allTreeRecipes[i]); |
| |
| // Hook up ownership links. |
| tr.b.iterItems(ownerDumps, function(ownedGuid, ownershipInfos) { |
| var ownedDump = ownableDumps[ownedGuid]; |
| assert.isDefined(ownedDump, 'Tree recipes don\'t contain a memory ' + |
| 'allocator dump with guid \'' + ownedGuid + '\''); |
| |
| ownershipInfos.forEach(function(ownershipInfo) { |
| addOwnershipLink( |
| ownershipInfo.dump, ownedDump, ownershipInfo.importance); |
| }); |
| }); |
| |
| return containerDumps; |
| } |
| |
| // Check that the buildDumpTrees testing helper method above builds a |
| // hierarchy of container and memory allocator dumps from tree recipes |
| // correctly. |
| test('testSanityCheck_buildDumpTrees', function() { |
| var containerDumps = buildDumpTrees([ |
| [ // GMD. |
| { |
| 'name': 'globalSharedDump1', |
| 'size': 123 |
| }, |
| { |
| 'name': 'globalSharedDump2', |
| 'subsystem_size': 999, |
| 'owns': 7, |
| 'importance': -1 |
| } |
| ], |
| undefined, // PMD1. |
| [ // PMD2. |
| { |
| 'name': 'v8', |
| 'children': [ |
| { |
| 'name': 'isolate1', |
| 'guid': 7, |
| 'weak': true |
| }, |
| { |
| 'name': 'isolate2', |
| 'skip_build': true |
| }, |
| { |
| 'name': 'isolate3', |
| 'size': 54, |
| 'guid': 60, |
| 'children': [ |
| { |
| 'name': 'obj1', |
| 'size': 89, |
| 'guid': 3 |
| }, |
| { |
| 'name': 'obj2', |
| 'owns': 3, |
| 'weak': true |
| }, |
| { |
| 'name': 'obj3', |
| 'owns': 3, |
| 'importance': 2 |
| } |
| ] |
| } |
| ] |
| } |
| ] |
| ]); |
| assert.lengthOf(containerDumps, 3); |
| var gmd = containerDumps[0]; |
| var pmd1 = containerDumps[1]; |
| var pmd2 = containerDumps[2]; |
| |
| function checkDump(dump, expectedGuid, expectedFullName, expectedParent, |
| expectedChildrenCount, expectedSize, expectedIsOwner, |
| expectedOwnersCount, expectedContainerDump, opt_expectedWeak) { |
| assert.isDefined(dump); |
| assert.instanceOf(dump, MemoryAllocatorDump); |
| assert.strictEqual(dump.guid, expectedGuid); |
| assert.strictEqual(dump.fullName, expectedFullName); |
| assert.strictEqual(dump.parent, expectedParent); |
| assert.lengthOf(dump.children, expectedChildrenCount); |
| |
| assertSizeNumeric(dump, 'size', expectedSize); |
| assertSizeNumeric(dump, 'subsystem_size', undefined); |
| |
| if (expectedIsOwner) |
| assert.isDefined(dump.owns); |
| else |
| assert.isUndefined(dump.owns); |
| assert.lengthOf(dump.ownedBy, expectedOwnersCount); |
| |
| assert.strictEqual(dump.containerMemoryDump, expectedContainerDump); |
| assert.strictEqual(expectedContainerDump.getMemoryAllocatorDumpByFullName( |
| expectedFullName), dump); |
| assert.strictEqual(dump.weak, !!opt_expectedWeak); |
| } |
| |
| function checkOwnershipLink(expectedSourceDump, expectedTargetDump, |
| expectedImportance) { |
| var link = expectedSourceDump.owns; |
| assert.isDefined(link); |
| assert.instanceOf(link, MemoryAllocatorDumpLink); |
| assert.strictEqual(link.source, expectedSourceDump); |
| assert.strictEqual(link.target, expectedTargetDump); |
| assert.strictEqual(link.importance, expectedImportance); |
| assert.include(expectedTargetDump.ownedBy, link); |
| } |
| |
| // GMD memory allocator dumps. |
| assert.lengthOf(gmd.memoryAllocatorDumps, 2); |
| var globalSharedDump1 = gmd.memoryAllocatorDumps[0]; |
| checkDump(globalSharedDump1, undefined, 'globalSharedDump1', undefined, 0, |
| 123, false, 0, gmd); |
| var globalSharedDump2 = gmd.memoryAllocatorDumps[1]; |
| checkDump(globalSharedDump2, undefined, 'globalSharedDump2', undefined, 0, |
| undefined, true, 0, gmd); |
| |
| // PMD1 memory allocator dumps. |
| assert.isUndefined(pmd1.memoryAllocatorDumps); |
| |
| // PMD2 memory allocator dumps. |
| assert.lengthOf(pmd2.memoryAllocatorDumps, 1); |
| var v8Dump = pmd2.memoryAllocatorDumps[0]; |
| checkDump(v8Dump, undefined, 'v8', undefined, 2, undefined, false, 0, |
| pmd2); |
| var isolate1Dump = v8Dump.children[0]; |
| checkDump(isolate1Dump, 7, 'v8/isolate1', v8Dump, 0, undefined, false, 1, |
| pmd2, true /* weak dump */); |
| var isolate3Dump = v8Dump.children[1]; |
| checkDump(isolate3Dump, 60, 'v8/isolate3', v8Dump, 3, 54, false, 0, pmd2); |
| var obj1Dump = isolate3Dump.children[0]; |
| checkDump(obj1Dump, 3, 'v8/isolate3/obj1', isolate3Dump, 0, 89, false, 2, |
| pmd2); |
| var obj2Dump = isolate3Dump.children[1]; |
| checkDump(obj2Dump, undefined, 'v8/isolate3/obj2', isolate3Dump, 0, |
| undefined, true, 0, pmd2, true /* weak dump */); |
| var obj3Dump = isolate3Dump.children[2]; |
| checkDump(obj3Dump, undefined, 'v8/isolate3/obj3', isolate3Dump, 0, |
| undefined, true, 0, pmd2); |
| |
| // Ownership links. |
| checkOwnershipLink(globalSharedDump2, isolate1Dump, -1); |
| checkOwnershipLink(obj2Dump, obj1Dump, undefined); |
| checkOwnershipLink(obj3Dump, obj1Dump, 2); |
| }); |
| |
| /** |
| * Check that container memory dumps have the expected structure with sizes |
| * as described by tree recipes. The fields of a tree recipe are used by this |
| * function to check the properties of a MemoryAllocatorDump as follows (see |
| * the buildDumpTrees documentation for more details about the structure of |
| * tree recipes): |
| * |
| * name: Expected name of the MAD. |
| * expected_removed: If provided and true, it is expected that there is no |
| * dump for the recipe. |
| * expected_size: Expected value of the 'size' numeric of the MAD (no |
| * 'size' numeric expected if undefined). |
| * expected_effective_size: Expected value of the 'effective_size' |
| * numeric of the MAD (no 'effective_size' numeric expected if |
| * undefined). |
| * expected_infos: List of expected MAD infos (zero infos expected if |
| * undefined). |
| * weak: Whether the MAD is expected to be weak (non-weak if undefined). |
| * owns: Expected GUID of the dump owned by the MAD. |
| * importance: Expected importance of the owhership from this MAD. |
| * expected_owned_by_links_count: Expected number of 'ownedBy' links of the |
| * MAD. |
| * children: List of tree recipes for child MADs (no children expected if |
| * undefined). |
| * |
| * Other fields of a tree recipe (including 'skip_build') are ignored by this |
| * function. |
| */ |
| function checkDumpTrees(containerDumps, allTreeRecipes) { |
| assert.lengthOf(containerDumps, allTreeRecipes.length); |
| |
| for (var i = 0; i < containerDumps.length; i++) { |
| var containerDump = containerDumps[i]; |
| var treeRecipes = allTreeRecipes[i]; |
| |
| var memoryAllocatorDumps = containerDump.memoryAllocatorDumps; |
| if (treeRecipes === undefined) { |
| assert.isUndefined(memoryAllocatorDumps, |
| 'expected undefined memory allocator dumps in ' + |
| containerDump.userFriendlyName); |
| continue; |
| } |
| |
| var expectedTreeRecipes = treeRecipes.filter(function(treeRecipe) { |
| return !treeRecipe['expected_removed']; |
| }); |
| |
| assert.isDefined(memoryAllocatorDumps, |
| 'expected defined memory allocator dumps in ' + |
| containerDump.userFriendlyName); |
| assert.lengthOf(memoryAllocatorDumps, expectedTreeRecipes.length, |
| 'expected ' + expectedTreeRecipes.length + ' root memory allocator ' + |
| 'dumps but got ' + memoryAllocatorDumps.length + ' in ' + |
| containerDump.userFriendlyName); |
| |
| function checkDumpTree(dump, treeRecipe, expectedParent, namePrefix) { |
| // Test sanity check. |
| assert.isFalse(!!treeRecipe['expected_removed']); |
| |
| // Check full name, parent, and container dump. |
| var expectedFullName = namePrefix + treeRecipe['name']; |
| var quantifiedName = dump.quantifiedName; |
| assert.equal(dump.fullName, expectedFullName, |
| quantifiedName + ' has invalid full name'); |
| assert.strictEqual(dump.parent, expectedParent, |
| quantifiedName + ' has invalid parent'); |
| assert.strictEqual(dump.containerMemoryDump, containerDump, |
| quantifiedName + ' has invalid container memory dump'); |
| assert.strictEqual(containerDump.getMemoryAllocatorDumpByFullName( |
| expectedFullName), dump, quantifiedName + |
| 'is not indexed in its container memory dump'); |
| |
| // Check the guid of the dump. |
| assert.strictEqual(dump.guid, treeRecipe['guid'], |
| quantifiedName + ' has invalid guid'); |
| |
| // Check that the 'weak' flag is correct. |
| assert.strictEqual(dump.weak, !!treeRecipe['weak'], |
| quantifiedName + ' has invalid weak flag'); |
| |
| // Check that sizes were calculated correctly. |
| assertDumpSizes(dump, |
| treeRecipe['expected_size'], |
| treeRecipe['expected_effective_size'], |
| treeRecipe['expected_infos'], |
| treeRecipe['expected_owned_by_sibling_sizes']); |
| |
| // Check that the 'owns' link is correct. |
| if (treeRecipe['owns'] === undefined) { |
| assert.isUndefined(dump.owns, |
| quantifiedName + ' was expected not to own another dump'); |
| } else { |
| var ownershipLink = dump.owns; |
| assert.isDefined(dump.owns, quantifiedName + |
| ' was expected to have an \'owns\' link'); |
| assert.strictEqual(ownershipLink.source, dump, |
| 'the \'owns\' link of ' + quantifiedName + ' has invalid source'); |
| var expectedImportance = treeRecipe['importance']; |
| assert.strictEqual(ownershipLink.importance, expectedImportance, |
| 'expected the importance of the \'owns\' link of ' + |
| quantifiedName + ' to be ' + expectedImportance + |
| ' but got ' + ownershipLink.importance); |
| var ownedDump = ownershipLink.target; |
| assert.strictEqual(ownedDump.guid, treeRecipe['owns'], |
| 'the \'owns\' link of ' + quantifiedName + |
| ' has an invalid target'); |
| assert.include(ownedDump.ownedBy, ownershipLink, |
| 'the target of the \'owns\' link of ' + quantifiedName + |
| ' doesn\'t have the link in its \'ownedBy\' list'); |
| } |
| |
| // Check that the number of 'ownedBy' links is correct. |
| var expectedOwnedByLinksCount = |
| treeRecipe['expected_owned_by_links_count']; |
| if (expectedOwnedByLinksCount !== undefined) { |
| assert.lengthOf(dump.ownedBy, expectedOwnedByLinksCount, |
| 'expected ' + quantifiedName + ' to have ' + |
| expectedOwnedByLinksCount + ' \'ownedBy\' links but got ' + |
| dump.ownedBy.length); |
| } |
| |
| // Check children recursively. |
| var actualChildren = dump.children; |
| var expectedChildren = (treeRecipe.children || []).filter( |
| function(childRecipe) { |
| return !childRecipe['expected_removed']; |
| }); |
| assert.lengthOf(actualChildren, expectedChildren.length, |
| 'expected ' + quantifiedName + ' to have ' + |
| expectedChildren.length + ' children but got ' + |
| actualChildren.length); |
| for (var k = 0; k < actualChildren.length; k++) { |
| checkDumpTree(actualChildren[k], expectedChildren[k], dump, |
| expectedFullName + '/'); |
| } |
| } |
| |
| for (var j = 0; j < memoryAllocatorDumps.length; j++) { |
| checkDumpTree( |
| memoryAllocatorDumps[j], expectedTreeRecipes[j], undefined, ''); |
| } |
| } |
| } |
| |
| // Check that the checkDumpTrees testing helper method above actually |
| // performs the expected checks. Since it will be used heavily throughout |
| // this file (where it is expected to pass), we only need to verify that it |
| // does indeed fail in several cases where it should. |
| test('testSanityCheck_checkDumpTrees_invalidName', function() { |
| var containerDumps = createContainerDumps(0); |
| var gmd = containerDumps[0]; |
| var v8Dump = new MemoryAllocatorDump(gmd, 'v8'); |
| var heapsDump = |
| new MemoryAllocatorDump(gmd, 'heaps'); // Should be 'v8/heaps'. |
| v8Dump.children.push(heapsDump); |
| heapsDump.parent = v8Dump; |
| gmd.memoryAllocatorDumps = [v8Dump]; |
| |
| assert.throws(function() { |
| checkDumpTrees(containerDumps, [ |
| [ |
| { |
| 'name': 'v8', |
| 'children': [ |
| { |
| 'name': 'heaps' |
| } |
| ] |
| } |
| ] |
| ]); |
| }, /'heaps'.*invalid full name/); |
| }); |
| |
| test('testSanityCheck_checkDumpTrees_invalidGuid', function() { |
| var containerDumps = createContainerDumps(0); |
| var gmd = containerDumps[0]; |
| gmd.memoryAllocatorDumps = [new MemoryAllocatorDump(gmd, 'v8', 42)]; |
| |
| assert.throws(function() { |
| checkDumpTrees(containerDumps, [ |
| [ |
| { |
| 'name': 'v8', |
| 'guid': 43 // This should be 42. |
| } |
| ] |
| ]); |
| }, /'v8'.*\binvalid guid\b/); |
| }); |
| |
| test('testSanityCheck_checkDumpTrees_invalidStructure', function() { |
| var containerDumps = createContainerDumps(0); |
| var gmd = containerDumps[0]; |
| var rootDump = new MemoryAllocatorDump(gmd, 'root'); |
| addChildDump(rootDump, 'child1'); |
| gmd.memoryAllocatorDumps = [rootDump]; |
| |
| assert.throws(function() { |
| checkDumpTrees(containerDumps, [ |
| [ |
| { |
| 'name': 'root', |
| 'children': [ |
| { |
| 'name': 'child1' |
| }, |
| { |
| // This child is not present in the dump. |
| 'name': 'child2', |
| 'skip_build': true |
| } |
| ] |
| } |
| ] |
| ]); |
| }, /expected.*\b2\b.*children.*got.*\b1\b/); |
| }); |
| |
| test('testSanityCheck_checkDumpTrees_invalidParentLink', function() { |
| var containerDumps = createContainerDumps(0); |
| var gmd = containerDumps[0]; |
| var rootDump = new MemoryAllocatorDump(gmd, 'root'); |
| var parentDump = addChildDump(rootDump, 'parent'); |
| var childDump = addChildDump(parentDump, 'child'); |
| childDump.parent = rootDump; // This should correctly be parentDump. |
| gmd.memoryAllocatorDumps = [rootDump]; |
| |
| assert.throws(function() { |
| checkDumpTrees(containerDumps, [ |
| [ |
| { |
| 'name': 'root', |
| 'children': [ |
| { |
| 'name': 'parent', |
| 'children': [ |
| { |
| 'name': 'child' |
| } |
| ] |
| } |
| ] |
| } |
| ] |
| ]); |
| }, 'invalid parent'); |
| }); |
| |
| test('testSanityCheck_checkDumpTrees_invalidSize', function() { |
| var containerDumps = createContainerDumps(1); |
| var gmd = containerDumps[0]; |
| var pmd = containerDumps[1]; |
| var rootDump = newAllocatorDump(pmd, 'root', {numerics: {size: 100}}); |
| addChildDump(rootDump, 'parent', {numerics: {size: 49}}); |
| pmd.memoryAllocatorDumps = [rootDump]; |
| |
| assert.throws(function() { |
| checkDumpTrees(containerDumps, [ |
| undefined, |
| [ |
| { |
| 'name': 'root', |
| 'expected_size': 100, |
| 'children': [ |
| { |
| 'name': 'parent', |
| 'expected_size': 50 // This should be 49. |
| } |
| ] |
| } |
| ] |
| ]); |
| }, /expected.*'size'.*value.*\b50\b.*got.*\b49\b/); |
| }); |
| |
| test('testSanityCheck_checkDumpTrees_invalidEffectiveSize', function() { |
| var containerDumps = createContainerDumps(1); |
| var gmd = containerDumps[0]; |
| var pmd = containerDumps[1]; |
| var rootDump = newAllocatorDump(pmd, 'root', |
| {numerics: {effective_size: 99}}); |
| addChildDump(rootDump, 'parent', {numerics: {effective_size: 50}}); |
| pmd.memoryAllocatorDumps = [rootDump]; |
| |
| assert.throws(function() { |
| checkDumpTrees(containerDumps, [ |
| undefined, |
| [ |
| { |
| 'name': 'root', |
| 'expected_effective_size': 100, // This should be 99. |
| 'children': [ |
| { |
| 'name': 'parent', |
| 'expected_effective_size': 50 |
| } |
| ] |
| } |
| ] |
| ]); |
| }, /expected.*'effective_size'.*value.*\b100\b.*got.*\b99\b/); |
| }); |
| |
| test('testSanityCheck_checkDumpTrees_invalidInfoCount', function() { |
| var containerDumps = createContainerDumps(0); |
| var gmd = containerDumps[0]; |
| gmd.memoryAllocatorDumps = [ |
| newAllocatorDump(gmd, 'v8', {numerics: {size: 50}}) |
| ]; |
| |
| assert.throws(function() { |
| checkDumpTrees(containerDumps, [ |
| [ |
| { |
| 'name': 'v8', |
| 'expected_size': 50, |
| 'expected_infos': [ |
| { |
| type: PROVIDED_SIZE_LESS_THAN_AGGREGATED_CHILDREN, |
| providedSize: 50, |
| dependencySize: 60 |
| } |
| ] |
| } |
| ] |
| ]); |
| }, /expected.*'v8'.*\b1 infos\b.*\bgot\b.*\b0\b/); |
| }); |
| |
| test('testSanityCheck_checkDumpTrees_invalidInfo', function() { |
| var containerDumps = createContainerDumps(0); |
| var gmd = containerDumps[0]; |
| var v8Dump = newAllocatorDump(gmd, 'v8', {numerics: {size: 50}}); |
| v8Dump.infos.push({ |
| type: PROVIDED_SIZE_LESS_THAN_AGGREGATED_CHILDREN, |
| providedSize: 40, |
| dependencySize: 50 |
| }); |
| gmd.memoryAllocatorDumps = [v8Dump]; |
| |
| assert.throws(function() { |
| checkDumpTrees(containerDumps, [ |
| [ |
| { |
| 'name': 'v8', |
| 'expected_size': 50, |
| 'expected_infos': [ |
| { |
| // Should be PROVIDED_SIZE_LESS_THAN_AGGREGATED_CHILDREN below. |
| type: PROVIDED_SIZE_LESS_THAN_LARGEST_OWNER, |
| providedSize: 40, |
| dependencySize: 50 |
| } |
| ] |
| } |
| ] |
| ]); |
| }, /\binfo 0\b.*'v8'.*\bexpected\b/); |
| }); |
| |
| test('testSanityCheck_checkDumpTrees_invalidOwnedBySiblingSizes', function() { |
| var containerDumps = createContainerDumps(0); |
| var gmd = containerDumps[0]; |
| var v8Dump = new MemoryAllocatorDump(gmd, 'v8'); |
| addChildDump(v8Dump, 'child1', {guid: 42}); |
| addChildDump(v8Dump, 'child2'); |
| gmd.memoryAllocatorDumps = [v8Dump]; |
| |
| assert.throws(function() { |
| checkDumpTrees(containerDumps, [ |
| [ |
| { |
| 'name': 'v8', |
| 'children': [ |
| { |
| 'name': 'child1', |
| 'guid': 42 |
| }, |
| { |
| 'name': 'child2', |
| 'expected_owned_by_sibling_sizes': { |
| 'child1': 40 // This should be 30. |
| } |
| } |
| ] |
| } |
| ] |
| ]); |
| }, /\bownedBySiblingSizes\b.*'v8\/child2'.*\bexpected\b/); |
| }); |
| |
| test('testSanityCheck_checkDumpTrees_invalidWeakFlag', |
| function() { |
| var containerDumps = createContainerDumps(0); |
| var gmd = containerDumps[0]; |
| var parentDump = new MemoryAllocatorDump(gmd, 'parent'); |
| var childDump = addChildDump(parentDump, 'child'); |
| childDump.weak = true; |
| gmd.memoryAllocatorDumps = [parentDump]; |
| |
| assert.throws(function() { |
| checkDumpTrees(containerDumps, [ |
| [ |
| { |
| 'name': 'parent', |
| 'children': [ |
| { |
| 'name': 'child', |
| // Missing "'weak': true". |
| } |
| ] |
| } |
| ] |
| ]); |
| }, /'parent\/child'.*\binvalid weak flag\b/); |
| |
| assert.throws(function() { |
| checkDumpTrees(containerDumps, [ |
| [ |
| { |
| 'name': 'parent', |
| 'weak': true, // This should be false (or not provided). |
| 'children': [ |
| { |
| 'name': 'child', |
| 'weak': true |
| } |
| ] |
| } |
| ] |
| ]); |
| }, /'parent'.*\binvalid weak flag\b/); |
| }); |
| |
| test('testSanityCheck_checkDumpTrees_dumpNotRemoved', function() { |
| var containerDumps = createContainerDumps(0); |
| var gmd = containerDumps[0]; |
| var parentDump = new MemoryAllocatorDump(gmd, 'parent'); |
| for (var i = 1; i <= 3; i++) |
| addChildDump(parentDump, 'child' + i); |
| var otherDump = new MemoryAllocatorDump(gmd, 'other'); |
| gmd.memoryAllocatorDumps = [parentDump, otherDump]; |
| |
| // Child MAD not removed. |
| assert.throws(function() { |
| checkDumpTrees(containerDumps, [ |
| [ |
| { |
| 'name': 'parent', |
| 'children': [ |
| { |
| 'name': 'child1', |
| }, |
| { |
| 'name': 'child2', |
| 'expected_removed': true |
| }, |
| { |
| 'name': 'child3', |
| } |
| ] |
| }, |
| { |
| 'name': 'other' |
| } |
| ] |
| ]); |
| }, /\bexpected\b.*'parent'.*\b2 children\b.*\bgot 3\b/); |
| |
| // Root MAD not removed. |
| assert.throws(function() { |
| checkDumpTrees(containerDumps, [ |
| [ |
| { |
| 'name': 'parent', |
| 'children': [ |
| { |
| 'name': 'child1', |
| }, |
| { |
| 'name': 'child2' |
| }, |
| { |
| 'name': 'child3', |
| } |
| ] |
| }, |
| { |
| 'name': 'other', |
| 'expected_removed': true |
| } |
| ] |
| ]); |
| }, /\bexpected\b.*\b1 root memory allocator dumps\b.*\bgot 2\b/); |
| }); |
| |
| test('testSanityCheck_checkDumpTrees_invalidOwnership', function() { |
| var containerDumps = createContainerDumps(1); |
| var gmd = containerDumps[0]; |
| var pmd1 = containerDumps[1]; |
| var ownedDump = new MemoryAllocatorDump(gmd, 'owned', 42); |
| var ownerDump1 = new MemoryAllocatorDump(pmd1, 'owner1'); |
| var link1 = addOwnershipLink(ownerDump1, ownedDump); |
| var ownerDump2 = new MemoryAllocatorDump(pmd1, 'owner2'); |
| var link2 = addOwnershipLink(ownerDump2, ownedDump, 3); |
| var nonOwnerDump = new MemoryAllocatorDump(pmd1, 'non-owner', 90); |
| gmd.memoryAllocatorDumps = [ownedDump]; |
| pmd1.memoryAllocatorDumps = [ownerDump1, ownerDump2, nonOwnerDump]; |
| |
| // Missing 'owns' link. |
| assert.throws(function() { |
| checkDumpTrees(containerDumps, [ |
| [ |
| { |
| 'name': 'owned', |
| 'guid': 42 |
| } |
| ], |
| [ |
| { |
| 'name': 'owner1', |
| 'owns': 42 |
| }, |
| { |
| 'name': 'owner2', |
| 'importance': 3, |
| 'owns': 42 |
| }, |
| { |
| 'name': 'non-owner', |
| 'guid': 90, |
| 'owns': 42 // This should not be here. |
| } |
| ] |
| ]); |
| }, /'non-owner'.*\bwas expected to have\b.*'owns' link\b/); |
| |
| // Extra 'owns' link. |
| assert.throws(function() { |
| checkDumpTrees(containerDumps, [ |
| [ |
| { |
| 'name': 'owned', |
| 'guid': 42 |
| } |
| ], |
| [ |
| { |
| 'name': 'owner1' |
| // Missing: "'owns': 42". |
| }, |
| { |
| 'name': 'owner2', |
| 'importance': 3, |
| 'owns': 42 |
| }, |
| { |
| 'name': 'non-owner', |
| 'guid': 90 |
| } |
| ] |
| ]); |
| }, /'owner1'.*\bwas expected not to own\b/); |
| |
| // Invalid ownership importance. |
| assert.throws(function() { |
| checkDumpTrees(containerDumps, [ |
| [ |
| { |
| 'name': 'owned', |
| 'guid': 42 |
| } |
| ], |
| [ |
| { |
| 'name': 'owner1', |
| 'owns': 42 |
| }, |
| { |
| 'name': 'owner2', |
| 'importance': 2, // This should be 3. |
| 'owns': 42 |
| }, |
| { |
| 'name': 'non-owner', |
| 'guid': 90 |
| } |
| ] |
| ]); |
| }, /\bexpected\b.*\bimportance\b.*'owner2'.*\b2 but got 3\b/); |
| |
| // Invalid ownership target. |
| assert.throws(function() { |
| checkDumpTrees(containerDumps, [ |
| [ |
| { |
| 'name': 'owned', |
| 'guid': 42 |
| } |
| ], |
| [ |
| { |
| 'name': 'owner1', |
| 'owns': 90 // This should be 42. |
| }, |
| { |
| 'name': 'owner2', |
| 'importance': 3, |
| 'owns': 42 |
| }, |
| { |
| 'name': 'non-owner', |
| 'guid': 90 |
| } |
| ] |
| ]); |
| }, /'owner1'.*\binvalid target\b/); |
| |
| // Invalid 'ownedBy' ownership links count. |
| assert.throws(function() { |
| checkDumpTrees(containerDumps, [ |
| [ |
| { |
| 'name': 'owned', |
| 'guid': 42, |
| 'expected_owned_by_links_count': 3 // This should be 2. |
| } |
| ], |
| [ |
| { |
| 'name': 'owner1', |
| 'owns': 42 |
| }, |
| { |
| 'name': 'owner2', |
| 'importance': 3, |
| 'owns': 42 |
| }, |
| { |
| 'name': 'non-owner', |
| 'guid': 90 |
| } |
| ] |
| ]); |
| }, /'owned'.*\bhave 3 'ownedBy' links\b.*\bgot 2\b/); |
| |
| // Invalid ownership source. |
| link1.source = ownerDump2; |
| assert.throws(function() { |
| checkDumpTrees(containerDumps, [ |
| [ |
| { |
| 'name': 'owned', |
| 'guid': 42 |
| } |
| ], |
| [ |
| { |
| 'name': 'owner1', |
| 'owns': 42 |
| }, |
| { |
| 'name': 'owner2', |
| 'importance': 3, |
| 'owns': 42 |
| }, |
| { |
| 'name': 'non-owner', |
| 'guid': 90 |
| } |
| ] |
| ]); |
| }, /'owns' link\b.*'owner1'.*\binvalid source\b/); |
| link1.source = ownerDump1; |
| |
| // Ownership link not in target's 'ownedBy' list. |
| ownedDump.ownedBy.pop(); |
| assert.throws(function() { |
| checkDumpTrees(containerDumps, [ |
| [ |
| { |
| 'name': 'owned', |
| 'guid': 42 |
| } |
| ], |
| [ |
| { |
| 'name': 'owner1', |
| 'owns': 42 |
| }, |
| { |
| 'name': 'owner2', |
| 'importance': 3, |
| 'owns': 42 |
| }, |
| { |
| 'name': 'non-owner', |
| 'guid': 90 |
| } |
| ] |
| ]); |
| }, /\btarget of\b.*'owner2'.*'ownedBy' list\b/); |
| ownedDump.ownedBy.push(link2); |
| }); |
| |
| /** |
| * Build container memory dumps from tree recipes, let the resulting |
| * GlobalMemoryDump calculate sizes and effective sizes, and then check that |
| * the augmented container memory dumps have the expected structure with |
| * correct sizes and effective sizes (as described by the same tree recipes). |
| * |
| * See the documentation for buildDumpTrees and checkDumpTrees for more |
| * details about the structure of tree recipes. |
| */ |
| function testSizesCalculation(allTreeRecipes) { |
| var m = new Model(); |
| var io = new tr.importer.ImportOptions(); |
| io.showImportWarnings = false; |
| m.importOptions = io; |
| |
| var containerDumps = buildDumpTrees(allTreeRecipes, m); |
| var gmd = containerDumps[0]; |
| gmd.calculateSizes(); |
| gmd.calculateEffectiveSizes(); |
| gmd.forceRebuildingMemoryAllocatorDumpByFullNameIndices(); |
| checkDumpTrees(containerDumps, allTreeRecipes); |
| } |
| |
| // Check that the testSizesCalculation testing helper method above |
| // actually performs the expected checks. Since it will be used heavily |
| // throughout this file (where it is expected to pass), we only need to |
| // verify that it does indeed fail when it should. |
| test('testSanityCheck_testSizesCalculation', function() { |
| assert.throws(function() { |
| testSizesCalculation([ |
| [], |
| undefined, |
| [ |
| { |
| 'name': 'winheap' |
| }, |
| { |
| 'name': 'malloc', |
| 'expected_size': 100, |
| 'children': [ |
| { |
| 'name': 'allocated_objects', |
| 'size': 100, |
| 'expected_size': 100 |
| }, |
| { |
| 'name': 'extra', |
| 'size': 20, |
| 'expected_size': 20 |
| } |
| ] |
| } |
| ] |
| ]); |
| }, /expected.*'size'.*value.*\b100\b.*got.*\b120\b/); |
| }); |
| |
| function calculationTest(caseName, treeRecipes) { |
| test('calculateSizes_' + caseName, function() { |
| testSizesCalculation(treeRecipes); |
| }); |
| } |
| |
| /** |
| * Build container memory dumps from tree recipes, let the resulting |
| * GlobalMemoryDump remove weak memory dumps, and then check that the updated |
| * container memory dumps have the expected structure (as described by the |
| * same tree recipes). |
| * |
| * See the documentation for buildDumpTrees and checkDumpTrees for more |
| * details about the structure of tree recipes. |
| */ |
| function testWeakDumpRemoval(allTreeRecipes) { |
| var m = new tr.Model(); |
| var io = new tr.importer.ImportOptions(); |
| io.showImportWarnings = false; |
| m.importOptions = io; |
| |
| var containerDumps = buildDumpTrees(allTreeRecipes, m); |
| var gmd = containerDumps[0]; |
| gmd.removeWeakDumps(); |
| gmd.forceRebuildingMemoryAllocatorDumpByFullNameIndices(); |
| checkDumpTrees(containerDumps, allTreeRecipes); |
| } |
| |
| // Similarly to testSanityCheck_testSizesCalculation, check that the |
| // testWeakDumpRemoval testing helper method above actually performs the |
| // expected checks. |
| test('testSanityCheck_testWeakDumpRemoval', function() { |
| assert.throws(function() { |
| testWeakDumpRemoval([ |
| [], |
| undefined, |
| [ |
| { |
| 'name': 'winheap' |
| }, |
| { |
| 'name': 'malloc', |
| 'children': [ |
| { |
| 'name': 'allocated_objects' |
| }, |
| { |
| 'name': 'directly_weak', |
| 'guid': 42, |
| 'weak': true, |
| 'expected_removed': true |
| }, |
| { |
| 'name': 'indirectly_weak', |
| 'owns': 42 |
| // Missing: "'expected_removed': true". |
| } |
| ] |
| } |
| ] |
| ]); |
| }, /expected.*'malloc'.*\b2 children\b.*\bgot 1\b/); |
| }); |
| |
| function weakDumpRemovalTest(caseName, treeRecipes) { |
| test('removeWeakDumps_' + caseName, function() { |
| testWeakDumpRemoval(treeRecipes); |
| }); |
| } |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // Actual tests begin here. |
| ///////////////////////////////////////////////////////////////////////////// |
| |
| test('iterateContainerDumps_withoutProcessMemoryDumps', function() { |
| var containerDumps = createContainerDumps(0); |
| var gmd = containerDumps[0]; |
| |
| var visitedContainerDumps = []; |
| gmd.iterateContainerDumps(buildArgPusher(visitedContainerDumps)); |
| assertEqualUniqueMembers(visitedContainerDumps, containerDumps); |
| }); |
| |
| test('iterateContainerDumps_withProcessMemoryDumps', function() { |
| var containerDumps = createContainerDumps(2); |
| var gmd = containerDumps[0]; |
| |
| var visitedContainerDumps = []; |
| gmd.iterateContainerDumps(buildArgPusher(visitedContainerDumps)); |
| assertEqualUniqueMembers(visitedContainerDumps, containerDumps); |
| }); |
| |
| test('iterateAllRootAllocatorDumps', function() { |
| var containerDumps = buildDumpTrees([ |
| [ // GMD. |
| { |
| 'name': 'globalSharedDump1' |
| }, |
| { |
| 'name': 'globalSharedDump2' |
| } |
| ], |
| [ // PMD. |
| { |
| 'name': 'v8', |
| 'children': [ |
| { |
| 'name': 'isolate' |
| } |
| ] |
| } |
| ] |
| ]); |
| var gmd = containerDumps[0]; |
| var pmd = containerDumps[1]; |
| |
| var visitedAllocatorDumps = []; |
| gmd.iterateAllRootAllocatorDumps(buildArgPusher(visitedAllocatorDumps)); |
| assertEqualUniqueMembers(visitedAllocatorDumps, [ |
| gmd.getMemoryAllocatorDumpByFullName('globalSharedDump1'), |
| gmd.getMemoryAllocatorDumpByFullName('globalSharedDump2'), |
| pmd.getMemoryAllocatorDumpByFullName('v8') |
| ]); |
| }); |
| |
| test('traverseAllocatorDumpsInDepthFirstOrder_oneTreeWithoutOwners', |
| function() { |
| var containerDumps = buildDumpTrees([ |
| [ // GMD. |
| { |
| 'name': 'root', |
| 'children': [ |
| { |
| 'name': 'parent', |
| 'children': [ |
| { |
| 'name': 'child1' |
| }, |
| { |
| 'name': 'child2' |
| } |
| ] |
| } |
| ] |
| } |
| ] |
| ]); |
| var gmd = containerDumps[0]; |
| |
| // Post-order. |
| var visitedAllocatorDumps = []; |
| gmd.traverseAllocatorDumpsInDepthFirstPostOrder( |
| buildArgPusher(visitedAllocatorDumps)); |
| assert.deepEqual(visitedAllocatorDumps, [ |
| gmd.getMemoryAllocatorDumpByFullName('root/parent/child1'), |
| gmd.getMemoryAllocatorDumpByFullName('root/parent/child2'), |
| gmd.getMemoryAllocatorDumpByFullName('root/parent'), |
| gmd.getMemoryAllocatorDumpByFullName('root') |
| ]); |
| |
| // Pre-order. |
| var visitedAllocatorDumps = []; |
| gmd.traverseAllocatorDumpsInDepthFirstPreOrder( |
| buildArgPusher(visitedAllocatorDumps)); |
| assert.deepEqual(visitedAllocatorDumps, [ |
| gmd.getMemoryAllocatorDumpByFullName('root'), |
| gmd.getMemoryAllocatorDumpByFullName('root/parent'), |
| gmd.getMemoryAllocatorDumpByFullName('root/parent/child1'), |
| gmd.getMemoryAllocatorDumpByFullName('root/parent/child2') |
| ]); |
| }); |
| |
| test('traverseAllocatorDumpsInDepthFirstOrder_oneTreeWithOwners', |
| function() { |
| var containerDumps = buildDumpTrees([ |
| [], // GMD. |
| [ // PMD. |
| { |
| 'name': 'root', |
| 'children': [ |
| { |
| 'name': 'parent', |
| 'children': [ |
| { |
| 'name': 'child1', |
| 'owns': 0 |
| }, |
| { |
| 'name': 'child2', |
| 'guid': 0 |
| }, |
| { |
| 'name': 'child3', |
| 'owns': 0 |
| } |
| ] |
| } |
| ] |
| } |
| ] |
| ]); |
| var gmd = containerDumps[0]; |
| var pmd = containerDumps[1]; |
| |
| // Post-order. |
| var visitedAllocatorDumps = []; |
| gmd.traverseAllocatorDumpsInDepthFirstPostOrder( |
| buildArgPusher(visitedAllocatorDumps)); |
| assert.deepEqual(visitedAllocatorDumps, [ |
| pmd.getMemoryAllocatorDumpByFullName('root/parent/child1'), |
| pmd.getMemoryAllocatorDumpByFullName('root/parent/child3'), |
| pmd.getMemoryAllocatorDumpByFullName('root/parent/child2'), |
| pmd.getMemoryAllocatorDumpByFullName('root/parent'), |
| pmd.getMemoryAllocatorDumpByFullName('root') |
| ]); |
| |
| // Pre-order. |
| var visitedAllocatorDumps = []; |
| gmd.traverseAllocatorDumpsInDepthFirstPreOrder( |
| buildArgPusher(visitedAllocatorDumps)); |
| assert.deepEqual(visitedAllocatorDumps, [ |
| pmd.getMemoryAllocatorDumpByFullName('root'), |
| pmd.getMemoryAllocatorDumpByFullName('root/parent'), |
| pmd.getMemoryAllocatorDumpByFullName('root/parent/child2'), |
| pmd.getMemoryAllocatorDumpByFullName('root/parent/child1'), |
| pmd.getMemoryAllocatorDumpByFullName('root/parent/child3') |
| ]); |
| }); |
| |
| test('traverseAllocatorDumpsInDepthFirstOrder_multipleTrees', function() { |
| var containerDumps = buildDumpTrees([ |
| [ // GMD. |
| { |
| 'name': 'shared', |
| 'children': [ |
| { |
| 'name': 'pool1', |
| 'guid': 1 |
| }, |
| { |
| 'name': 'pool2', |
| 'owns': 3 |
| } |
| ] |
| } |
| ], |
| [ // PMD. |
| { |
| 'name': 'oilpan', |
| 'children': [ |
| { |
| 'name': 'objects' |
| }, |
| { |
| 'name': 'heaps', |
| 'owns': 1, |
| 'children': [ |
| { |
| 'name': 'small', |
| 'guid': 2 |
| }, |
| { |
| 'name': 'large' |
| } |
| ] |
| } |
| ] |
| }, |
| { |
| 'name': 'v8', |
| 'children': [ |
| { |
| 'name': 'isolate1', |
| 'owns': 2 |
| }, |
| { |
| 'name': 'isolate2', |
| 'guid': 3 |
| } |
| ] |
| } |
| ] |
| ]); |
| var gmd = containerDumps[0]; |
| var pmd = containerDumps[1]; |
| |
| // Post-order. |
| var visitedAllocatorDumps = []; |
| gmd.traverseAllocatorDumpsInDepthFirstPostOrder( |
| buildArgPusher(visitedAllocatorDumps)); |
| assert.deepEqual(visitedAllocatorDumps, [ |
| pmd.getMemoryAllocatorDumpByFullName('v8/isolate1'), |
| pmd.getMemoryAllocatorDumpByFullName('oilpan/heaps/small'), |
| pmd.getMemoryAllocatorDumpByFullName('oilpan/heaps/large'), |
| pmd.getMemoryAllocatorDumpByFullName('oilpan/heaps'), |
| gmd.getMemoryAllocatorDumpByFullName('shared/pool1'), |
| gmd.getMemoryAllocatorDumpByFullName('shared/pool2'), |
| gmd.getMemoryAllocatorDumpByFullName('shared'), |
| pmd.getMemoryAllocatorDumpByFullName('oilpan/objects'), |
| pmd.getMemoryAllocatorDumpByFullName('oilpan'), |
| pmd.getMemoryAllocatorDumpByFullName('v8/isolate2'), |
| pmd.getMemoryAllocatorDumpByFullName('v8') |
| ]); |
| |
| // Pre-order. |
| var visitedAllocatorDumps = []; |
| gmd.traverseAllocatorDumpsInDepthFirstPreOrder( |
| buildArgPusher(visitedAllocatorDumps)); |
| assert.deepEqual(visitedAllocatorDumps, [ |
| gmd.getMemoryAllocatorDumpByFullName('shared'), |
| gmd.getMemoryAllocatorDumpByFullName('shared/pool1'), |
| pmd.getMemoryAllocatorDumpByFullName('oilpan'), |
| pmd.getMemoryAllocatorDumpByFullName('oilpan/objects'), |
| pmd.getMemoryAllocatorDumpByFullName('oilpan/heaps'), |
| pmd.getMemoryAllocatorDumpByFullName('oilpan/heaps/small'), |
| pmd.getMemoryAllocatorDumpByFullName('oilpan/heaps/large'), |
| pmd.getMemoryAllocatorDumpByFullName('v8'), |
| pmd.getMemoryAllocatorDumpByFullName('v8/isolate1'), |
| pmd.getMemoryAllocatorDumpByFullName('v8/isolate2'), |
| gmd.getMemoryAllocatorDumpByFullName('shared/pool2') |
| ]); |
| }); |
| |
| test('traverseAllocatorDumpsInDepthFirstPostOrder_cycle', function() { |
| var containerDumps = buildDumpTrees([ |
| [ // GMD. |
| { |
| 'name': 'shared', |
| 'owns': 2, |
| 'children': [ |
| { |
| 'name': 'pool', |
| 'guid': 1 |
| } |
| ] |
| } |
| ], |
| [ // PMD. |
| { |
| 'name': 'oilpan', |
| 'owns': 1, |
| 'children': [ |
| { |
| 'name': 'objects', |
| 'guid': 2 |
| } |
| ] |
| } |
| ] |
| ]); |
| var gmd = containerDumps[0]; |
| |
| // Post-order. |
| assert.throws(function() { |
| gmd.traverseAllocatorDumpsInDepthFirstPostOrder(function() {}); |
| }, /contains.*cycle/); |
| |
| // Pre-order. |
| var visitedAllocatorDumps = []; |
| gmd.traverseAllocatorDumpsInDepthFirstPreOrder( |
| buildArgPusher(visitedAllocatorDumps)); |
| assert.deepEqual(visitedAllocatorDumps, []); |
| }); |
| |
| // Just check that the method doesn't crash upon encountering empty and/or |
| // undefined memory allocator dumps. |
| calculationTest('noDumps', [ |
| undefined, // GMD. |
| [], // PMD1. |
| undefined // PMD2. |
| ]); |
| |
| calculationTest('flatDumps', [ |
| [ // GMD. |
| { |
| 'name': 'shared', |
| 'size': 1024, |
| 'expected_size': 1024, |
| 'expected_effective_size': 1024 |
| } |
| ], |
| [ // PMD. |
| { |
| 'name': 'gpu' |
| } |
| ] |
| ]); |
| |
| calculationTest('zeroSizes', [ |
| [ // GMD. |
| { |
| 'name': 'shared', |
| 'size': 0, |
| 'expected_size': 0, |
| 'expected_effective_size': 0 |
| } |
| ], |
| [ // PMD1. |
| { |
| 'name': 'gpu', |
| 'expected_size': 0, |
| 'expected_effective_size': 0, |
| 'children': [ |
| { |
| 'name': 'zero', |
| 'size': 0, |
| 'expected_size': 0, |
| 'expected_effective_size': 0 |
| } |
| ] |
| } |
| ], |
| [ // PMD2. |
| { |
| 'name': 'gpu', |
| 'expected_size': 0, |
| 'expected_effective_size': 0, |
| 'children': [ |
| { |
| 'name': 'zero', |
| 'size': 0, |
| 'expected_size': 0, |
| 'expected_effective_size': 0 |
| }, |
| { |
| 'name': 'undefined' |
| } |
| ] |
| } |
| ] |
| ]); |
| |
| calculationTest('children_allSizesUndefined', [ |
| [ |
| { |
| 'name': 'parent', |
| 'children': [ |
| { |
| 'name': 'child1' |
| }, |
| { |
| 'name': 'child2' |
| } |
| ] |
| } |
| ] |
| ]); |
| |
| calculationTest('children_parentSizeUndefined', [ |
| [ |
| { |
| 'name': 'parent', |
| 'expected_size': 384, |
| 'expected_effective_size': 384, |
| 'children': [ |
| { |
| 'name': 'child1', |
| 'size': 128, |
| 'expected_size': 128, |
| 'expected_effective_size': 128 |
| }, |
| { |
| 'name': 'child2', |
| 'size': 256, |
| 'expected_size': 256, |
| 'expected_effective_size': 256 |
| } |
| ] |
| } |
| ] |
| ]); |
| |
| calculationTest('children_parentSizeDefined_childrenAddUp', [ |
| [ |
| { |
| 'name': 'parent', |
| 'size': 0, |
| 'expected_size': 0, |
| 'expected_effective_size': 0, |
| 'children': [ |
| { |
| 'name': 'child1' |
| }, |
| { |
| 'name': 'child2' |
| } |
| ] |
| } |
| ] |
| ]); |
| |
| calculationTest('children_parentSizeDefined_childrenDontAddUp', [ |
| [ |
| { |
| 'name': 'parent', |
| 'size': 2048, |
| 'expected_size': 2048, |
| 'expected_effective_size': 2048, |
| 'children': [ |
| { |
| 'name': '<unspecified>', |
| 'skip_build': true, |
| 'expected_size': 2048, |
| 'expected_effective_size': 2048 |
| }, |
| { |
| 'name': 'child1' |
| }, |
| { |
| 'name': 'child2' |
| } |
| ] |
| } |
| ] |
| ]); |
| |
| calculationTest('children_oneChildSizeUndefined_childrenAddUp', [ |
| [ |
| { |
| 'name': 'parent', |
| 'size': 4096, |
| 'expected_size': 4096, |
| 'expected_effective_size': 4096, |
| 'children': [ |
| { |
| 'name': 'child1' |
| }, |
| { |
| 'name': 'child2', |
| 'size': 4096, |
| 'expected_size': 4096, |
| 'expected_effective_size': 4096 |
| } |
| ] |
| } |
| ] |
| ]); |
| |
| calculationTest('children_oneChildSizeUndefined_childrenDontAddUp', [ |
| [ |
| { |
| 'name': 'parent', |
| 'size': 6144, |
| 'expected_size': 6144, |
| 'expected_effective_size': 6144, |
| 'children': [ |
| { |
| 'name': '<unspecified>', |
| 'skip_build': true, |
| 'expected_size': 2048, |
| 'expected_effective_size': 2048 |
| }, |
| { |
| 'name': 'child1' |
| }, |
| { |
| 'name': 'child2', |
| 'size': 4096, |
| 'expected_size': 4096, |
| 'expected_effective_size': 4096 |
| } |
| ] |
| } |
| ] |
| ]); |
| |
| calculationTest('children_allSizesDefined_childrenAddUp', [ |
| [ |
| { |
| 'name': 'parent', |
| 'size': 100, |
| 'expected_size': 100, |
| 'expected_effective_size': 100, |
| 'children': [ |
| { |
| 'name': 'child1', |
| 'size': 70, |
| 'expected_size': 70, |
| 'expected_effective_size': 70 |
| }, |
| { |
| 'name': 'child2', |
| 'size': 30, |
| 'expected_size': 30, |
| 'expected_effective_size': 30 |
| } |
| ] |
| } |
| ] |
| ]); |
| |
| calculationTest('children_allSizesDefined_childrenDontUp', [ |
| [ |
| { |
| 'name': 'parent', |
| 'size': 150, |
| 'expected_size': 150, |
| 'expected_effective_size': 150, |
| 'children': [ |
| { |
| 'name': '<unspecified>', |
| 'skip_build': true, |
| 'expected_size': 50, |
| 'expected_effective_size': 50 |
| }, |
| { |
| 'name': 'child1', |
| 'size': 70, |
| 'expected_size': 70, |
| 'expected_effective_size': 70 |
| }, |
| { |
| 'name': 'child2', |
| 'size': 30, |
| 'expected_size': 30, |
| 'expected_effective_size': 30 |
| } |
| ] |
| } |
| ] |
| ]); |
| |
| calculationTest('children_oneChildSizeDefined', [ |
| [ |
| { |
| 'name': 'parent', |
| 'expected_size': 49, |
| 'expected_effective_size': 49, |
| 'children': [ |
| { |
| 'name': 'child1', |
| 'size': 49, |
| 'expected_size': 49, |
| 'expected_effective_size': 49 |
| }, |
| { |
| 'name': 'child2' |
| } |
| ] |
| } |
| ] |
| ]); |
| |
| calculationTest('children_multipleLevels', [ |
| [], // GMD. |
| [ // PMD. |
| { |
| 'name': 'v8', |
| 'expected_size': 36, |
| 'expected_effective_size': 36, |
| 'children': [ |
| { |
| 'name': 'isolate1', |
| 'size': 10, |
| 'expected_size': 10, |
| 'expected_effective_size': 10, |
| 'children': [ |
| { |
| 'skip_build': true, |
| 'name': '<unspecified>', |
| 'expected_size': 3, |
| 'expected_effective_size': 3 |
| }, |
| { |
| 'name': 'objects', |
| 'size': 3, |
| 'expected_size': 3, |
| 'expected_effective_size': 3 |
| }, |
| { |
| 'name': 'heaps', |
| 'size': 4, |
| 'expected_size': 4, |
| 'expected_effective_size': 4 |
| } |
| ] |
| }, |
| { |
| 'name': 'isolate2', |
| 'size': 12, |
| 'expected_size': 12, |
| 'expected_effective_size': 12, |
| 'children': [ |
| { |
| 'name': 'objects', |
| 'size': 5, |
| 'expected_size': 5, |
| 'expected_effective_size': 5 |
| }, |
| { |
| 'name': 'heaps', |
| 'size': 7, |
| 'expected_size': 7, |
| 'expected_effective_size': 7 |
| } |
| ] |
| }, |
| { |
| 'name': 'isolate3', |
| 'expected_size': 14, |
| 'expected_effective_size': 14, |
| 'children': [ |
| { |
| 'name': 'objects', |
| 'size': 14, |
| 'expected_size': 14, |
| 'expected_effective_size': 14 |
| }, |
| { |
| 'name': 'heaps' |
| } |
| ] |
| } |
| ] |
| } |
| ] |
| ]); |
| |
| calculationTest('owners_allSizesUndefined', [ |
| [ // GMD. |
| { |
| 'name': 'bitmap', |
| 'guid': 7 |
| } |
| ], |
| [ // PMD1. |
| { |
| 'name': 'tile', |
| 'owns': 7, |
| 'importance': 1 |
| } |
| ], |
| [ // PMD2. |
| { |
| 'name': 'chunk', |
| 'owns': 7 |
| } |
| ] |
| ]); |
| |
| calculationTest('owners_ownedSizeDefined', [ |
| [ // GMD. |
| { |
| 'name': 'bitmap', |
| 'size': 15, |
| 'expected_size': 15, |
| 'expected_effective_size': 15, |
| 'guid': 7 |
| } |
| ], |
| [ // PMD1. |
| { |
| 'name': 'tile', |
| 'owns': 7 |
| } |
| ], |
| [ // PMD2. |
| { |
| 'name': 'chunk', |
| 'owns': 7 |
| } |
| ] |
| ]); |
| |
| calculationTest('owners_ownedSizeUndefined', [ |
| [ // GMD. |
| { |
| 'name': 'bitmap', |
| 'expected_size': 9, |
| 'expected_effective_size': 0, |
| 'guid': 7 |
| } |
| ], |
| [ // PMD1. |
| { |
| 'name': 'tile', |
| 'size': 5, |
| 'expected_size': 5, |
| 'expected_effective_size': 2.5, |
| 'owns': 7 |
| } |
| ], |
| [ // PMD2. |
| { |
| 'name': 'chunk', |
| 'size': 9, |
| 'expected_size': 9, |
| 'expected_effective_size': 6.5, |
| 'owns': 7 |
| } |
| ] |
| ]); |
| |
| calculationTest('owners_oneOwnerSizeDefined', [ |
| [ // GMD. |
| { |
| 'name': 'bitmap', |
| 'expected_size': 16, |
| 'expected_effective_size': 0, |
| 'guid': 7 |
| } |
| ], |
| [ // PMD1. |
| { |
| 'name': 'tile', |
| 'size': 16, |
| 'expected_size': 16, |
| 'expected_effective_size': 16, |
| 'owns': 7 |
| } |
| ], |
| [ // PMD2. |
| { |
| 'name': 'chunk', |
| 'owns': 7 |
| } |
| ] |
| ]); |
| |
| calculationTest('owners_oneOwnerSizeUndefined', [ |
| [ // GMD. |
| { |
| 'name': 'bitmap', |
| 'size': 20, |
| 'expected_size': 20, |
| 'expected_effective_size': 2, |
| 'guid': 7 |
| } |
| ], |
| [ // PMD1. |
| { |
| 'name': 'tile', |
| 'owns': 7 |
| } |
| ], |
| [ // PMD2. |
| { |
| 'name': 'chunk', |
| 'size': 18, |
| 'expected_size': 18, |
| 'expected_effective_size': 18, |
| 'owns': 7 |
| } |
| ] |
| ]); |
| |
| calculationTest('owners_allSizesDefined', [ |
| [ // GMD. |
| { |
| 'name': 'bitmap', |
| 'size': 60, |
| 'expected_size': 60, |
| 'expected_effective_size': 31, |
| 'guid': 7 |
| } |
| ], |
| [ // PMD1. |
| { |
| 'name': 'tile', |
| 'size': 29, |
| 'expected_size': 29, |
| 'expected_effective_size': 19.5, |
| 'owns': 7 |
| } |
| ], |
| [ // PMD2. |
| { |
| 'name': 'chunk', |
| 'size': 19, |
| 'expected_size': 19, |
| 'expected_effective_size': 9.5, |
| 'owns': 7 |
| } |
| ] |
| ]); |
| |
| calculationTest('owners_hierarchy', [ |
| [ // GMD. |
| { |
| 'name': 'bitmap', |
| 'expected_size': 50, |
| 'expected_effective_size': 0, |
| 'guid': 7 |
| } |
| ], |
| [ // PMD1. |
| { |
| 'name': 'tile', |
| 'expected_size': 50, |
| 'expected_effective_size': 0, |
| 'owns': 7, |
| 'guid': 0 |
| }, |
| { |
| 'name': 'object1', |
| 'size': 30, |
| 'owns': 0, |
| 'expected_size': 30, |
| 'expected_effective_size': 9 |
| }, |
| { |
| 'name': 'object2', |
| 'owns': 0 |
| }, |
| { |
| 'name': 'object3', |
| 'size': 50, |
| 'owns': 0, |
| 'expected_size': 50, |
| 'expected_effective_size': 21 |
| } |
| ], |
| [ // PMD2. |
| { |
| 'name': 'chunk', |
| 'size': 40, |
| 'expected_size': 40, |
| 'expected_effective_size': 20, |
| 'owns': 7 |
| } |
| ] |
| ]); |
| |
| calculationTest('owners_withChildren', [ |
| [ // GMD. |
| { |
| 'name': 'bitmap', |
| 'guid': 7, |
| 'expected_size': 48, |
| 'expected_effective_size': 17, |
| 'children': [ |
| { |
| 'name': 'subbitmap1', |
| 'size': 32, |
| 'expected_size': 32, |
| 'expected_effective_size': 17 * (32 / 48) |
| }, |
| { |
| 'name': 'subbitmap2', |
| 'size': 16, |
| 'expected_size': 16, |
| 'expected_effective_size': 17 * (16 / 48) |
| } |
| ] |
| } |
| ], |
| [ // PMD. |
| { |
| 'name': 'tile', |
| 'expected_size': 31, |
| 'expected_effective_size': 0, |
| 'guid': 8, |
| 'owns': 7, |
| 'children': [ |
| { |
| 'name': '<unspecified>', |
| 'skip_build': true, |
| 'expected_size': 7, |
| 'expected_effective_size': 0 |
| }, |
| { |
| 'name': 'subtile', |
| 'size': 24, |
| 'expected_size': 24, |
| 'expected_effective_size': 0 |
| } |
| ] |
| }, |
| { |
| 'name': 'cc', |
| 'owns': 8, |
| 'size': 31, |
| 'expected_size': 31, |
| 'expected_effective_size': 31 |
| } |
| ] |
| ]); |
| |
| calculationTest('owners_withParents', [ |
| [ // GMD. |
| { |
| 'name': 'bitmap', |
| 'size': 96, |
| 'expected_size': 96, |
| 'expected_effective_size': 32, |
| 'children': [ |
| { |
| 'name': '<unspecified>', |
| 'skip_build': true, |
| 'expected_size': 32, |
| 'expected_effective_size': 32 |
| }, |
| { |
| 'name': 'subbitmap', |
| 'guid': 2, |
| 'expected_size': 64, |
| 'expected_effective_size': 0 |
| } |
| ] |
| } |
| ], |
| [ // PMD. |
| { |
| 'name': 'tile', |
| 'expected_size': 64, |
| 'expected_effective_size': 0, |
| 'children': [ |
| { |
| 'name': 'subtile', |
| 'guid': 1, |
| 'owns': 2, |
| 'expected_size': 64, |
| 'expected_effective_size': 0 |
| } |
| ] |
| }, |
| { |
| 'name': 'cc', |
| 'owns': 1, |
| 'size': 64, |
| 'expected_size': 64, |
| 'expected_effective_size': 64 |
| } |
| ] |
| ]); |
| |
| calculationTest('owners_multipleLevels', [ |
| [ // GMD. |
| { |
| 'name': 'bitmap', |
| 'size': 96, |
| 'expected_size': 96, |
| 'expected_effective_size': 32, |
| 'children': [ |
| { |
| 'name': '<unspecified>', |
| 'skip_build': true, |
| 'expected_size': 32, |
| 'expected_effective_size': 32 |
| }, |
| { |
| 'name': 'subbitmap', |
| 'guid': 2, |
| 'expected_size': 64, |
| 'expected_effective_size': 0 |
| } |
| ] |
| } |
| ], |
| [ // PMD. |
| { |
| 'name': 'tile', |
| 'expected_size': 64, |
| 'expected_effective_size': 0, |
| 'owns': 2, |
| 'children': [ |
| { |
| 'name': 'subtile', |
| 'guid': 1, |
| 'expected_size': 64, |
| 'expected_effective_size': 0 |
| } |
| ] |
| }, |
| { |
| 'name': 'cc', |
| 'owns': 1, |
| 'size': 64, |
| 'expected_size': 64, |
| 'expected_effective_size': 64 |
| } |
| ] |
| ]); |
| |
| calculationTest('views_allSizesUndefined', [ |
| [ |
| { |
| 'name': 'v8', |
| 'children': [ |
| { |
| 'name': 'v8/heaps', |
| 'guid': 1 |
| }, |
| { |
| 'name': 'v8/objects', |
| 'owns': 1 |
| } |
| ] |
| } |
| ] |
| ]); |
| |
| calculationTest('views_ownedSizeDefined', [ |
| [ |
| { |
| 'name': 'v8', |
| 'expected_size': 10, |
| 'expected_effective_size': 10, |
| 'children': [ |
| { |
| 'name': 'heaps', |
| 'guid': 1, |
| 'size': 10, |
| 'expected_size': 10, |
| 'expected_effective_size': 10 |
| }, |
| { |
| 'name': 'objects', |
| 'owns': 1 |
| } |
| ] |
| } |
| ] |
| ]); |
| |
| calculationTest('views_ownerSizeDefined', [ |
| [ |
| { |
| 'name': 'v8', |
| 'expected_size': 20, |
| 'expected_effective_size': 20, |
| 'children': [ |
| { |
| 'name': 'heaps', |
| 'guid': 1, |
| 'expected_size': 20, |
| 'expected_effective_size': 0, |
| 'expected_owned_by_sibling_sizes': { |
| 'objects': 20 |
| } |
| }, |
| { |
| 'name': 'objects', |
| 'owns': 1, |
| 'size': 20, |
| 'expected_size': 20, |
| 'expected_effective_size': 20 |
| } |
| ] |
| } |
| ] |
| ]); |
| |
| calculationTest('views_parentSizeUndefined', [ |
| [ |
| { |
| 'name': 'v8', |
| 'expected_size': 30, |
| 'expected_effective_size': 30, |
| 'children': [ |
| { |
| 'name': 'heaps', |
| 'guid': 1, |
| 'size': 30, |
| 'expected_size': 30, |
| 'expected_effective_size': 10, |
| 'expected_owned_by_sibling_sizes': { |
| 'objects': 20 |
| } |
| }, |
| { |
| 'name': 'objects', |
| 'owns': 1, |
| 'size': 20, |
| 'expected_size': 20, |
| 'expected_effective_size': 20 |
| } |
| ] |
| } |
| ] |
| ]); |
| |
| calculationTest('views_ownerSizeUndefined_childrenAddUp', [ |
| [ |
| { |
| 'name': 'v8', |
| 'size': 30, |
| 'expected_size': 30, |
| 'expected_effective_size': 30, |
| 'children': [ |
| { |
| 'name': 'heaps', |
| 'guid': 1, |
| 'size': 30, |
| 'expected_size': 30, |
| 'expected_effective_size': 30 |
| }, |
| { |
| 'name': 'objects', |
| 'owns': 1 |
| } |
| ] |
| } |
| ] |
| ]); |
| |
| calculationTest('views_ownerSizeUndefined_childrenDontAddUp', [ |
| [ |
| { |
| 'name': 'v8', |
| 'size': 40, |
| 'expected_size': 40, |
| 'expected_effective_size': 40, |
| 'children': [ |
| { |
| 'name': '<unspecified>', |
| 'skip_build': true, |
| 'expected_size': 10, |
| 'expected_effective_size': 10 |
| }, |
| { |
| 'name': 'heaps', |
| 'guid': 1, |
| 'size': 30, |
| 'expected_size': 30, |
| 'expected_effective_size': 30 |
| }, |
| { |
| 'name': 'objects', |
| 'owns': 1 |
| } |
| ] |
| } |
| ] |
| ]); |
| |
| calculationTest('views_ownedSizeUndefined_childrenAddUp', [ |
| [ |
| { |
| 'name': 'v8', |
| 'size': 30, |
| 'expected_size': 30, |
| 'expected_effective_size': 30, |
| 'children': [ |
| { |
| 'name': 'heaps', |
| 'guid': 1, |
| 'expected_size': 30, |
| 'expected_effective_size': 0, |
| 'expected_owned_by_sibling_sizes': { |
| 'objects': 30 |
| } |
| }, |
| { |
| 'name': 'objects', |
| 'owns': 1, |
| 'size': 30, |
| 'expected_size': 30, |
| 'expected_effective_size': 30 |
| } |
| ] |
| } |
| ] |
| ]); |
| |
| calculationTest('views_ownedSizeUndefined_childrenDontAddUp', [ |
| [ |
| { |
| 'name': 'v8', |
| 'size': 40, |
| 'expected_size': 40, |
| 'expected_effective_size': 40, |
| 'children': [ |
| { |
| 'name': '<unspecified>', |
| 'skip_build': true, |
| 'expected_size': 10, |
| 'expected_effective_size': 10 |
| }, |
| { |
| 'name': 'heaps', |
| 'guid': 1, |
| 'expected_size': 30, |
| 'expected_effective_size': 0, |
| 'expected_owned_by_sibling_sizes': { |
| 'objects': 30 |
| } |
| }, |
| { |
| 'name': 'objects', |
| 'owns': 1, |
| 'size': 30, |
| 'expected_size': 30, |
| 'expected_effective_size': 30 |
| } |
| ] |
| } |
| ] |
| ]); |
| |
| calculationTest('views_allSizesDefined_childrenAddUp', [ |
| [ |
| { |
| 'name': 'v8', |
| 'size': 30, |
| 'expected_size': 30, |
| 'expected_effective_size': 30, |
| 'children': [ |
| { |
| 'name': 'heaps', |
| 'guid': 1, |
| 'size': 30, |
| 'expected_size': 30, |
| 'expected_effective_size': 16, |
| 'expected_owned_by_sibling_sizes': { |
| 'objects': 14 |
| } |
| }, |
| { |
| 'name': 'objects', |
| 'owns': 1, |
| 'size': 14, |
| 'expected_size': 14, |
| 'expected_effective_size': 14 |
| } |
| ] |
| } |
| ] |
| ]); |
| |
| calculationTest('views_allSizesDefined_childrenDontAddUp', [ |
| [ |
| { |
| 'name': 'v8', |
| 'size': 35, |
| 'expected_size': 35, |
| 'expected_effective_size': 35, |
| 'children': [ |
| { |
| 'name': '<unspecified>', |
| 'skip_build': true, |
| 'expected_size': 5, |
| 'expected_effective_size': 5 |
| }, |
| { |
| 'name': 'heaps', |
| 'guid': 1, |
| 'size': 30, |
| 'expected_size': 30, |
| 'expected_effective_size': 16, |
| 'expected_owned_by_sibling_sizes': { |
| 'objects': 14 |
| } |
| }, |
| { |
| 'name': 'objects', |
| 'owns': 1, |
| 'size': 14, |
| 'expected_size': 14, |
| 'expected_effective_size': 14 |
| } |
| ] |
| } |
| ] |
| ]); |
| |
| calculationTest('views_deep', [ |
| [ |
| { |
| 'name': 'root', |
| 'expected_size': 17, |
| 'expected_effective_size': 17, |
| 'children': [ |
| { |
| 'name': 'parent1', |
| 'size': 10, |
| 'expected_size': 10, |
| 'expected_effective_size': 5, |
| 'expected_owned_by_sibling_sizes': { |
| 'parent2': 5 |
| }, |
| 'children': [ |
| { |
| 'name': '<unspecified>', |
| 'skip_build': true, |
| 'expected_size': 2, |
| 'expected_effective_size': 2 |
| }, |
| { |
| 'name': 'child', |
| 'guid': 1, |
| 'size': 8, |
| 'expected_size': 8, |
| 'expected_effective_size': 3 |
| } |
| ] |
| }, |
| { |
| 'name': 'parent2', |
| 'size': 8, |
| 'expected_size': 8, |
| 'expected_effective_size': 5, |
| 'expected_owned_by_sibling_sizes': { |
| 'parent3': 3 |
| }, |
| 'children': [ |
| { |
| 'name': '<unspecified>', |
| 'skip_build': true, |
| 'expected_size': 3, |
| 'expected_effective_size': 3 |
| }, |
| { |
| 'name': 'child', |
| 'guid': 2, |
| 'owns': 1, |
| 'size': 5, |
| 'expected_size': 5, |
| 'expected_effective_size': 2 |
| } |
| ] |
| }, |
| { |
| 'name': 'parent3', |
| 'size': 7, |
| 'expected_size': 7, |
| 'expected_effective_size': 7, |
| 'children': [ |
| { |
| 'name': '<unspecified>', |
| 'skip_build': true, |
| 'expected_size': 4, |
| 'expected_effective_size': 4 |
| }, |
| { |
| 'name': 'child', |
| 'owns': 2, |
| 'size': 3, |
| 'expected_size': 3, |
| 'expected_effective_size': 3 |
| } |
| ] |
| } |
| ] |
| } |
| ] |
| ]); |
| |
| calculationTest('views_nested', [ |
| [ |
| { |
| 'name': 'system', |
| 'expected_size': 7, |
| 'expected_effective_size': 7, |
| 'children': [ |
| { |
| 'name': 'subsystem-A', |
| 'owns': 15, |
| 'expected_size': 5, |
| 'expected_effective_size': 5, |
| 'children': [ |
| { |
| 'name': 'objects', |
| 'owns': 30, |
| 'size': 3, |
| 'expected_size': 3, |
| 'expected_effective_size': 3 |
| }, |
| { |
| 'name': 'heaps', |
| 'guid': 30, |
| 'size': 5, |
| 'expected_size': 5, |
| 'expected_effective_size': 2, |
| 'expected_owned_by_sibling_sizes': { |
| 'objects': 3 |
| } |
| } |
| ] |
| }, |
| { |
| 'name': 'subsystem-B', |
| 'guid': 15, |
| 'expected_size': 7, |
| 'expected_effective_size': 2, |
| 'expected_owned_by_sibling_sizes': { |
| 'subsystem-A': 5 |
| }, |
| 'children': [ |
| { |
| 'name': 'objects', |
| 'owns': 40, |
| 'size': 7, |
| 'expected_size': 7, |
| 'expected_effective_size': 2 |
| }, |
| { |
| 'name': 'heaps', |
| 'guid': 40, |
| 'expected_size': 7, |
| 'expected_effective_size': 0, |
| 'expected_owned_by_sibling_sizes': { |
| 'objects': 7 |
| } |
| } |
| ] |
| } |
| ] |
| } |
| ] |
| ]); |
| |
| calculationTest('importance_equal', [ |
| [ // GMD (both importances undefined and equal sizes). |
| { |
| 'name': 'owned', |
| 'guid': 1, |
| 'size': 10, |
| 'expected_size': 10, |
| 'expected_effective_size': 4 |
| }, |
| { |
| 'name': 'owner1', |
| 'owns': 1, |
| 'size': 6, |
| 'expected_size': 6, |
| 'expected_effective_size': 3 |
| }, |
| { |
| 'name': 'owner2', |
| 'owns': 1, |
| 'size': 6, |
| 'expected_size': 6, |
| 'expected_effective_size': 3 |
| } |
| ], |
| [ // PMD1 (only one importance defined and different sizes). |
| { |
| 'name': 'owned', |
| 'guid': 2, |
| 'size': 20, |
| 'expected_size': 20, |
| 'expected_effective_size': 5 |
| }, |
| { |
| 'name': 'owner1', |
| 'owns': 2, |
| 'importance': 0, |
| 'size': 15, |
| 'expected_size': 15, |
| 'expected_effective_size': 10 / 2 + 5 |
| }, |
| { |
| 'name': 'owner2', |
| 'owns': 2, |
| 'size': 10, |
| 'expected_size': 10, |
| 'expected_effective_size': 10 / 2 |
| } |
| ], |
| [ // PMD2 (all importances defined and different sizes). |
| { |
| 'name': 'owned', |
| 'guid': 3, |
| 'size': 15, |
| 'expected_size': 15, |
| 'expected_effective_size': 5 |
| }, |
| { |
| 'name': 'owner1', |
| 'owns': 3, |
| 'importance': 3, |
| 'size': 8, |
| 'expected_size': 8, |
| 'expected_effective_size': 8 / 3 |
| }, |
| { |
| 'name': 'owner2', |
| 'owns': 3, |
| 'importance': 3, |
| 'size': 9, |
| 'expected_size': 9, |
| 'expected_effective_size': 8 / 3 + 1 / 2 |
| }, |
| { |
| 'name': 'owner3', |
| 'owns': 3, |
| 'importance': 3, |
| 'size': 10, |
| 'expected_size': 10, |
| 'expected_effective_size': 8 / 3 + 1 / 2 + 1 |
| } |
| ] |
| ]); |
| |
| calculationTest('importance_notEqual', [ |
| [ // GMD (one importance undefined and equal sizes). |
| { |
| 'name': 'owned', |
| 'guid': 1, |
| 'size': 10, |
| 'expected_size': 10, |
| 'expected_effective_size': 4 |
| }, |
| { |
| 'name': 'owner1', |
| 'owns': 1, |
| 'size': 6, |
| 'expected_size': 6, |
| 'expected_effective_size': 0 |
| }, |
| { |
| 'name': 'owner2', |
| 'owns': 1, |
| 'importance': 1, |
| 'size': 6, |
| 'expected_size': 6, |
| 'expected_effective_size': 6 |
| } |
| ], |
| [ // PMD1 (one importance undefined and different sizes). |
| { |
| 'name': 'owned', |
| 'guid': 2, |
| 'size': 20, |
| 'expected_size': 20, |
| 'expected_effective_size': 4 |
| }, |
| { |
| 'name': 'owner1', |
| 'owns': 2, |
| 'importance': -1, |
| 'size': 16, |
| 'expected_size': 16, |
| 'expected_effective_size': 6 |
| }, |
| { |
| 'name': 'owner2', |
| 'owns': 2, |
| 'size': 10, |
| 'expected_size': 10, |
| 'expected_effective_size': 10 |
| } |
| ], |
| [ // PMD2 (all importances defined and different sizes). |
| { |
| 'name': 'owned', |
| 'guid': 3, |
| 'size': 15, |
| 'expected_size': 15, |
| 'expected_effective_size': 5 |
| }, |
| { |
| 'name': 'owner1', |
| 'owns': 3, |
| 'importance': 4, |
| 'size': 8, |
| 'expected_size': 8, |
| 'expected_effective_size': 8 |
| }, |
| { |
| 'name': 'owner2', |
| 'owns': 3, |
| 'importance': 3, |
| 'size': 6, |
| 'expected_size': 6, |
| 'expected_effective_size': 0 |
| }, |
| { |
| 'name': 'owner3', |
| 'owns': 3, |
| 'importance': 2, |
| 'size': 10, |
| 'expected_size': 10, |
| 'expected_effective_size': 2 |
| } |
| ] |
| ]); |
| |
| // Example taken from GlobalMemoryDump.calculateDumpOwnershipCoefficient_() |
| // documentation. |
| calculationTest('importance_manyOwners', [ |
| [ // GMD. |
| { |
| 'name': 'owned', |
| 'guid': 4, |
| 'size': 10, |
| 'expected_size': 10, |
| 'expected_effective_size': 2 |
| } |
| ], |
| [ // PMD1. |
| { |
| 'name': 'owner1', |
| 'owns': 4, |
| 'importance': 2, |
| 'size': 6, |
| 'expected_size': 6, |
| 'expected_effective_size': 6 / 2 |
| } |
| ], |
| [ // PMD2. |
| { |
| 'name': 'some_parent', |
| 'expected_size': 7, |
| 'expected_effective_size': 6 / 2 + 1, |
| 'children': [ |
| { |
| 'name': 'owner2', |
| 'owns': 4, |
| 'importance': 2, |
| 'size': 7, |
| 'expected_size': 7, |
| 'expected_effective_size': 6 / 2 + 1 |
| } |
| ] |
| } |
| ], |
| [ // PMD3. |
| { |
| 'name': 'owner3', |
| 'owns': 4, |
| 'importance': 1, |
| 'size': 5, |
| 'expected_size': 5, |
| 'expected_effective_size': 0 |
| }, |
| { |
| 'name': 'owner4', |
| 'owns': 4, |
| 'importance': 0, |
| 'size': 8, |
| 'expected_size': 8, |
| 'expected_effective_size': 1 |
| } |
| ] |
| ]); |
| |
| calculationTest('importance_chainOwnerships', [ |
| [ // GMD. |
| { |
| 'name': 'owned', |
| 'guid': 5, |
| 'size': 10, |
| 'expected_size': 10, |
| 'expected_effective_size': 2 |
| } |
| ], |
| [ // PMD1. |
| { |
| 'name': 'owner1', |
| 'owns': 5, |
| 'importance': 2, |
| 'guid': 6, |
| 'size': 6, |
| 'expected_size': 6, |
| 'expected_effective_size': 2 |
| }, |
| { |
| 'name': 'subowner1', |
| 'owns': 6, |
| 'size': 4, |
| 'expected_size': 4, |
| 'expected_effective_size': 4 |
| } |
| ], |
| [ // PMD2. |
| { |
| 'name': 'owner2', |
| 'owns': 5, |
| 'importance': 1, |
| 'guid': 8, |
| 'size': 8, |
| 'expected_size': 8, |
| 'expected_effective_size': 2 - 2 / 4 |
| }, |
| { |
| 'name': 'subowner2', |
| 'owns': 8, |
| 'size': 2, |
| 'expected_size': 2, |
| 'expected_effective_size': 2 / 4 |
| } |
| ] |
| ]); |
| |
| calculationTest('importance_nested', [ |
| [ |
| { |
| 'name': 'grey', |
| 'guid': 15, |
| 'size': 20, |
| 'expected_size': 20, |
| 'expected_effective_size': 6 |
| }, |
| { |
| 'name': 'blue', |
| 'guid': 18, |
| 'owns': 15, |
| 'importance': 1, |
| 'size': 14, |
| 'expected_size': 14, |
| 'expected_effective_size': 1 |
| }, |
| { |
| 'name': 'purple', |
| 'owns': 15, |
| 'importance': 2, |
| 'size': 7, |
| 'expected_size': 7, |
| 'expected_effective_size': 7 |
| }, |
| { |
| 'name': 'yellow', |
| 'owns': 21, |
| 'importance': 3, |
| 'size': 10, |
| 'expected_size': 10, |
| 'expected_effective_size': 3 |
| }, |
| { |
| 'name': 'red', |
| 'guid': 21, |
| 'owns': 18, |
| 'size': 12, |
| 'expected_size': 12, |
| 'expected_effective_size': 1 |
| }, |
| { |
| 'name': 'green', |
| 'owns': 21, |
| 'importance': 3, |
| 'size': 8, |
| 'expected_size': 8, |
| 'expected_effective_size': 2 |
| } |
| ] |
| ]); |
| |
| calculationTest('importance_singleRefinement', [ |
| [ |
| { |
| 'name': 'v8', |
| 'expected_size': 13, |
| 'expected_effective_size': 13, |
| 'children': [ |
| { |
| 'name': 'objects', |
| 'owns': 1, |
| 'size': 11, |
| 'expected_size': 11, |
| 'expected_effective_size': 11, |
| 'children': [ |
| { |
| 'name': '<unspecified>', |
| 'skip_build': true, |
| 'expected_size': 4, |
| 'expected_effective_size': 4 |
| }, |
| { |
| 'name': 'object1', |
| 'owns': 2, |
| 'size': 7, |
| 'expected_size': 7, |
| 'expected_effective_size': 7 |
| } |
| ] |
| }, |
| { |
| 'name': 'heaps', |
| 'guid': 1, |
| 'size': 13, |
| 'expected_size': 13, |
| 'expected_effective_size': 2, |
| 'expected_owned_by_sibling_sizes': { |
| 'objects': 11 |
| }, |
| 'children': [ |
| { |
| 'name': '<unspecified>', |
| 'skip_build': true, |
| 'expected_size': 3, |
| 'expected_effective_size': 1 |
| }, |
| { |
| 'name': 'heap1', |
| 'guid': 2, |
| 'size': 10, |
| 'expected_size': 10, |
| 'expected_effective_size': 1, |
| } |
| ] |
| } |
| ] |
| } |
| ] |
| ]); |
| |
| calculationTest('importance_sharedRefinement', [ |
| [ // GMD. |
| { |
| 'name': 'shared_bitmap', |
| 'guid': 0, |
| 'size': 23, |
| 'expected_size': 23, |
| 'expected_effective_size': 5, |
| 'children': [ |
| { |
| 'name': '<unspecified>', |
| 'skip_build': true, |
| 'expected_size': 13, |
| 'expected_effective_size': 13 * 5 / (13 + 3) |
| }, |
| { |
| 'name': 'bitmap0x7', |
| 'guid': 999, |
| 'size': 10, |
| 'expected_size': 10, |
| 'expected_effective_size': 3 * 5 / (13 + 3), |
| } |
| ] |
| } |
| ], |
| [ // PMD1. |
| { |
| 'name': 'tile_manager', |
| 'owns': 0, |
| 'importance': 2, |
| 'size': 12, |
| 'expected_size': 12, |
| 'expected_effective_size': 5 + 2, |
| 'children': [ |
| { |
| 'name': '<unspecified>', |
| 'skip_build': true, |
| 'expected_size': 5, |
| 'expected_effective_size': 5 |
| }, |
| { |
| 'name': 'tile42', |
| 'owns': 999, |
| 'importance': 1, |
| 'size': 7, |
| 'expected_size': 7, |
| 'expected_effective_size': 2, |
| } |
| ] |
| } |
| ], |
| [ // PMD2. |
| { |
| 'name': 'gpu', |
| 'owns': 0, |
| 'importance': 1, |
| 'size': 16, |
| 'expected_size': 16, |
| 'expected_effective_size': 6 + 5, |
| 'children': [ |
| { |
| 'name': '<unspecified>', |
| 'skip_build': true, |
| 'expected_size': 11, |
| 'expected_effective_size': 6 |
| }, |
| { |
| 'name': 'chunk-3.14', |
| 'owns': 999, |
| 'importance': 2, |
| 'size': 5, |
| 'expected_size': 5, |
| 'expected_effective_size': 5, |
| } |
| ] |
| } |
| ] |
| ]); |
| |
| // Example taken from https://goo.gl/fKg0dt. |
| calculationTest('documentationExample', [ |
| [ // GMD, Global (shared) memory. |
| { |
| 'name': 'unknown', |
| 'guid': 2, |
| 'expected_size': 16, |
| 'expected_effective_size': 0, |
| } |
| ], |
| [ // PMD1, Browser process. |
| { |
| 'name': 'sharedbitmap', |
| 'size': 17, |
| 'expected_size': 17, |
| 'expected_effective_size': 9, |
| 'children': [ |
| { |
| 'name': '<unspecified>', |
| 'skip_build': true, |
| 'expected_size': 1, |
| 'expected_effective_size': 1 |
| }, |
| { |
| 'name': '0x7', |
| 'size': 16, |
| 'expected_size': 16, |
| 'expected_effective_size': 8, |
| 'owns': 2, |
| 'importance': 1, |
| 'children': [ |
| { |
| 'name': '<unspecified>', |
| 'skip_build': true, |
| 'expected_size': 16, |
| 'expected_effective_size': 8 |
| }, |
| { |
| 'name': 'y' |
| } |
| ] |
| } |
| ] |
| } |
| ], |
| [ // PMD2, Renderer process. |
| { |
| 'name': 'v8', |
| 'expected_size': 13, |
| 'expected_effective_size': 13, |
| 'children': [ |
| { |
| 'name': 'heaps', |
| 'guid': 100, |
| 'expected_size': 12, |
| 'expected_effective_size': 3, |
| 'expected_owned_by_sibling_sizes': { |
| 'objects': 9 |
| }, |
| 'children': [ |
| { |
| 'name': '1', |
| 'size': 8, |
| 'expected_size': 8, |
| 'expected_effective_size': 2, |
| 'owns': 2, |
| 'importance': 2 |
| }, |
| { |
| 'name': '2', |
| 'expected_size': 4, |
| 'expected_effective_size': 1, |
| 'size': 4 |
| } |
| ] |
| }, |
| { |
| 'name': 'objects', |
| 'size': 10, |
| 'expected_size': 10, |
| 'expected_effective_size': 10, |
| 'children': [ |
| { |
| 'name': '<unspecified>', |
| 'skip_build': true, |
| 'expected_size': 1, |
| 'expected_effective_size': 1 |
| }, |
| { |
| 'name': 'strings', |
| 'size': 9, |
| 'expected_size': 9, |
| 'expected_effective_size': 9, |
| 'owns': 100 |
| } |
| ] |
| } |
| ] |
| } |
| ] |
| ]); |
| |
| // This should never happen. Nevertheless, this test checks that we can |
| // handle invalid sizes (parent dump being smaller than its aggregated |
| // children and owned dump being smaller than its largest owner) gracefully. |
| calculationTest('invalidSizes', [ |
| [ |
| { |
| 'name': 'root1', |
| 'size': 24, |
| 'expected_size': 24, |
| 'expected_effective_size': 4, |
| 'children': [ |
| { |
| 'name': '<unspecified>', |
| 'skip_build': true, |
| 'expected_size': 4, |
| 'expected_effective_size': 4 |
| }, |
| { |
| 'name': 'parent', |
| 'guid': 2, |
| 'size': 17, // Invalid: child has larger size. |
| 'expected_size': 20, |
| 'expected_infos': [ |
| { |
| type: PROVIDED_SIZE_LESS_THAN_AGGREGATED_CHILDREN, |
| providedSize: 17, |
| dependencySize: 20 |
| }, |
| { |
| type: PROVIDED_SIZE_LESS_THAN_LARGEST_OWNER, |
| providedSize: 17, |
| dependencySize: 18 |
| } |
| ], |
| 'expected_effective_size': 0, |
| 'children': [ |
| { |
| 'name': 'child', |
| 'guid': 1, |
| 'size': 10, // Invalid: owner has larger size. |
| 'expected_size': 20, |
| 'expected_infos': [ |
| { |
| type: PROVIDED_SIZE_LESS_THAN_LARGEST_OWNER, |
| providedSize: 10, |
| dependencySize: 20 |
| } |
| ], |
| 'expected_effective_size': 0, |
| } |
| ] |
| } |
| ] |
| }, |
| { |
| 'name': 'root2', |
| 'owns': 1, |
| 'size': 20, |
| 'expected_size': 20, |
| 'expected_effective_size': 20 |
| }, |
| { |
| 'name': 'root3', |
| 'owns': 2, |
| 'importance': -1, |
| 'size': 18, |
| 'expected_size': 18, |
| 'expected_effective_size': 18 |
| } |
| ] |
| ]); |
| |
| calculationTest('multipleInfos', [ |
| [ |
| { |
| 'name': 'root', |
| 'expected_size': 10, |
| 'expected_effective_size': 10, |
| 'children': [ |
| { |
| 'name': 'parent1', |
| 'size': 5, |
| 'expected_size': 10, |
| 'expected_infos': [ |
| { |
| type: PROVIDED_SIZE_LESS_THAN_AGGREGATED_CHILDREN, |
| providedSize: 5, |
| dependencySize: 10 |
| } |
| ], |
| 'expected_effective_size': 1, |
| 'expected_owned_by_sibling_sizes': { |
| 'parent2': 17, |
| 'parent3': 7 |
| }, |
| 'children': [ |
| { |
| 'name': 'child', |
| 'guid': 3, |
| 'size': 10, |
| 'expected_size': 10, |
| 'expected_effective_size': 1, |
| } |
| ] |
| }, |
| { |
| 'name': 'parent2', |
| // NOTE(petrcermak): The expected size here is a little strange |
| // because the children both own the same dump (namely |
| // root/parent1/child). It would, therefore, probably make more |
| // sense for the calculated size to be 9. Since this is an unlikely |
| // case and would complicate the (already complex) size |
| // calculation, we will now keep the algorithm as is. |
| 'expected_size': 17, |
| 'expected_effective_size': 14 / 3 + 2, |
| 'children': [ |
| { |
| 'name': 'child1', |
| 'owns': 3, |
| 'size': 9, |
| 'expected_size': 9, |
| 'expected_effective_size': 7 / 3 + 1 / 2 + 1, |
| }, |
| { |
| 'name': 'child2', |
| 'owns': 3, |
| 'size': 8, |
| 'expected_size': 8, |
| 'expected_effective_size': 7 / 3 + 1 / 2, |
| } |
| ] |
| }, |
| { |
| 'name': 'parent3', |
| 'size': 7, |
| 'expected_size': 7, |
| 'expected_effective_size': 7 / 3, |
| 'owns': 3 |
| } |
| ] |
| } |
| ] |
| ]); |
| |
| // Check that size calculation is NOT preceded by numeric aggregation, which |
| // would recursively sum up size numerics. |
| test('finalizeGraph_aggregation', function() { |
| var model = tr.c.TestUtils.newModel(function(model) { |
| buildDumpTrees([ |
| undefined, // GMD. |
| [ // PMD. |
| { |
| 'name': 'root', |
| 'children': [ |
| { |
| 'name': 'owner_child', |
| 'owns': 9, |
| 'size': 7 |
| }, |
| { |
| 'name': 'owned_child', |
| 'guid': 9, |
| 'size': 20 |
| } |
| ] |
| } |
| ] |
| ], model); |
| }); |
| var pmd = model.getProcess(0).memoryDumps[0]; |
| |
| var rootDump = pmd.getMemoryAllocatorDumpByFullName('root'); |
| assertDumpSizes(rootDump, 20, 20); |
| |
| var ownerChildDump = pmd.getMemoryAllocatorDumpByFullName( |
| 'root/owner_child'); |
| assertDumpSizes(ownerChildDump, 7, 7); |
| |
| var ownedChildDump = pmd.getMemoryAllocatorDumpByFullName( |
| 'root/owned_child'); |
| assertDumpSizes(ownedChildDump, 20, 13, [] /* expectedInfos */, |
| {'owner_child': 7} /* expectedOwnedBySiblingSizes */); |
| }); |
| |
| // Check that numeric and diagnostics propagation and aggregation are |
| // performed in the correct order. |
| test('finalizeGraph_propagation', function() { |
| var model = tr.c.TestUtils.newModel(function(model) { |
| buildDumpTrees([ |
| [ // GMD. |
| { |
| 'name': 'owned_root', |
| 'guid': 1, |
| 'size': 10, |
| 'diagnostics': { |
| 'url': 'https://hello.world.com:42' |
| }, |
| 'children': [ |
| { |
| 'name': 'owned_child1', |
| 'numerics': { |
| 'summed': new ScalarNumeric(sizeInBytes_smallerIsBetter, 12) |
| }, |
| 'diagnostics': { |
| 'url2': 'http://not.aggregated.to/owned/parent/dump' |
| } |
| }, |
| { |
| 'name': 'owned_child2', |
| 'numerics': { |
| 'summed': new ScalarNumeric(sizeInBytes_smallerIsBetter, 15) |
| } |
| } |
| ] |
| } |
| ], |
| [ // PMD. |
| { |
| 'name': 'direct_owner', |
| 'owns': 1, |
| 'guid': 2, |
| 'diagnostics': { |
| 'url': 'file://not_overriden.html' |
| } |
| }, |
| { |
| 'name': 'parent_owner', |
| 'children': [ |
| { |
| 'name': 'child_owner', |
| 'owns': 1 |
| }, |
| { |
| 'name': 'sibling', |
| 'size': 5, |
| 'numerics': { |
| 'summed': new ScalarNumeric(sizeInBytes_smallerIsBetter, 13) |
| } |
| } |
| ] |
| }, |
| { |
| 'name': 'precedent_owner', |
| 'owns': 1, |
| 'numerics': { |
| 'summed': new ScalarNumeric(sizeInBytes_smallerIsBetter, 0) |
| } |
| }, |
| { |
| 'name': 'indirect_owner', |
| 'owns': 2 |
| } |
| ] |
| ], model); |
| }); |
| var pmd = model.getProcess(0).memoryDumps[0]; |
| |
| checkDumpNumericsAndDiagnostics( |
| pmd.getMemoryAllocatorDumpByFullName('direct_owner'), |
| { |
| size: 10, |
| effective_size: 3.3333, |
| summed: 27 |
| }, |
| { |
| url: 'file://not_overriden.html' |
| }); |
| checkDumpNumericsAndDiagnostics( |
| pmd.getMemoryAllocatorDumpByFullName('parent_owner/child_owner'), |
| { |
| size: 10, |
| effective_size: 3.3333, |
| summed: 27 |
| }, |
| { |
| url: 'https://hello.world.com:42' |
| }); |
| checkDumpNumericsAndDiagnostics( |
| pmd.getMemoryAllocatorDumpByFullName('parent_owner'), |
| { |
| size: 15, |
| effective_size: 8.3333, |
| summed: 40 |
| }, {}); |
| checkDumpNumericsAndDiagnostics( |
| pmd.getMemoryAllocatorDumpByFullName('precedent_owner'), |
| { |
| size: 10, |
| effective_size: 3.3333, |
| summed: 0 |
| }, |
| { |
| url: 'https://hello.world.com:42' |
| }); |
| checkDumpNumericsAndDiagnostics( |
| pmd.getMemoryAllocatorDumpByFullName('indirect_owner'), {}, {}); |
| }); |
| |
| // Check that weak dumps are removed before size size calculation. |
| test('finalizeGraph_weakDumpRemoval', function() { |
| var model = tr.c.TestUtils.newModel(function(model) { |
| buildDumpTrees([ |
| undefined, // GMD. |
| [ // PMD. |
| { |
| 'name': 'root', |
| 'children': [ |
| { |
| 'name': 'directly_weak_child', |
| 'weak': true, |
| 'guid': 5, |
| 'owns': 10, |
| 'size': 100 |
| }, |
| { |
| 'name': 'strong_child', |
| 'guid': 10, |
| 'size': 120 |
| }, |
| { |
| 'name': 'indirectly_weak_child', |
| 'owns': 5, |
| 'size': 70 |
| }, |
| { |
| 'name': 'separate_weak_child', |
| 'weak': true, |
| 'size': 300 |
| } |
| ] |
| } |
| ] |
| ], model); |
| }); |
| var pmd = model.getProcess(0).memoryDumps[0]; |
| |
| var rootDump = pmd.getMemoryAllocatorDumpByFullName('root'); |
| assertDumpSizes(rootDump, 120, 120); |
| assert.lengthOf(rootDump.children, 1); |
| |
| var strongChildDump = pmd.getMemoryAllocatorDumpByFullName( |
| 'root/strong_child'); |
| assertDumpSizes(strongChildDump, 120, 120); |
| assert.lengthOf(strongChildDump.ownedBy, 0); |
| |
| assert.isUndefined(pmd.getMemoryAllocatorDumpByFullName( |
| 'root/directly_weak_child')); |
| assert.isUndefined(pmd.getMemoryAllocatorDumpByFullName( |
| 'root/indirectly_weak_child')); |
| assert.isUndefined(pmd.getMemoryAllocatorDumpByFullName( |
| 'root/separate_weak_child')); |
| }); |
| |
| test('indicesUpdatedCorrectly', function() { |
| var gmd, rootDump, childDump; |
| var model = tr.c.TestUtils.newModel(function(model) { |
| gmd = new GlobalMemoryDump(model, 10); |
| model.globalMemoryDumps.push(gmd); |
| |
| rootDump = newAllocatorDump(gmd, 'root', {numerics: {size: 64}}); |
| childDump = addChildDump(rootDump, 'child', {numerics: {size: 48}}); |
| |
| gmd.memoryAllocatorDumps = [rootDump]; |
| |
| // Before model is finalized. |
| assert.strictEqual( |
| gmd.getMemoryAllocatorDumpByFullName('root'), rootDump); |
| assert.strictEqual( |
| gmd.getMemoryAllocatorDumpByFullName('root/child'), childDump); |
| assert.isUndefined( |
| gmd.getMemoryAllocatorDumpByFullName('root/<unspecified>')); |
| }); |
| |
| // Test sanity check. |
| assert.isDefined(gmd); |
| assert.isDefined(rootDump); |
| assert.isDefined(childDump); |
| |
| // After model is finalized. |
| assert.strictEqual(gmd.getMemoryAllocatorDumpByFullName('root'), rootDump); |
| assert.strictEqual( |
| gmd.getMemoryAllocatorDumpByFullName('root/child'), childDump); |
| var unspecifiedDump = |
| gmd.getMemoryAllocatorDumpByFullName('root/<unspecified>'); |
| assert.strictEqual(unspecifiedDump.fullName, 'root/<unspecified>'); |
| assert.strictEqual(unspecifiedDump.parent, rootDump); |
| assert.strictEqual(rootDump.children[0], unspecifiedDump); |
| }); |
| |
| weakDumpRemovalTest('allDumpsNonWeak', [ |
| [ // GMD. |
| { |
| 'name': 'malloc', |
| 'children': [ |
| { |
| 'name': 'allocated_objects', |
| 'children': [ |
| { |
| 'name': 'obj42', |
| 'guid': 5, |
| 'expected_owned_by_links_count': 2 |
| } |
| ] |
| } |
| ] |
| } |
| ], |
| undefined, // PMD1. |
| [ // PMD2. |
| { |
| 'name': 'oilpan' |
| }, |
| { |
| 'name': 'v8', |
| 'children': [ |
| { |
| 'name': 'heaps', |
| 'children': [ |
| { |
| 'name': 'S', |
| 'owns': 5 |
| }, |
| { |
| 'name': 'L', |
| 'owns': 5 |
| } |
| ] |
| } |
| ] |
| } |
| ] |
| ]); |
| |
| weakDumpRemovalTest('weakRootDump', [ |
| [], // GMD. |
| [ // PMD1. |
| { |
| 'name': 'strong1' |
| }, |
| { |
| 'name': 'weak', |
| 'weak': true, |
| 'expected_removed': true |
| }, |
| { |
| 'name': 'strong2' |
| } |
| ] |
| ]); |
| |
| weakDumpRemovalTest('weakChildDump', [ |
| [ // GMD. |
| { |
| 'name': 'root', |
| 'children': [ |
| { |
| 'name': 'parent', |
| 'children': [ |
| { |
| 'name': 'strong1' |
| }, |
| { |
| 'name': 'weak', |
| 'weak': true, |
| 'expected_removed': true, |
| 'children': [ |
| { |
| 'name': 'implicitly-removed' |
| } |
| ] |
| }, |
| { |
| 'name': 'strong2' |
| } |
| ] |
| } |
| ] |
| } |
| ] |
| ]); |
| |
| weakDumpRemovalTest('transitiveOwnerRemoval', [ |
| [ // GMD. |
| { |
| 'name': 'not-removed-strong-dump', |
| 'guid': 0, |
| 'expected_owned_by_links_count': 1 |
| }, |
| { |
| 'name': 'weak-owned-dump', |
| 'guid': 1, |
| 'owns': 0, |
| 'weak': true, |
| 'expected_removed': true |
| } |
| ], |
| [ // PMD1. |
| { |
| 'name': 'direct-owner-dump', |
| 'guid': 2, |
| 'owns': 1, |
| 'expected_removed': true |
| }, |
| { |
| 'name': 'also-not-removed-strong-dump', |
| 'owns': 0 |
| } |
| ], |
| [ // PMD2. |
| { |
| 'name': 'indirect-owner-dump', |
| 'owns': 2, |
| 'expected_removed': true |
| } |
| ] |
| ]); |
| |
| weakDumpRemovalTest('transitiveDescendantRemoval', [ |
| [ // GMD. |
| { |
| 'name': 'A', |
| 'owns': 10, |
| // A =owns=> B -child-of-> C -> D => E -> F -> G (weak). |
| 'expected_removed': true |
| }, |
| { |
| 'name': 'D', |
| 'owns': 5, |
| 'expected_removed': true, // D =owns=> E -child-of-> F -> G (weak). |
| 'children': [ |
| { |
| 'name': 'C', |
| 'children': [ |
| { |
| 'name': 'B', |
| 'guid': 10 |
| } |
| ] |
| } |
| ] |
| } |
| ], |
| undefined, // PMD1. |
| [ // PMD2. |
| { |
| 'name': 'first-retained-dump', |
| 'children': [ |
| { |
| 'name': 'G', |
| 'weak': true, |
| 'expected_removed': true, |
| 'children': [ |
| { |
| 'name': 'F', |
| 'children': [ |
| { |
| 'name': 'E', |
| 'guid': 5 |
| } |
| ] |
| }, |
| { |
| 'name': 'H', |
| 'children': [ |
| { |
| 'name': 'I', |
| 'children': [ |
| { |
| 'name': 'J', |
| 'owns': 2 |
| } |
| ] |
| } |
| ] |
| } |
| ] |
| } |
| ] |
| } |
| ], |
| [ // PMD3. |
| { |
| 'name': 'second-retained-dump', |
| 'guid': 2, |
| // The only owner (J) is removed because J -child-of-> I -> H -> |
| // G (weak). |
| 'expected_owned_by_links_count': 0 |
| } |
| ] |
| ]); |
| |
| weakDumpRemovalTest('subownerships', [ |
| [ // GMD. |
| { |
| 'name': 'root1', |
| 'owns': 20, |
| 'expected_removed': true, // root1 =owns=> root2 (weak). |
| 'children': [ |
| { |
| 'name': 'child1', |
| 'owns': 2 |
| } |
| ] |
| }, |
| { |
| 'name': 'root2', |
| 'guid': 20, |
| 'owns': 30, |
| 'weak': true, |
| 'expected_removed': true, |
| 'children': [ |
| { |
| 'name': 'child2', |
| 'guid': 2, |
| 'owns': 3 |
| } |
| ] |
| }, |
| { |
| 'name': 'root3', |
| 'guid': 30, |
| 'owns': 40, |
| 'expected_owned_by_links_count': 0, |
| 'children': [ |
| { |
| 'name': 'child3', |
| 'guid': 3, |
| 'owns': 4, |
| 'weak': true, |
| 'expected_removed': true |
| } |
| ] |
| } |
| ], |
| [ // PMD1. |
| { |
| 'name': 'root4', |
| 'guid': 40, |
| 'expected_owned_by_links_count': 1, |
| 'children': [ |
| { |
| 'name': 'child4', |
| 'guid': 4, |
| 'expected_owned_by_links_count': 0 |
| } |
| ] |
| } |
| ], |
| [ // PMD2. |
| { |
| 'name': 'root5', |
| 'owns': 60, |
| 'expected_removed': true, // root5 =owns=> root6 => root7 (weak). |
| 'children': [ |
| { |
| 'name': 'child5', |
| 'owns': 6 |
| } |
| ] |
| }, |
| { |
| 'name': 'root6', |
| 'guid': 60, |
| 'owns': 70, |
| 'expected_removed': true, // root6 =owns=> root7 (weak). |
| 'children': [ |
| { |
| 'name': 'child6', |
| 'guid': 6, |
| 'owns': 7 |
| } |
| ] |
| }, |
| { |
| 'name': 'root7', |
| 'guid': 70, |
| 'owns': 40, |
| 'weak': true, |
| 'expected_removed': true, |
| 'children': [ |
| { |
| 'name': 'child7', |
| 'guid': 7, |
| 'owns': 4 |
| } |
| ] |
| } |
| ] |
| ]); |
| }); |
| </script> |