| <!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/base/color.html"> |
| <link rel="import" href="/tracing/base/color_scheme.html"> |
| <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/heap_dump.html"> |
| <link rel="import" href="/tracing/model/memory_dump_test_utils.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'; |
| |
| /** |
| * @fileoverview Helper functions for memory dump analysis sub-view tests. |
| */ |
| tr.exportTo('tr.ui.analysis', function() { |
| var Color = tr.b.Color; |
| var ColorScheme = tr.b.ColorScheme; |
| 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 sizeInBytes_smallerIsBetter = |
| tr.v.Unit.byName.sizeInBytes_smallerIsBetter; |
| var unitlessNumber_smallerIsBetter = |
| tr.v.Unit.byName.unitlessNumber_smallerIsBetter; |
| var HeapDump = tr.model.HeapDump; |
| var newAllocatorDump = tr.model.MemoryDumpTestUtils.newAllocatorDump; |
| var addOwnershipLink = tr.model.MemoryDumpTestUtils.addOwnershipLink; |
| |
| function createMultipleTestGlobalMemoryDumps() { |
| var model = tr.c.TestUtils.newModel(function(model) { |
| var pA = model.getOrCreateProcess(1); |
| var pB = model.getOrCreateProcess(2); |
| var pC = model.getOrCreateProcess(3); |
| var pD = model.getOrCreateProcess(4); |
| |
| // ====================================================================== |
| // First timestamp. |
| // ====================================================================== |
| var gmd1 = addGlobalMemoryDump(model, 42); |
| |
| // Totals and VM regions. |
| var pmd1A = addProcessMemoryDump(gmd1, pA, 41); |
| pmd1A.totals = { residentBytes: 31457280 /* 30 MiB */ }; |
| pmd1A.vmRegions = VMRegionClassificationNode.fromRegions([ |
| VMRegion.fromDict({ |
| startAddress: 1024, |
| sizeInBytes: 20971520, /* 20 MiB */ |
| protectionFlags: VMRegion.PROTECTION_FLAG_READ, |
| mappedFile: '[stack]', |
| byteStats: { |
| privateDirtyResident: 8388608, /* 8 MiB */ |
| sharedCleanResident: 12582912, /* 12 MiB */ |
| proportionalResident: 10485760 /* 10 MiB */ |
| } |
| }) |
| ]); |
| |
| // Everything. |
| var pmd1B = addProcessMemoryDump(gmd1, pB, 42); |
| pmd1B.totals = { |
| residentBytes: 20971520, /* 20 MiB */ |
| peakResidentBytes: 41943040, /* 40 MiB */ |
| arePeakResidentBytesResettable: false, |
| platformSpecific: { |
| private_bytes: 10485760 /* 10 MiB */ |
| } |
| }; |
| pmd1B.vmRegions = VMRegionClassificationNode.fromRegions([ |
| VMRegion.fromDict({ |
| startAddress: 256, |
| sizeInBytes: 6000, |
| protectionFlags: VMRegion.PROTECTION_FLAG_READ | |
| VMRegion.PROTECTION_FLAG_WRITE, |
| mappedFile: '[stack:20310]', |
| byteStats: { |
| proportionalResident: 15728640, /* 15 MiB */ |
| privateDirtyResident: 1572864, /* 1.5 MiB */ |
| swapped: 32 /* 32 B */ |
| } |
| }), |
| VMRegion.fromDict({ |
| startAddress: 100000, |
| sizeInBytes: 4096, |
| protectionFlags: VMRegion.PROTECTION_FLAG_READ, |
| mappedFile: '/usr/lib/libwtf.so', |
| byteStats: { |
| proportionalResident: 4194304, /* 4 MiB */ |
| privateDirtyResident: 0, |
| swapped: 0 /* 32 B */ |
| } |
| }) |
| ]); |
| pmd1B.memoryAllocatorDumps = [ |
| newAllocatorDump(pmd1B, 'malloc', { size: 3145728 /* 3 MiB */ }), |
| newAllocatorDump(pmd1B, 'v8', { size: 5242880 /* 5 MiB */ }), |
| newAllocatorDump(pmd1B, 'tracing', { |
| size: 1048576 /* 1 MiB */, |
| resident_size: 1572864 /* 1.5 MiB */ |
| }) |
| ]; |
| |
| // Allocator dumps only. |
| var pmd1C = addProcessMemoryDump(gmd1, pC, 43); |
| pmd1C.memoryAllocatorDumps = (function() { |
| var oilpanDump = newAllocatorDump(pmd1C, 'oilpan', { |
| size: 3221225472 /* 3 GiB */, |
| inner_size: 5242880 /* 5 MiB */, |
| objects_count: new ScalarNumeric(unitlessNumber_smallerIsBetter, 2015) |
| }); |
| var v8Dump = newAllocatorDump(pmd1C, 'v8', { |
| size: 1073741824 /* 1 GiB */, |
| inner_size: 2097152 /* 2 MiB */, |
| objects_count: new ScalarNumeric(unitlessNumber_smallerIsBetter, 204) |
| }); |
| |
| addOwnershipLink(v8Dump, oilpanDump); |
| |
| return [oilpanDump, v8Dump]; |
| })(); |
| pmd1C.heapDumps = { |
| 'v8': (function() { |
| var v8HeapDump = new HeapDump(pmd1C, 'v8'); |
| v8HeapDump.addEntry( |
| tr.c.TestUtils.newStackTrace(model, |
| ['V8.Execute', 'UpdateLayoutTree']), |
| undefined /* sum over all object types */, |
| 536870912 /* 512 MiB */); |
| return v8HeapDump; |
| })() |
| }; |
| |
| // ====================================================================== |
| // Second timestamp. |
| // ====================================================================== |
| var gmd2 = addGlobalMemoryDump(model, 68); |
| |
| // Everything. |
| var pmd2A = addProcessMemoryDump(gmd2, pA, 67); |
| pmd2A.totals = { residentBytes: 32505856 /* 31 MiB */ }; |
| pmd2A.vmRegions = VMRegionClassificationNode.fromRegions([ |
| VMRegion.fromDict({ |
| startAddress: 1024, |
| sizeInBytes: 20971520, /* 20 MiB */ |
| protectionFlags: VMRegion.PROTECTION_FLAG_READ, |
| mappedFile: '[stack]', |
| byteStats: { |
| privateDirtyResident: 8388608, /* 8 MiB */ |
| sharedCleanResident: 11534336, /* 11 MiB */ |
| proportionalResident: 11534336 /* 11 MiB */ |
| } |
| }), |
| VMRegion.fromDict({ |
| startAddress: 104857600, |
| sizeInBytes: 5242880, /* 5 MiB */ |
| protectionFlags: VMRegion.PROTECTION_FLAG_EXECUTE, |
| mappedFile: '/usr/bin/google-chrome', |
| byteStats: { |
| privateDirtyResident: 0, |
| sharedCleanResident: 4194304, /* 4 MiB */ |
| proportionalResident: 524288 /* 512 KiB */ |
| } |
| }) |
| ]); |
| pmd2A.memoryAllocatorDumps = [ |
| newAllocatorDump(pmd2A, 'malloc', { size: 9437184 /* 9 MiB */ }), |
| newAllocatorDump(pmd2A, 'tracing', { |
| size: 2097152 /* 2 MiB */, |
| resident_size: 2621440 /* 2.5 MiB */ |
| }) |
| ]; |
| |
| // Totals and allocator dumps only. |
| var pmd2B = addProcessMemoryDump(gmd2, pB, 69); |
| pmd2B.totals = { |
| residentBytes: 19922944, /* 19 MiB */ |
| peakResidentBytes: 41943040, /* 40 MiB */ |
| arePeakResidentBytesResettable: false, |
| platformSpecific: { |
| private_bytes: 8912896 /* 8.5 MiB */ |
| } |
| }; |
| pmd2B.memoryAllocatorDumps = [ |
| newAllocatorDump(pmd2B, 'malloc', { size: 2621440 /* 2.5 MiB */ }), |
| newAllocatorDump(pmd2B, 'v8', { size: 5242880 /* 5 MiB */ }), |
| newAllocatorDump(pmd2B, 'blink', { size: 7340032 /* 7 MiB */ }), |
| newAllocatorDump(pmd2B, 'oilpan', { size: 1 }), |
| newAllocatorDump(pmd2B, 'tracing', { |
| size: 1572864 /* 1.5 MiB */, |
| resident_size: 2097152 /* 2 MiB */ |
| }) |
| ]; |
| |
| // Resettable peak total size only. |
| var pmd2D = addProcessMemoryDump(gmd2, pD, 71); |
| pmd2D.totals = { |
| peakResidentBytes: 17825792, /* 17 MiB */ |
| arePeakResidentBytesResettable: true |
| }; |
| |
| // ====================================================================== |
| // Third timestamp. |
| // ====================================================================== |
| var gmd3 = addGlobalMemoryDump(model, 100); |
| |
| // Everything. |
| var pmd3B = addProcessMemoryDump(gmd3, pB, 102); |
| pmd3B.totals = { |
| residentBytes: 18874368, /* 18 MiB */ |
| peakResidentBytes: 44040192, /* 42 MiB */ |
| arePeakResidentBytesResettable: false, |
| platformSpecific: { |
| private_bytes: 7340032 /* 7 MiB */ |
| } |
| }; |
| pmd3B.vmRegions = VMRegionClassificationNode.fromRegions([ |
| VMRegion.fromDict({ |
| startAddress: 256, |
| sizeInBytes: 6000, |
| protectionFlags: VMRegion.PROTECTION_FLAG_READ | |
| VMRegion.PROTECTION_FLAG_WRITE, |
| mappedFile: '[stack:20310]', |
| byteStats: { |
| proportionalResident: 21495808, /* 20.5 MiB */ |
| privateDirtyResident: 524288, /* 0.5 MiB */ |
| swapped: 64 /* 32 B */ |
| } |
| }) |
| ]); |
| pmd3B.memoryAllocatorDumps = [ |
| newAllocatorDump(pmd3B, 'malloc', {size: 2883584 /* 2.75 MiB */ }), |
| newAllocatorDump(pmd3B, 'v8', { size: 5767168 /* 5.5 MiB */ }), |
| newAllocatorDump(pmd3B, 'blink', { size: 6291456 /* 7 MiB */ }), |
| newAllocatorDump(pmd3B, 'tracing', { |
| size: 2097152 /* 2 MiB */, |
| resident_size: 3145728 /* 3 MiB */ |
| }) |
| ]; |
| |
| // Allocator dumps only. |
| var pmd3C = addProcessMemoryDump(gmd3, pC, 100); |
| pmd3C.memoryAllocatorDumps = (function() { |
| var oilpanDump = newAllocatorDump(pmd3C, 'oilpan', { |
| size: 3221225472 /* 3 GiB */, |
| inner_size: 5242880 /* 5 MiB */, |
| objects_count: new ScalarNumeric(unitlessNumber_smallerIsBetter, 2015) |
| }); |
| var v8Dump = newAllocatorDump(pmd3C, 'v8', { |
| size: 2147483648 /* 2 GiB */, |
| inner_size: 2097152 /* 2 MiB */, |
| objects_count: new ScalarNumeric(unitlessNumber_smallerIsBetter, 204) |
| }); |
| |
| addOwnershipLink(v8Dump, oilpanDump); |
| |
| return [oilpanDump, v8Dump]; |
| })(); |
| pmd3C.heapDumps = { |
| 'v8': (function() { |
| var v8HeapDump = new HeapDump(pmd1C, 'v8'); |
| v8HeapDump.addEntry( |
| tr.c.TestUtils.newStackTrace(model, |
| ['V8.Execute', 'UpdateLayoutTree']), |
| undefined /* sum over all object types */, |
| 268435456 /* 256 MiB */); |
| v8HeapDump.addEntry( |
| tr.c.TestUtils.newStackTrace(model, |
| ['V8.Execute', 'FrameView::layout']), |
| undefined /* sum over all object types */, |
| 134217728 /* 128 MiB */); |
| return v8HeapDump; |
| })() |
| }; |
| |
| // Resettable peak total size only. |
| var pmd3D = addProcessMemoryDump(gmd3, pD, 99); |
| pmd3D.totals = { |
| peakResidentBytes: 17825792, /* 17 MiB */ |
| arePeakResidentBytesResettable: true |
| }; |
| }); |
| |
| return model.globalMemoryDumps; |
| } |
| |
| function createSingleTestGlobalMemoryDump() { |
| return createMultipleTestGlobalMemoryDumps()[1]; |
| } |
| |
| function createMultipleTestProcessMemoryDumps() { |
| return createMultipleTestGlobalMemoryDumps().map(function(gmd) { |
| return gmd.processMemoryDumps[2]; |
| }); |
| } |
| |
| function createSingleTestProcessMemoryDump() { |
| return createMultipleTestProcessMemoryDumps()[1]; |
| } |
| |
| function checkNumericFields(row, column, expectedValues, expectedUnit) { |
| var fields; |
| if (column === undefined) |
| fields = row; |
| else |
| fields = column.fields(row); |
| |
| if (expectedValues === undefined) { |
| assert.isUndefined(fields); |
| return; |
| } |
| |
| assert.lengthOf(fields, expectedValues.length); |
| for (var i = 0; i < fields.length; i++) { |
| var field = fields[i]; |
| var expectedValue = expectedValues[i]; |
| if (expectedValue === undefined) { |
| assert.isUndefined(field); |
| } else { |
| assert.isDefined(expectedUnit); // Test sanity check. |
| assert.instanceOf(field, ScalarNumeric); |
| assert.equal(field.value, expectedValue); |
| assert.equal(field.unit, expectedUnit); |
| } |
| } |
| } |
| |
| function checkSizeNumericFields(row, column, expectedValues) { |
| checkNumericFields(row, column, expectedValues, |
| sizeInBytes_smallerIsBetter); |
| } |
| |
| function checkStringFields(row, column, expectedStrings) { |
| var fields = column.fields(row); |
| |
| if (expectedStrings === undefined) { |
| assert.isUndefined(fields); |
| return; |
| } |
| |
| assert.deepEqual(tr.b.asArray(fields), expectedStrings); |
| } |
| |
| /** |
| * Check the titles, types and aggregation modes of a list of columns. |
| * expectedColumns is a list of dictionaries with the following fields: |
| * |
| * - title: Either the expected title (string), or a matcher for it |
| * (function that accepts the actual title as its argument). |
| * - type: The expected class of the column. |
| * - noAggregation: If true, the column is expected to have no aggregation |
| * mode (regardless of expectedAggregationMode). |
| */ |
| function checkColumns(columns, expectedColumns, expectedAggregationMode) { |
| assert.lengthOf(columns, expectedColumns.length); |
| for (var i = 0; i < expectedColumns.length; i++) { |
| var actualColumn = columns[i]; |
| var expectedColumn = expectedColumns[i]; |
| var expectedTitle = expectedColumn.title; |
| if (typeof expectedTitle === 'function') |
| expectedTitle(actualColumn.title); // Custom title matcher. |
| else |
| assert.strictEqual(actualColumn.title, expectedTitle); // String title. |
| assert.instanceOf(actualColumn, expectedColumn.type); |
| assert.strictEqual(actualColumn.aggregationMode, |
| expectedColumn.noAggregation ? undefined : expectedAggregationMode); |
| } |
| } |
| |
| function checkColumnInfosAndColor( |
| column, fields, contexts, expectedInfos, expectedColorReservedName) { |
| // Test sanity checks. |
| assert.isDefined(fields); |
| if (contexts !== undefined) |
| assert.lengthOf(contexts, fields.length); |
| |
| // Check infos. |
| var infos = []; |
| column.addInfos(fields, contexts, infos); |
| assert.lengthOf(infos, expectedInfos.length); |
| for (var i = 0; i < expectedInfos.length; i++) |
| assert.deepEqual(infos[i], expectedInfos[i]); |
| |
| // Check color. |
| var actualColor = typeof column.color === 'function' ? |
| column.color(fields, contexts) : |
| column.color; |
| checkColor(actualColor, expectedColorReservedName); |
| } |
| |
| function checkColor(actualColorString, expectedColorReservedName) { |
| if (expectedColorReservedName === undefined) { |
| assert.isUndefined(actualColorString); |
| return; |
| } |
| |
| var actualColor = Color.fromString(actualColorString); |
| var expectedColor = ColorScheme.colors[ |
| ColorScheme.getColorIdForReservedName(expectedColorReservedName)]; |
| assert.deepEqual(actualColor, expectedColor); |
| } |
| |
| function createAndCheckEmptyPanes( |
| test, paneTagName, propertyName, opt_callback) { |
| // Unset property. |
| var unsetViewEl = createTestPane(paneTagName); |
| unsetViewEl.rebuild(); |
| assert.isUndefined(unsetViewEl.createChildPane()); |
| test.addHTMLOutput(unsetViewEl); |
| |
| // Undefined property. |
| var undefinedViewEl = createTestPane(paneTagName); |
| undefinedViewEl[propertyName] = undefined; |
| undefinedViewEl.rebuild(); |
| assert.isUndefined(undefinedViewEl.createChildPane()); |
| test.addHTMLOutput(undefinedViewEl); |
| |
| // Empty property. |
| var emptyViewEl = createTestPane(paneTagName); |
| emptyViewEl[propertyName] = []; |
| emptyViewEl.rebuild(); |
| assert.isUndefined(undefinedViewEl.createChildPane()); |
| test.addHTMLOutput(emptyViewEl); |
| |
| // Check that all the panes have the same dimensions. |
| var unsetBounds = unsetViewEl.getBoundingClientRect(); |
| var undefinedBounds = undefinedViewEl.getBoundingClientRect(); |
| var emptyBounds = emptyViewEl.getBoundingClientRect(); |
| assert.equal(undefinedBounds.width, unsetBounds.width); |
| assert.equal(emptyBounds.width, unsetBounds.width); |
| assert.equal(undefinedBounds.height, unsetBounds.height); |
| assert.equal(emptyBounds.height, unsetBounds.height); |
| |
| // Custom checks (if provided). |
| if (opt_callback) { |
| opt_callback(unsetViewEl); |
| opt_callback(undefinedViewEl); |
| opt_callback(emptyViewEl); |
| } |
| } |
| |
| function createTestPane(tagName) { |
| var paneEl = document.createElement(tagName); |
| |
| // Store a list of requested child panes (for inspection in tests). |
| paneEl.requestedChildPanes = []; |
| paneEl.addEventListener('request-child-pane-change', function() { |
| paneEl.requestedChildPanes.push(paneEl.createChildPane()); |
| }); |
| |
| paneEl.createChildPane = function() { |
| var childPaneBuilder = this.childPaneBuilder; |
| if (childPaneBuilder === undefined) |
| return undefined; |
| return childPaneBuilder(); |
| }; |
| |
| return paneEl; |
| } |
| |
| function addGlobalMemoryDump(model, timestamp) { |
| var gmd = new GlobalMemoryDump(model, timestamp); |
| model.globalMemoryDumps.push(gmd); |
| return gmd; |
| } |
| |
| function addProcessMemoryDump(gmd, process, timestamp) { |
| var pmd = new ProcessMemoryDump(gmd, process, timestamp); |
| process.memoryDumps.push(pmd); |
| if (process.pid in gmd.processMemoryDumps) { |
| // Test sanity check. |
| throw new Error('Process memory dump for process with pid=' + |
| process.pid + ' has already been provided'); |
| } |
| gmd.processMemoryDumps[process.pid] = pmd; |
| return pmd; |
| } |
| |
| // TODO(petrcermak): Consider moving this to tracing/ui/base/dom_helpers.html. |
| function isElementDisplayed(element) { |
| var style = getComputedStyle(element); |
| var displayed = style['display']; |
| if (displayed === undefined) |
| return true; |
| return displayed.indexOf('none') === -1; |
| } |
| |
| /** |
| * Convert a list of ContainerMemoryDump(s) to a list of dictionaries of the |
| * underlying ProcessMemoryDump(s). |
| */ |
| function convertToProcessMemoryDumps(containerMemoryDumps) { |
| return containerMemoryDumps.map(function(containerMemoryDump) { |
| return containerMemoryDump.processMemoryDumps; |
| }); |
| } |
| |
| /** |
| * Extract a chronological list of ProcessMemoryDump(s) (for a given process) |
| * from a chronological list of dictionaries of ProcessMemoryDump(s). |
| */ |
| function extractProcessMemoryDumps(processMemoryDumps, pid) { |
| return processMemoryDumps.map(function(memoryDumps) { |
| return memoryDumps[pid]; |
| }); |
| } |
| |
| /** |
| * Extract a chronological list of lists of VMRegion(s) (for a given process) |
| * from a chronological list of dictionaries of ProcessMemoryDump(s). |
| */ |
| function extractVmRegions(processMemoryDumps, pid) { |
| return processMemoryDumps.map(function(memoryDumps) { |
| var processMemoryDump = memoryDumps[pid]; |
| if (processMemoryDump === undefined) |
| return undefined; |
| return processMemoryDump.mostRecentVmRegions; |
| }); |
| } |
| |
| /** |
| * Extract a chronological list of MemoryAllocatorDump(s) (for a given |
| * process and allocator name) from a chronological list of dictionaries of |
| * ProcessMemoryDump(s). |
| */ |
| function extractMemoryAllocatorDumps(processMemoryDumps, pid, allocatorName) { |
| return processMemoryDumps.map(function(memoryDumps) { |
| var processMemoryDump = memoryDumps[pid]; |
| if (processMemoryDump === undefined) |
| return undefined; |
| return processMemoryDump.getMemoryAllocatorDumpByFullName(allocatorName); |
| }); |
| } |
| |
| /** |
| * Extract a chronological list of HeapDump(s) (for a given process and |
| * allocator name) from a chronological list of dictionaries of |
| * ProcessMemoryDump(s). |
| */ |
| function extractHeapDumps(processMemoryDumps, pid, allocatorName) { |
| return processMemoryDumps.map(function(memoryDumps) { |
| var processMemoryDump = memoryDumps[pid]; |
| if (processMemoryDump === undefined || |
| processMemoryDump.heapDumps === undefined) |
| return undefined; |
| return processMemoryDump.heapDumps[allocatorName]; |
| }); |
| } |
| |
| return { |
| addGlobalMemoryDump: addGlobalMemoryDump, |
| addProcessMemoryDump: addProcessMemoryDump, |
| createSingleTestGlobalMemoryDump: createSingleTestGlobalMemoryDump, |
| createMultipleTestGlobalMemoryDumps: createMultipleTestGlobalMemoryDumps, |
| createSingleTestProcessMemoryDump: createSingleTestProcessMemoryDump, |
| createMultipleTestProcessMemoryDumps: createMultipleTestProcessMemoryDumps, |
| checkNumericFields: checkNumericFields, |
| checkSizeNumericFields: checkSizeNumericFields, |
| checkStringFields: checkStringFields, |
| checkColumns: checkColumns, |
| checkColumnInfosAndColor: checkColumnInfosAndColor, |
| checkColor: checkColor, |
| createAndCheckEmptyPanes: createAndCheckEmptyPanes, |
| createTestPane: createTestPane, |
| isElementDisplayed: isElementDisplayed, |
| convertToProcessMemoryDumps: convertToProcessMemoryDumps, |
| extractProcessMemoryDumps: extractProcessMemoryDumps, |
| extractVmRegions: extractVmRegions, |
| extractMemoryAllocatorDumps: extractMemoryAllocatorDumps, |
| extractHeapDumps: extractHeapDumps |
| }; |
| }); |
| </script> |