blob: 46046fd8e0a70ff51674030eb32f66309a9f76ed [file] [log] [blame]
<!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>