| <!DOCTYPE html> |
| <!-- |
| Copyright (c) 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/core/test_utils.html"> |
| <link rel="import" href="/tracing/model/global_memory_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/model/vm_region.html"> |
| <link rel="import" href="/tracing/value/numeric.html"> |
| <link rel="import" href="/tracing/value/unit.html"> |
| |
| <script> |
| 'use strict'; |
| |
| tr.b.unittest.testSuite(function() { |
| var GlobalMemoryDump = tr.model.GlobalMemoryDump; |
| var ProcessMemoryDump = tr.model.ProcessMemoryDump; |
| var VMRegion = tr.model.VMRegion; |
| var VMRegionClassificationNode = tr.model.VMRegionClassificationNode; |
| var ScalarNumeric = tr.v.ScalarNumeric; |
| var unitlessNumber_smallerIsBetter = |
| tr.v.Unit.byName.unitlessNumber_smallerIsBetter; |
| var newAllocatorDump = tr.model.MemoryDumpTestUtils.newAllocatorDump; |
| var newChildDump = tr.model.MemoryDumpTestUtils.newChildDump; |
| var addOwnershipLink = tr.model.MemoryDumpTestUtils.addOwnershipLink; |
| var checkDumpNumericsAndDiagnostics = |
| tr.model.MemoryDumpTestUtils.checkDumpNumericsAndDiagnostics; |
| var checkVMRegions = tr.model.MemoryDumpTestUtils.checkVMRegions; |
| |
| function createClassificationNode(opt_sizeInBytes, opt_byteStats) { |
| var node = new VMRegionClassificationNode(); |
| if (opt_sizeInBytes !== undefined || opt_byteStats !== undefined) { |
| node.addRegion(VMRegion.fromDict({ |
| mappedFile: 'mock.so', |
| sizeInBytes: opt_sizeInBytes, |
| byteStats: opt_byteStats |
| })); |
| } |
| return node; |
| } |
| |
| function createProcessMemoryDump(timestamp, model) { |
| var gmd = new GlobalMemoryDump(model, timestamp); |
| model.globalMemoryDumps.push(gmd); |
| var p = model.getOrCreateProcess(123); |
| var pmd = new ProcessMemoryDump(gmd, p, timestamp + 1); |
| gmd.processMemoryDumps[123] = pmd; |
| p.memoryDumps.push(pmd); |
| return pmd; |
| } |
| |
| function createFinalizedProcessMemoryDump(timestamp, opt_createdCallback) { |
| return createFinalizedProcessMemoryDumps([timestamp], function(pmds) { |
| if (opt_createdCallback !== undefined) |
| opt_createdCallback(pmds[0]); |
| })[0]; |
| } |
| |
| function createFinalizedProcessMemoryDumps(timestamps, createdCallback) { |
| var model = tr.c.TestUtils.newModel(function(model) { |
| var pmds = timestamps.map(function(timestamp) { |
| return createProcessMemoryDump(timestamp, model); |
| }); |
| createdCallback(pmds); |
| }); |
| var pmds = model.getProcess(123).memoryDumps; |
| assert.lengthOf(pmds, timestamps.length); |
| return pmds; |
| } |
| |
| test('processMemoryDumps', function() { |
| var pmd = createFinalizedProcessMemoryDump(42); |
| var pmds = pmd.processMemoryDumps; |
| assert.lengthOf(Object.keys(pmds), 1); |
| assert.strictEqual(pmds[123], pmd); |
| }); |
| |
| test('hookUpMostRecentVmRegionsLinks_emptyArray', function() { |
| var dumps = []; |
| ProcessMemoryDump.hookUpMostRecentVmRegionsLinks(dumps); |
| assert.lengthOf(dumps, 0); |
| }); |
| |
| test('hookUpMostRecentVmRegionsLinks_nonEmptyArray', function() { |
| var m = new tr.Model(); |
| |
| // A dump with no VM regions or allocator dumps. |
| var dump1 = createProcessMemoryDump(1, m); |
| |
| // A dump with VM regions and malloc and Oilpan allocator dumps. |
| var dump2 = createProcessMemoryDump(2, m); |
| dump2.vmRegions = createClassificationNode(); |
| dump2.memoryAllocatorDumps = [ |
| newAllocatorDump(dump2, 'oilpan', { |
| size: 1024, |
| objects_count: new ScalarNumeric(unitlessNumber_smallerIsBetter, 7), |
| inner_size: 768 |
| }), |
| newAllocatorDump(dump2, 'v8', { |
| size: 2048, |
| objects_count: new ScalarNumeric(unitlessNumber_smallerIsBetter, 15), |
| inner_size: 1999 |
| }) |
| ]; |
| |
| // A dump with malloc and V8 allocator dumps. |
| var dump3 = createProcessMemoryDump(3, m); |
| dump3.memoryAllocatorDumps = [ |
| newAllocatorDump(dump3, 'malloc', { |
| size: 1024, |
| objects_count: new ScalarNumeric(unitlessNumber_smallerIsBetter, 7), |
| inner_size: 768 |
| }), |
| newAllocatorDump(dump3, 'v8', { |
| size: 2048, |
| objects_count: new ScalarNumeric(unitlessNumber_smallerIsBetter, 15), |
| inner_size: 1999 |
| }) |
| ]; |
| |
| // A dump with VM regions. |
| var dump4 = createProcessMemoryDump(4, m); |
| dump4.vmRegions = createClassificationNode(); |
| |
| var dumps = [dump1, dump2, dump3, dump4]; |
| ProcessMemoryDump.hookUpMostRecentVmRegionsLinks(dumps); |
| |
| assert.lengthOf(dumps, 4); |
| |
| assert.equal(dumps[0], dump1); |
| assert.isUndefined(dump1.mostRecentVmRegions); |
| |
| assert.equal(dumps[1], dump2); |
| assert.equal(dump2.mostRecentVmRegions, dump2.vmRegions); |
| |
| assert.equal(dumps[2], dump3); |
| assert.equal(dump3.mostRecentVmRegions, dump2.vmRegions); |
| |
| assert.equal(dumps[3], dump4); |
| assert.equal(dump4.mostRecentVmRegions, dump4.vmRegions); |
| }); |
| |
| test('checkDiscountTracingOverhead_undefinedFields', function() { |
| var pmd = createFinalizedProcessMemoryDump(42, function(pmd) { |
| pmd.memoryAllocatorDumps = [ |
| newAllocatorDump(pmd, 'v8', { size: 2048 }), |
| newAllocatorDump(pmd, 'tracing', { size: 1024 }) |
| ]; |
| }); |
| |
| assert.isUndefined(pmd.totals); |
| assert.isUndefined(pmd.vmRegions); |
| |
| var v8Dump = pmd.getMemoryAllocatorDumpByFullName('v8'); |
| checkDumpNumericsAndDiagnostics(v8Dump, { |
| 'size': 2048, |
| 'effective_size': 2048 |
| }, {}); |
| |
| var tracingDump = pmd.getMemoryAllocatorDumpByFullName('tracing'); |
| checkDumpNumericsAndDiagnostics(tracingDump, { |
| 'size': 1024, |
| 'effective_size': 1024 |
| }, {}); |
| }); |
| |
| test('checkDiscountTracingOverhead_definedFields', function() { |
| var pmd = createFinalizedProcessMemoryDump(42, function(pmd) { |
| pmd.totals = { residentBytes: 10240 }; |
| pmd.vmRegions = createClassificationNode(6000, { |
| privateDirtyResident: 4096, |
| proportionalResident: 5120, |
| swapped: 1536 |
| }); |
| |
| var mallocDump = newAllocatorDump(pmd, 'malloc', { size: 3072 }); |
| newChildDump(mallocDump, 'allocated_objects', { size: 2560 }); |
| |
| var tracingDump = newAllocatorDump( |
| pmd, 'tracing', { size: 1024, resident_size: 1000 }); |
| |
| pmd.memoryAllocatorDumps = [mallocDump, tracingDump]; |
| }); |
| |
| assert.equal(pmd.totals.residentBytes, 9240); |
| assert.isUndefined(pmd.totals.peakResidentBytes); |
| |
| var vmRegions = pmd.vmRegions; |
| assert.strictEqual(vmRegions.sizeInBytes, 4976); |
| assert.deepEqual(vmRegions.byteStats, { |
| privateDirtyResident: 3096, |
| proportionalResident: 4120, |
| swapped: 1536 |
| }); |
| |
| checkVMRegions(vmRegions, [ |
| { |
| mappedFile: 'mock.so', |
| sizeInBytes: 6000, |
| byteStats: { |
| privateDirtyResident: 4096, |
| proportionalResident: 5120, |
| swapped: 1536 |
| } |
| }, |
| { |
| mappedFile: '[discounted tracing overhead]', |
| sizeInBytes: -1024, |
| byteStats: { |
| privateDirtyResident: -1000, |
| proportionalResident: -1000 |
| } |
| } |
| ]); |
| |
| var mallocDump = pmd.getMemoryAllocatorDumpByFullName('malloc'); |
| checkDumpNumericsAndDiagnostics(mallocDump, { |
| 'size': 3072, |
| 'effective_size': 2048 |
| }, {}); |
| assert.lengthOf( |
| mallocDump.children, 2 /* 'allocated_objects' and '<unspecified>' */); |
| |
| var allocatedObjectsDump = pmd.getMemoryAllocatorDumpByFullName( |
| 'malloc/allocated_objects'); |
| checkDumpNumericsAndDiagnostics(allocatedObjectsDump, { |
| 'size': 2560, |
| 'effective_size': 1536 |
| }, {}); |
| assert.lengthOf( |
| allocatedObjectsDump.children, |
| 2 /* 'tracing_overhead' and '<unspecified>' */); |
| |
| var discountDump = pmd.getMemoryAllocatorDumpByFullName( |
| 'malloc/allocated_objects/tracing_overhead'); |
| assert.strictEqual(discountDump.parent, allocatedObjectsDump); |
| assert.include(allocatedObjectsDump.children, discountDump); |
| checkDumpNumericsAndDiagnostics(discountDump, { |
| 'size': 1024, |
| 'effective_size': 0 |
| }, {}); |
| |
| var tracingDump = pmd.getMemoryAllocatorDumpByFullName('tracing'); |
| checkDumpNumericsAndDiagnostics(tracingDump, { |
| 'size': 1024, |
| 'effective_size': 1024, |
| 'resident_size': 1000 |
| }, {}); |
| assert.strictEqual(tracingDump.owns.target, discountDump); |
| }); |
| |
| test('checkDiscountTracingOverhead_winheap', function() { |
| var pmd = createFinalizedProcessMemoryDump(42, function(pmd) { |
| pmd.memoryAllocatorDumps = [ |
| newAllocatorDump(pmd, 'tracing', { size: 2048 }), |
| newAllocatorDump(pmd, 'winheap', { size: 5120 }) |
| ]; |
| }); |
| |
| assert.isUndefined(pmd.totals); |
| assert.isUndefined(pmd.vmRegions); |
| |
| var winheapDump = pmd.getMemoryAllocatorDumpByFullName('winheap'); |
| checkDumpNumericsAndDiagnostics(winheapDump, { |
| 'size': 5120, |
| 'effective_size': 3072 |
| }, {}); |
| assert.lengthOf(winheapDump.children, |
| 2 /* 'allocated_objects' and '<unspecified>' */); |
| |
| var allocatedObjectsDump = pmd.getMemoryAllocatorDumpByFullName( |
| 'winheap/allocated_objects'); |
| checkDumpNumericsAndDiagnostics(allocatedObjectsDump, { |
| 'size': 2048, |
| 'effective_size': 0 |
| }, {}); |
| assert.lengthOf( |
| allocatedObjectsDump.children, 1 /* 'tracing_overhead' */); |
| |
| var discountDump = pmd.getMemoryAllocatorDumpByFullName( |
| 'winheap/allocated_objects/tracing_overhead'); |
| assert.strictEqual(discountDump.parent, allocatedObjectsDump); |
| assert.include(allocatedObjectsDump.children, discountDump); |
| checkDumpNumericsAndDiagnostics(discountDump, { |
| 'size': 2048, |
| 'effective_size': 0 |
| }, {}); |
| |
| var tracingDump = pmd.getMemoryAllocatorDumpByFullName('tracing'); |
| checkDumpNumericsAndDiagnostics(tracingDump, { |
| 'size': 2048, |
| 'effective_size': 2048 |
| }, {}); |
| assert.strictEqual(tracingDump.owns.target, discountDump); |
| }); |
| |
| test('checkDiscountTracingOverhead_withMostRecentVmRegionsLinks', function() { |
| var pmds = createFinalizedProcessMemoryDumps([42, 90], function(pmds) { |
| pmds[0].totals = { residentBytes: 1000, peakResidentBytes: 2000 }; |
| pmds[0].vmRegions = createClassificationNode(6000, { |
| privateDirtyResident: 4096 |
| }); |
| pmds[0].memoryAllocatorDumps = [ |
| newAllocatorDump(pmds[0], 'tracing', { size: 300, resident_size: 100 }) |
| ]; |
| |
| pmds[1].totals = { peakResidentBytes: 3000 }; |
| pmds[1].memoryAllocatorDumps = [ |
| newAllocatorDump(pmds[0], 'tracing', { resident_size: 200 }) |
| ]; |
| }); |
| |
| // First PMD: Both total resident and private dirty resident size should be |
| // reduced by 100. Virtual size should be reduced by 300. |
| assert.strictEqual(pmds[0].totals.residentBytes, 900); |
| assert.strictEqual(pmds[0].totals.peakResidentBytes, 1900); |
| assert.strictEqual(pmds[0].vmRegions.sizeInBytes, 5700); |
| assert.deepEqual(pmds[0].vmRegions.byteStats, { |
| privateDirtyResident: 3996 |
| }); |
| checkVMRegions(pmds[0].vmRegions, [ |
| { |
| mappedFile: 'mock.so', |
| sizeInBytes: 6000, |
| byteStats: { |
| privateDirtyResident: 4096, |
| } |
| }, |
| { |
| mappedFile: '[discounted tracing overhead]', |
| sizeInBytes: -300, |
| byteStats: { |
| privateDirtyResident: -100 |
| } |
| } |
| ]); |
| assert.strictEqual(pmds[0].mostRecentVmRegions, pmds[0].vmRegions); |
| |
| // Second PMD: Total resident size should be reduced by 200, whereas private |
| // dirty resident size should be reduced by 100 (because it comes from |
| // the VM regions in the first dump). Similarly, virtual size should be |
| // reduced by 300. |
| assert.isUndefined(pmds[1].totals.residentBytes); |
| assert.equal(pmds[1].totals.peakResidentBytes, 2800); |
| assert.isUndefined(pmds[1].vmRegions); |
| assert.strictEqual(pmds[1].mostRecentVmRegions, pmds[0].vmRegions); |
| }); |
| |
| test('checkDiscountTracingOverhead_allDiscountedVmRegionFields', function() { |
| var pmd = createFinalizedProcessMemoryDump(42, function(pmd) { |
| pmd.vmRegions = createClassificationNode(10000, { |
| privateDirtyResident: 4096, |
| proportionalResident: 8192, |
| swapped: 1536 |
| }); |
| pmd.memoryAllocatorDumps = [ |
| newAllocatorDump(pmd, 'tracing', { size: 1000, resident_size: 1024 }) |
| ]; |
| }); |
| |
| var vmRegions = pmd.vmRegions; |
| assert.strictEqual(vmRegions.sizeInBytes, 9000); |
| assert.deepEqual(vmRegions.byteStats, { |
| privateDirtyResident: 3072, |
| proportionalResident: 7168, |
| swapped: 1536 |
| }); |
| checkVMRegions(vmRegions, [ |
| { |
| mappedFile: 'mock.so', |
| sizeInBytes: 10000, |
| byteStats: { |
| privateDirtyResident: 4096, |
| proportionalResident: 8192, |
| swapped: 1536 |
| } |
| }, |
| { |
| mappedFile: '[discounted tracing overhead]', |
| sizeInBytes: -1000, |
| byteStats: { |
| privateDirtyResident: -1024, |
| proportionalResident: -1024 |
| } |
| } |
| ]); |
| }); |
| |
| test('checkDiscountTracingOverhead_twoDiscountedVmRegionField', function() { |
| var pmd = createFinalizedProcessMemoryDump(42, function(pmd) { |
| pmd.vmRegions = createClassificationNode(10000, { |
| privateDirtyResident: 4096, |
| swapped: 1536 |
| }); |
| pmd.memoryAllocatorDumps = [ |
| newAllocatorDump(pmd, 'tracing', { size: 1000, resident_size: 1024 }) |
| ]; |
| }); |
| |
| var vmRegions = pmd.vmRegions; |
| assert.strictEqual(vmRegions.sizeInBytes, 9000); |
| assert.deepEqual(vmRegions.byteStats, { |
| privateDirtyResident: 3072, |
| swapped: 1536 |
| }); |
| checkVMRegions(vmRegions, [ |
| { |
| mappedFile: 'mock.so', |
| sizeInBytes: 10000, |
| byteStats: { |
| privateDirtyResident: 4096, |
| swapped: 1536 |
| } |
| }, |
| { |
| mappedFile: '[discounted tracing overhead]', |
| sizeInBytes: -1000, |
| byteStats: { |
| privateDirtyResident: -1024 |
| } |
| } |
| ]); |
| }); |
| |
| test('checkDiscountTracingOverhead_oneDiscountedVmRegionField', function() { |
| var pmd = createFinalizedProcessMemoryDump(42, function(pmd) { |
| pmd.vmRegions = createClassificationNode(10000); |
| pmd.memoryAllocatorDumps = [ |
| newAllocatorDump(pmd, 'tracing', { size: 1000, resident_size: 1024 }) |
| ]; |
| }); |
| |
| var vmRegions = pmd.vmRegions; |
| assert.strictEqual(vmRegions.sizeInBytes, 9000); |
| assert.deepEqual(vmRegions.byteStats, {}); |
| checkVMRegions(vmRegions, [ |
| { |
| mappedFile: 'mock.so', |
| sizeInBytes: 10000 |
| }, |
| { |
| mappedFile: '[discounted tracing overhead]', |
| sizeInBytes: -1000 |
| } |
| ]); |
| }); |
| |
| test('checkDiscountTracingOverhead_noDiscountedVmRegionFields', function() { |
| var pmd = createFinalizedProcessMemoryDump(42, function(pmd) { |
| pmd.vmRegions = createClassificationNode(undefined, { |
| swapped: 1536 |
| }); |
| pmd.memoryAllocatorDumps = [ |
| newAllocatorDump(pmd, 'tracing', { size: 1000, resident_size: 1024 }) |
| ]; |
| }); |
| |
| var vmRegions = pmd.vmRegions; |
| assert.isUndefined(vmRegions.sizeInBytes); |
| assert.deepEqual(vmRegions.byteStats, { |
| swapped: 1536 |
| }); |
| checkVMRegions(vmRegions, [ |
| { |
| mappedFile: 'mock.so', |
| byteStats: { |
| swapped: 1536 |
| } |
| } |
| ]); |
| }); |
| |
| test('checkDiscountTracingOverhead_existingLink', function() { |
| var pmd = createFinalizedProcessMemoryDump(42, function(pmd) { |
| pmd.totals = { residentBytes: 10240 }; |
| |
| pmd.vmRegions = createClassificationNode(6000, { |
| privateDirtyResident: 4096, |
| swapped: 1536, |
| proportionalResident: 5120 |
| }); |
| |
| var mallocDump = newAllocatorDump(pmd, 'malloc', { size: 3072 }); |
| var tracingDump = newAllocatorDump(pmd, 'tracing', |
| { size: 1024, resident_size: 1000 }); |
| var ownedDump = newAllocatorDump(pmd, 'owned'); |
| |
| // The code for discounting tracing overhead should *not* override an |
| // existing ownership. |
| addOwnershipLink(tracingDump, ownedDump); |
| |
| pmd.memoryAllocatorDumps = [mallocDump, tracingDump, ownedDump]; |
| }); |
| |
| assert.strictEqual(pmd.totals.residentBytes, 9240); |
| assert.isUndefined(pmd.totals.peakResidentBytes); |
| |
| var vmRegions = pmd.vmRegions; |
| assert.strictEqual(vmRegions.sizeInBytes, 4976); |
| assert.deepEqual(vmRegions.byteStats, { |
| privateDirtyResident: 3096, |
| proportionalResident: 4120, |
| swapped: 1536 |
| }); |
| checkVMRegions(vmRegions, [ |
| { |
| mappedFile: 'mock.so', |
| sizeInBytes: 6000, |
| byteStats: { |
| privateDirtyResident: 4096, |
| proportionalResident: 5120, |
| swapped: 1536 |
| } |
| }, |
| { |
| mappedFile: '[discounted tracing overhead]', |
| sizeInBytes: -1024, |
| byteStats: { |
| privateDirtyResident: -1000, |
| proportionalResident: -1000 |
| } |
| } |
| ]); |
| |
| var mallocDump = pmd.getMemoryAllocatorDumpByFullName('malloc'); |
| checkDumpNumericsAndDiagnostics(mallocDump, { |
| 'size': 3072, |
| 'effective_size': 3072 |
| }, {}); |
| assert.lengthOf(mallocDump.children, 0); |
| |
| var ownedDump = pmd.getMemoryAllocatorDumpByFullName('owned'); |
| checkDumpNumericsAndDiagnostics(ownedDump, { |
| 'size': 1024, |
| 'effective_size': 0 |
| }, {}); |
| assert.lengthOf(ownedDump.children, 0); |
| |
| var tracingDump = pmd.getMemoryAllocatorDumpByFullName('tracing'); |
| checkDumpNumericsAndDiagnostics(tracingDump, { |
| 'size': 1024, |
| 'effective_size': 1024, |
| 'resident_size': 1000 |
| }, {}); |
| assert.strictEqual(tracingDump.owns.target, ownedDump); |
| }); |
| }); |
| </script> |