summaryrefslogtreecommitdiff
path: root/chromium/third_party/catapult/tracing/tracing/ui/analysis/memory_dump_overview_pane_test.html
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/catapult/tracing/tracing/ui/analysis/memory_dump_overview_pane_test.html')
-rw-r--r--chromium/third_party/catapult/tracing/tracing/ui/analysis/memory_dump_overview_pane_test.html840
1 files changed, 840 insertions, 0 deletions
diff --git a/chromium/third_party/catapult/tracing/tracing/ui/analysis/memory_dump_overview_pane_test.html b/chromium/third_party/catapult/tracing/tracing/ui/analysis/memory_dump_overview_pane_test.html
new file mode 100644
index 00000000000..80127e020a8
--- /dev/null
+++ b/chromium/third_party/catapult/tracing/tracing/ui/analysis/memory_dump_overview_pane_test.html
@@ -0,0 +1,840 @@
+<!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/utils.html">
+<link rel="import" href="/tracing/core/test_utils.html">
+<link rel="import" href="/tracing/model/heap_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/ui/analysis/memory_dump_overview_pane.html">
+<link rel="import"
+ href="/tracing/ui/analysis/memory_dump_sub_view_test_utils.html">
+<link rel="import" href="/tracing/ui/analysis/memory_dump_sub_view_util.html">
+<link rel="import" href="/tracing/ui/brushing_state_controller.html">
+
+<script>
+'use strict';
+
+tr.b.unittest.testSuite(function() {
+ const Scalar = tr.b.Scalar;
+ const sizeInBytes_smallerIsBetter =
+ tr.b.Unit.byName.sizeInBytes_smallerIsBetter;
+ const MemoryAllocatorDump = tr.model.MemoryAllocatorDump;
+ const HeapDump = tr.model.HeapDump;
+ const AggregationMode = tr.ui.analysis.MemoryColumn.AggregationMode;
+ const checkSizeNumericFields = tr.ui.analysis.checkSizeNumericFields;
+ const checkColor = tr.ui.analysis.checkColor;
+ const checkColumns = tr.ui.analysis.checkColumns;
+ const checkColumnInfosAndColor = tr.ui.analysis.checkColumnInfosAndColor;
+ const convertToProcessMemoryDumps =
+ tr.ui.analysis.convertToProcessMemoryDumps;
+ const extractProcessMemoryDumps = tr.ui.analysis.extractProcessMemoryDumps;
+ const extractVmRegions = tr.ui.analysis.extractVmRegions;
+ const extractMemoryAllocatorDumps =
+ tr.ui.analysis.extractMemoryAllocatorDumps;
+ const isElementDisplayed = tr.ui.analysis.isElementDisplayed;
+ const addProcessMemoryDump =
+ tr.model.MemoryDumpTestUtils.addProcessMemoryDump;
+ const addGlobalMemoryDump = tr.model.MemoryDumpTestUtils.addGlobalMemoryDump;
+ const ProcessNameColumn = tr.ui.analysis.ProcessNameColumn;
+ const UsedMemoryColumn = tr.ui.analysis.UsedMemoryColumn;
+ const PeakMemoryColumn = tr.ui.analysis.PeakMemoryColumn;
+ const ByteStatColumn = tr.ui.analysis.ByteStatColumn;
+ const AllocatorColumn = tr.ui.analysis.AllocatorColumn;
+ const TracingColumn = tr.ui.analysis.TracingColumn;
+
+ function spanMatcher(expectedTitle) {
+ return function(actualTitle) {
+ assert.instanceOf(actualTitle, HTMLElement);
+ assert.strictEqual(actualTitle.tagName, 'SPAN');
+ assert.strictEqual(Polymer.dom(actualTitle).textContent, expectedTitle);
+ };
+ }
+
+ function colorLegendMatcher(expectedTitle) {
+ return function(actualTitle) {
+ assert.instanceOf(actualTitle, HTMLElement);
+ assert.strictEqual(actualTitle.tagName, 'TR-UI-B-COLOR-LEGEND');
+ assert.strictEqual(actualTitle.label, expectedTitle);
+ };
+ }
+
+ const EXPECTED_COLUMNS = [
+ { title: 'Process', type: ProcessNameColumn, noAggregation: true },
+ { title: spanMatcher('Total resident'), type: UsedMemoryColumn },
+ { title: spanMatcher('Peak total resident'), type: PeakMemoryColumn },
+ { title: spanMatcher('PSS'), type: ByteStatColumn },
+ { title: spanMatcher('Private dirty'), type: ByteStatColumn },
+ { title: spanMatcher('Swapped'), type: ByteStatColumn },
+ { title: spanMatcher('Private'), type: UsedMemoryColumn },
+ { title: spanMatcher('Private footprint'), type: UsedMemoryColumn },
+ { title: colorLegendMatcher('blink'), type: AllocatorColumn },
+ { title: colorLegendMatcher('gpu'), type: AllocatorColumn },
+ { title: colorLegendMatcher('malloc'), type: AllocatorColumn },
+ { title: colorLegendMatcher('oilpan'), type: AllocatorColumn },
+ { title: colorLegendMatcher('v8'), type: AllocatorColumn },
+ { title: spanMatcher('tracing'), type: TracingColumn }
+ ];
+
+ function checkRow(columns, row, expectedTitle, expectedSizes,
+ expectedContexts) {
+ // Check title.
+ const formattedTitle = columns[0].formatTitle(row);
+ if (typeof expectedTitle === 'function') {
+ expectedTitle(formattedTitle);
+ } else {
+ assert.strictEqual(formattedTitle, expectedTitle);
+ }
+
+ // Check all sizes. The first assert below is a test sanity check.
+ assert.lengthOf(expectedSizes, columns.length - 1 /* all except title */);
+ for (let i = 0; i < expectedSizes.length; i++) {
+ checkSizeNumericFields(row, columns[i + 1], expectedSizes[i]);
+ }
+
+ // There should be no row nesting on the overview pane.
+ assert.isUndefined(row.subRows);
+
+ if (expectedContexts) {
+ assert.deepEqual(Array.from(row.contexts), expectedContexts);
+ } else {
+ assert.isUndefined(row.contexts);
+ }
+ }
+
+ function checkRows(columns, actualRows, expectedRows) {
+ if (expectedRows === undefined) {
+ assert.isUndefined(actualRows);
+ return;
+ }
+ assert.lengthOf(actualRows, expectedRows.length);
+ for (let i = 0; i < expectedRows.length; i++) {
+ const actualRow = actualRows[i];
+ const expectedRow = expectedRows[i];
+ checkRow(columns, actualRow, expectedRow.title, expectedRow.sizes,
+ expectedRow.contexts);
+ }
+ }
+
+ function checkSpanWithColor(span, expectedText, expectedColor) {
+ assert.strictEqual(span.tagName, 'SPAN');
+ assert.strictEqual(Polymer.dom(span).textContent, expectedText);
+ checkColor(span.style.color, expectedColor);
+ }
+
+ function checkColorLegend(legend, expectedLabel) {
+ assert.strictEqual(legend.tagName, 'TR-UI-B-COLOR-LEGEND');
+ assert.strictEqual(legend.label, expectedLabel);
+ }
+
+ function createAndCheckMemoryDumpOverviewPane(
+ test, processMemoryDumps, expectedRows, expectedFooterRows,
+ aggregationMode) {
+ const viewEl =
+ tr.ui.analysis.createTestPane('tr-ui-a-memory-dump-overview-pane');
+ viewEl.processMemoryDumps = processMemoryDumps;
+ viewEl.aggregationMode = aggregationMode;
+ viewEl.rebuild();
+ test.addHTMLOutput(viewEl);
+
+ // Check that the table is shown.
+ assert.isTrue(isElementDisplayed(viewEl.$.table));
+ assert.isFalse(isElementDisplayed(viewEl.$.info_text));
+
+ assert.isUndefined(viewEl.createChildPane());
+
+ const table = viewEl.$.table;
+ const columns = table.tableColumns;
+ checkColumns(columns, EXPECTED_COLUMNS, aggregationMode);
+ const rows = table.tableRows;
+
+ checkRows(columns, table.tableRows, expectedRows);
+ checkRows(columns, table.footerRows, expectedFooterRows);
+ }
+
+ const FIELD = 1 << 0;
+ const DUMP = 1 << 1;
+
+ function checkOverviewColumnInfosAndColor(column, fieldAndDumpMask,
+ dumpCreatedCallback, expectedInfos, expectedColorReservedName) {
+ const fields = fieldAndDumpMask.map(function(mask, index) {
+ return mask & FIELD ?
+ new Scalar(sizeInBytes_smallerIsBetter, 1024 + 32 * index) :
+ undefined;
+ });
+
+ let contexts;
+ if (dumpCreatedCallback === undefined) {
+ contexts = undefined;
+ } else {
+ tr.c.TestUtils.newModel(function(model) {
+ const process = model.getOrCreateProcess(1);
+ fieldAndDumpMask.forEach(function(mask, i) {
+ const timestamp = 10 + i;
+ const gmd = addGlobalMemoryDump(model, {ts: timestamp});
+ if (mask & DUMP) {
+ const pmd = addProcessMemoryDump(gmd, process, {ts: timestamp});
+ dumpCreatedCallback(pmd, mask);
+ }
+ });
+ contexts = model.globalMemoryDumps.map(function(gmd) {
+ return gmd.processMemoryDumps[1];
+ });
+ });
+ }
+
+ checkColumnInfosAndColor(
+ column, fields, contexts, expectedInfos, expectedColorReservedName);
+ }
+
+ test('colorsAreDefined', function() {
+ // We use these constants in the code and the tests so here we guard
+ // against them being undefined and causing all the tests to still
+ // pass while the we end up with no colors.
+ assert.isDefined(UsedMemoryColumn.COLOR);
+ assert.isDefined(UsedMemoryColumn.OLDER_COLOR);
+ assert.isDefined(TracingColumn.COLOR);
+ });
+
+ test('instantiate_empty', function() {
+ tr.ui.analysis.createAndCheckEmptyPanes(this,
+ 'tr-ui-a-memory-dump-overview-pane', 'processMemoryDumps',
+ function(viewEl) {
+ // Check that the info text is shown.
+ assert.isTrue(isElementDisplayed(viewEl.$.info_text));
+ assert.isFalse(isElementDisplayed(viewEl.$.table));
+ });
+ });
+
+ test('instantiate_singleGlobalMemoryDump', function() {
+ const processMemoryDumps = convertToProcessMemoryDumps(
+ [tr.ui.analysis.createSingleTestGlobalMemoryDump()]);
+ createAndCheckMemoryDumpOverviewPane(this,
+ processMemoryDumps,
+ [ // Table rows.
+ {
+ title: colorLegendMatcher('Process 1'),
+ sizes: [[29884416], undefined, [9437184], [5767168], undefined,
+ undefined, undefined, undefined, undefined, [7340032], undefined,
+ undefined, [2097152]],
+ contexts: extractProcessMemoryDumps(processMemoryDumps, 1)
+ },
+ {
+ title: colorLegendMatcher('Process 2'),
+ sizes: [[17825792], [39845888], [18350080], [0], [32], [8912896],
+ [15728640], [7340032], [0], [1048576], [1], [5242880],
+ [1572864]],
+ contexts: extractProcessMemoryDumps(processMemoryDumps, 2)
+ },
+ {
+ title: colorLegendMatcher('Process 4'),
+ sizes: [undefined, [17825792], undefined, undefined, undefined,
+ undefined, undefined, undefined, undefined, undefined,
+ undefined, undefined, undefined],
+ contexts: extractProcessMemoryDumps(processMemoryDumps, 4)
+ }
+ ],
+ [ // Footer rows.
+ {
+ title: 'Total',
+ sizes: [[47710208], [57671680], [27787264], [5767168], [32],
+ [8912896], [15728640], [7340032], [0], [8388608], [1],
+ [5242880], [3670016]],
+ contexts: undefined
+ }
+ ],
+ undefined /* no aggregation */);
+ });
+
+ test('instantiate_multipleGlobalMemoryDumps', function() {
+ const processMemoryDumps = convertToProcessMemoryDumps(
+ tr.ui.analysis.createMultipleTestGlobalMemoryDumps());
+ createAndCheckMemoryDumpOverviewPane(this,
+ processMemoryDumps,
+ [ // Table rows.
+ {
+ title: colorLegendMatcher('Process 1'),
+ sizes: [[31457280, 29884416, undefined], undefined,
+ [10485760, 9437184, undefined], [8388608, 5767168, undefined],
+ undefined, undefined, undefined, undefined, undefined,
+ [undefined, 7340032, undefined], undefined, undefined,
+ [undefined, 2097152, undefined]],
+ contexts: extractProcessMemoryDumps(processMemoryDumps, 1)
+ },
+ {
+ title: colorLegendMatcher('Process 2'),
+ sizes: [[19398656, 17825792, 15728640],
+ [40370176, 39845888, 40894464], [18350080, 18350080, 18350080],
+ [0, 0, -2621440], [32, 32, 64], [10485760, 8912896, 7340032],
+ [15728640, 15728640, 15728640], [undefined, 7340032, 6291456],
+ [undefined, 0, 1048576], [2097152, 1048576, 786432],
+ [undefined, 1, undefined], [5242880, 5242880, 5767168],
+ [1048576, 1572864, 2097152]],
+ contexts: extractProcessMemoryDumps(processMemoryDumps, 2)
+ },
+ {
+ title: colorLegendMatcher('Process 3'),
+ sizes: [undefined, undefined, undefined, undefined, undefined,
+ undefined, undefined, undefined, undefined, undefined,
+ [2147483648, undefined, 1073741824],
+ [1073741824, undefined, 2147483648], undefined],
+ contexts: extractProcessMemoryDumps(processMemoryDumps, 3)
+ },
+ {
+ title: colorLegendMatcher('Process 4'),
+ sizes: [undefined, [undefined, 17825792, 17825792], undefined,
+ undefined, undefined, undefined, undefined, undefined,
+ undefined, undefined, undefined, undefined, undefined],
+ contexts: extractProcessMemoryDumps(processMemoryDumps, 4)
+ }
+ ],
+ [ // Footer rows.
+ {
+ title: 'Total',
+ sizes: [[50855936, 47710208, 15728640],
+ [40370176, 57671680, 58720256], [28835840, 27787264, 18350080],
+ [8388608, 5767168, -2621440], [32, 32, 64],
+ [10485760, 8912896, 7340032], [15728640, 15728640, 15728640],
+ [undefined, 7340032, 6291456], [undefined, 0, 1048576],
+ [2097152, 8388608, 786432], [2147483648, 1, 1073741824],
+ [1078984704, 5242880, 2153250816],
+ [1048576, 3670016, 2097152]],
+ contexts: undefined
+ }
+ ],
+ AggregationMode.DIFF);
+ });
+
+ test('instantiate_singleProcessMemoryDump', function() {
+ const processMemoryDumps = convertToProcessMemoryDumps(
+ [tr.ui.analysis.createSingleTestProcessMemoryDump()]);
+ createAndCheckMemoryDumpOverviewPane(this,
+ processMemoryDumps,
+ [ // Table rows.
+ {
+ title: colorLegendMatcher('Process 2'),
+ sizes: [[17825792], [39845888], [18350080], [0], [32], [8912896],
+ [15728640], [7340032], [0], [1048576], [1], [5242880],
+ [1572864]],
+ contexts: extractProcessMemoryDumps(processMemoryDumps, 2)
+ }
+ ],
+ [] /* footer rows */,
+ undefined /* no aggregation */);
+ });
+
+ test('instantiate_multipleProcessMemoryDumps', function() {
+ const processMemoryDumps = convertToProcessMemoryDumps(
+ tr.ui.analysis.createMultipleTestProcessMemoryDumps());
+ createAndCheckMemoryDumpOverviewPane(this,
+ processMemoryDumps,
+ [ // Table rows.
+ {
+ title: colorLegendMatcher('Process 2'),
+ sizes: [[19398656, 17825792, 15728640],
+ [40370176, 39845888, 40894464], [18350080, 18350080, 18350080],
+ [0, 0, -2621440], [32, 32, 64], [10485760, 8912896, 7340032],
+ [15728640, 15728640, 15728640], [undefined, 7340032, 6291456],
+ [undefined, 0, 1048576], [2097152, 1048576, 786432],
+ [undefined, 1, undefined], [5242880, 5242880, 5767168],
+ [1048576, 1572864, 2097152]],
+ contexts: extractProcessMemoryDumps(processMemoryDumps, 2)
+ }
+ ],
+ [] /* footer rows */,
+ AggregationMode.MAX);
+ });
+
+ test('selection', function() {
+ const processMemoryDumps = convertToProcessMemoryDumps(
+ tr.ui.analysis.createMultipleTestGlobalMemoryDumps());
+
+ const viewEl =
+ tr.ui.analysis.createTestPane('tr-ui-a-memory-dump-overview-pane');
+ viewEl.processMemoryDumps = processMemoryDumps;
+ viewEl.aggregationMode = AggregationMode.DIFF;
+ viewEl.rebuild();
+ this.addHTMLOutput(viewEl);
+
+ const table = viewEl.$.table;
+
+ // Simulate clicking on the 'malloc' cell of the second process.
+ table.selectedTableRow = table.tableRows[1];
+ table.selectedColumnIndex = 10;
+ assert.lengthOf(viewEl.requestedChildPanes, 2);
+ let lastChildPane = viewEl.requestedChildPanes[1];
+ assert.strictEqual(
+ lastChildPane.tagName, 'TR-UI-A-MEMORY-DUMP-ALLOCATOR-DETAILS-PANE');
+ assert.strictEqual(lastChildPane.aggregationMode, AggregationMode.DIFF);
+ assert.deepEqual(lastChildPane.memoryAllocatorDumps,
+ extractMemoryAllocatorDumps(processMemoryDumps, 2, 'malloc'));
+
+ // Simulate clicking on the 'Oilpan' cell of the second process.
+ table.selectedColumnIndex = 10;
+ assert.lengthOf(viewEl.requestedChildPanes, 3);
+ lastChildPane = viewEl.requestedChildPanes[2];
+ assert.isUndefined(viewEl.lastChildPane);
+ });
+
+ test('memory', function() {
+ const processMemoryDumps = convertToProcessMemoryDumps(
+ tr.ui.analysis.createMultipleTestGlobalMemoryDumps());
+ const containerEl = document.createElement('div');
+ containerEl.brushingStateController =
+ new tr.c.BrushingStateController(undefined);
+
+ function simulateView(pids, aggregationMode,
+ expectedSelectedCellFieldValues, expectedSelectedRowTitle,
+ expectedSelectedColumnIndex, callback) {
+ const viewEl =
+ tr.ui.analysis.createTestPane('tr-ui-a-memory-dump-overview-pane');
+ const table = viewEl.$.table;
+ Polymer.dom(containerEl).textContent = '';
+ Polymer.dom(containerEl).appendChild(viewEl);
+
+ const displayedProcessMemoryDumps = processMemoryDumps.map(
+ function(memoryDumps) {
+ const result = {};
+ for (const [pid, pmd] of Object.entries(memoryDumps)) {
+ if (pids.includes(pmd.process.pid)) result[pid] = pmd;
+ }
+ return result;
+ });
+ viewEl.processMemoryDumps = displayedProcessMemoryDumps;
+ viewEl.aggregationMode = aggregationMode;
+ viewEl.rebuild();
+
+ if (expectedSelectedCellFieldValues === undefined) {
+ assert.isUndefined(viewEl.childPaneBuilder);
+ } else {
+ checkSizeNumericFields(table.selectedTableRow,
+ table.tableColumns[table.selectedColumnIndex],
+ expectedSelectedCellFieldValues);
+ }
+
+ assert.strictEqual(
+ table.selectedColumnIndex, expectedSelectedColumnIndex);
+ if (expectedSelectedRowTitle === undefined) {
+ assert.isUndefined(table.selectedTableRow);
+ } else {
+ assert.strictEqual(
+ table.selectedTableRow.title, expectedSelectedRowTitle);
+ }
+
+ callback(viewEl, viewEl.$.table);
+ }
+
+ simulateView(
+ [1, 2, 3, 4], // All processes.
+ AggregationMode.DIFF,
+ undefined, undefined, undefined, // No cell should be selected.
+ function(view, table) {
+ assert.isUndefined(view.createChildPane());
+
+ // Select the 'PSS' column of the second process.
+ table.selectedTableRow = table.tableRows[1];
+ table.selectedColumnIndex = 3;
+ });
+
+ simulateView(
+ [2, 3],
+ AggregationMode.MAX,
+ [18350080, 18350080, 18350080], 'Process 2', 3, /* PSS */
+ function(view, table) {
+ const childPane = view.createChildPane();
+ assert.strictEqual(
+ childPane.tagName, 'TR-UI-A-MEMORY-DUMP-VM-REGIONS-DETAILS-PANE');
+ assert.deepEqual(Array.from(childPane.vmRegions),
+ extractVmRegions(processMemoryDumps, 2));
+ assert.strictEqual(childPane.aggregationMode, AggregationMode.MAX);
+ });
+
+ simulateView(
+ [3],
+ undefined, /* No aggregation */
+ undefined, undefined, undefined, // No cell selected.
+ function(view, table) {
+ assert.isUndefined(view.createChildPane());
+ });
+
+ simulateView(
+ [1, 2, 3, 4],
+ AggregationMode.DIFF,
+ [18350080, 18350080, 18350080], 'Process 2', 3, /* PSS */
+ function(view, table) {
+ const childPane = view.createChildPane();
+ assert.strictEqual(
+ childPane.tagName, 'TR-UI-A-MEMORY-DUMP-VM-REGIONS-DETAILS-PANE');
+ assert.deepEqual(Array.from(childPane.vmRegions),
+ extractVmRegions(processMemoryDumps, 2));
+ assert.strictEqual(childPane.aggregationMode, AggregationMode.DIFF);
+
+ // Select the 'v8' column of the first process (empty cell).
+ table.selectedTableRow = table.tableRows[0];
+ table.selectedColumnIndex = 11;
+ });
+
+ simulateView(
+ [1],
+ undefined, /* No aggregation */
+ undefined, undefined, undefined, // No cell should selected.
+ function(view, table) {
+ assert.isUndefined(view.createChildPane());
+
+ // Select 'Total resident' column of the first process.
+ table.selectedTableRow = table.tableRows[0];
+ table.selectedColumnIndex = 1;
+ });
+
+ simulateView(
+ [1, 2, 3, 4],
+ AggregationMode.MAX,
+ [31457280, 29884416, undefined], 'Process 1', 1, /* Total resident */
+ function(view, table) {
+ const childPane = view.createChildPane();
+ assert.strictEqual(
+ childPane.tagName, 'TR-UI-A-MEMORY-DUMP-VM-REGIONS-DETAILS-PANE');
+ assert.deepEqual(Array.from(childPane.vmRegions),
+ extractVmRegions(processMemoryDumps, 1));
+ assert.strictEqual(childPane.aggregationMode, AggregationMode.MAX);
+ });
+ });
+
+ test('processNameColumn_formatTitle', function() {
+ const c = new ProcessNameColumn();
+
+ // With context (total row).
+ assert.strictEqual(c.formatTitle({
+ title: 'Total',
+ usedMemoryCells: {}
+ }), 'Total');
+
+ // Without context (process row).
+ const title = c.formatTitle({
+ title: 'Process 1',
+ usedMemoryCells: {},
+ contexts: [tr.ui.analysis.createSingleTestProcessMemoryDump()]
+ });
+ checkColorLegend(title, 'Process 1');
+ });
+
+ test('usedMemoryColumn', function() {
+ const c = new UsedMemoryColumn('Private', 'bytes', (x => x),
+ AggregationMode.DIFF);
+ checkSpanWithColor(c.title, 'Private',
+ UsedMemoryColumn.COLOR /* blue (column title) */);
+ checkColor(c.color(undefined /* contexts */),
+ UsedMemoryColumn.COLOR /* blue (column cells) */);
+ });
+
+ test('peakMemoryColumn', function() {
+ const c = new PeakMemoryColumn('Peak', 'bytes', (x => x),
+ AggregationMode.MAX);
+ checkSpanWithColor(c.title, 'Peak',
+ UsedMemoryColumn.COLOR /* blue (column title) */);
+ checkColor(c.color(undefined) /* contexts */,
+ UsedMemoryColumn.COLOR /* blue (column cells) */);
+
+ const RESETTABLE_PEAK = 1 << 2;
+ const NON_RESETTABLE_PEAK = 1 << 3;
+ function checkPeakColumnInfosAndColor(fieldAndDumpMask, expectedInfos) {
+ checkOverviewColumnInfosAndColor(c,
+ fieldAndDumpMask,
+ function(pmd, mask) {
+ if (mask & RESETTABLE_PEAK) {
+ assert.strictEqual(
+ mask & NON_RESETTABLE_PEAK, 0); // Test sanity check.
+ pmd.arePeakResidentBytesResettable = true;
+ } else if (mask & NON_RESETTABLE_PEAK) {
+ pmd.arePeakResidentBytesResettable = false;
+ }
+ },
+ expectedInfos,
+ UsedMemoryColumn.COLOR);
+ }
+
+ // No context.
+ checkOverviewColumnInfosAndColor(c,
+ [FIELD],
+ undefined /* no context */,
+ [] /* no infos */,
+ UsedMemoryColumn.COLOR /* blue color */);
+ checkOverviewColumnInfosAndColor(c,
+ [FIELD, FIELD, 0, FIELD],
+ undefined /* no context */,
+ [] /* no infos */,
+ UsedMemoryColumn.COLOR /* blue color */);
+
+ // All resettable.
+ const EXPECTED_RESETTABLE_INFO = {
+ icon: '\u21AA',
+ message: 'Peak RSS since previous memory dump.'
+ };
+ checkPeakColumnInfosAndColor([
+ FIELD | DUMP | RESETTABLE_PEAK
+ ], [EXPECTED_RESETTABLE_INFO]);
+ checkPeakColumnInfosAndColor([
+ FIELD | DUMP | RESETTABLE_PEAK,
+ DUMP /* ignored because there's no field */,
+ 0,
+ FIELD | DUMP | RESETTABLE_PEAK
+ ], [EXPECTED_RESETTABLE_INFO]);
+
+ // All non-resettable.
+ const EXPECTED_NON_RESETTABLE_INFO = {
+ icon: '\u21A6',
+ message: 'Peak RSS since process startup. Finer grained peaks require ' +
+ 'a Linux kernel version \u2265 4.0.'
+ };
+ checkPeakColumnInfosAndColor([
+ FIELD | DUMP | NON_RESETTABLE_PEAK
+ ], [EXPECTED_NON_RESETTABLE_INFO]);
+ checkPeakColumnInfosAndColor([
+ 0,
+ DUMP | RESETTABLE_PEAK /* ignored because there's no field */,
+ FIELD | DUMP | NON_RESETTABLE_PEAK,
+ FIELD | DUMP | NON_RESETTABLE_PEAK
+ ], [EXPECTED_NON_RESETTABLE_INFO]);
+
+ // Combination (warning).
+ const EXPECTED_COMBINATION_INFO = {
+ icon: '\u26A0',
+ message: 'Both resettable and non-resettable peak RSS values were ' +
+ 'provided by the process',
+ color: 'red'
+ };
+ checkPeakColumnInfosAndColor([
+ FIELD | DUMP | NON_RESETTABLE_PEAK,
+ 0,
+ FIELD | DUMP | RESETTABLE_PEAK,
+ 0
+ ], [EXPECTED_COMBINATION_INFO]);
+ });
+
+ test('byteStatColumn', function() {
+ const c = new ByteStatColumn('Stat', 'bytes', (x => x),
+ AggregationMode.DIFF);
+ checkSpanWithColor(c.title, 'Stat',
+ UsedMemoryColumn.COLOR /* blue (column title) */);
+
+ const HAS_OWN_VM_REGIONS = 1 << 2;
+ function checkByteStatColumnInfosAndColor(
+ fieldAndDumpMask, expectedInfos, expectedIsOlderColor) {
+ checkOverviewColumnInfosAndColor(c,
+ fieldAndDumpMask,
+ function(pmd, mask) {
+ if (mask & HAS_OWN_VM_REGIONS) {
+ pmd.vmRegions = [];
+ }
+ },
+ expectedInfos,
+ expectedIsOlderColor ?
+ UsedMemoryColumn.OLDER_COLOR /* light blue */ :
+ UsedMemoryColumn.COLOR /* blue color */);
+ }
+
+ const EXPECTED_ALL_OLDER_VALUES = {
+ icon: '\u26AF',
+ message: 'Older value (only heavy (purple) memory dumps contain ' +
+ 'memory maps).'
+ };
+ const EXPECTED_SOME_OLDER_VALUES = {
+ icon: '\u26AF',
+ message: 'Older value at some selected timestamps (only heavy ' +
+ '(purple) memory dumps contain memory maps).'
+ };
+
+ // No context.
+ checkOverviewColumnInfosAndColor(c,
+ [FIELD],
+ undefined /* no context */,
+ [] /* no infos */,
+ UsedMemoryColumn.COLOR /* blue color */);
+ checkOverviewColumnInfosAndColor(c,
+ [FIELD, FIELD, 0, FIELD],
+ undefined /* no context */,
+ [] /* no infos */,
+ UsedMemoryColumn.COLOR /* blue color */);
+
+ // All process memory dumps have own VM regions.
+ checkByteStatColumnInfosAndColor([
+ FIELD | DUMP | HAS_OWN_VM_REGIONS
+ ], [] /* no infos */, false /* blue color */);
+ checkByteStatColumnInfosAndColor([
+ FIELD | DUMP | HAS_OWN_VM_REGIONS,
+ FIELD | DUMP | HAS_OWN_VM_REGIONS,
+ 0,
+ FIELD | DUMP | HAS_OWN_VM_REGIONS
+ ], [] /* no infos */, false /* blue color */);
+
+ // No process memory dumps have own VM regions.
+ checkByteStatColumnInfosAndColor([
+ FIELD | DUMP
+ ], [EXPECTED_ALL_OLDER_VALUES], true /* light blue */);
+ checkByteStatColumnInfosAndColor([
+ FIELD | DUMP,
+ FIELD | DUMP
+ ], [EXPECTED_ALL_OLDER_VALUES], true /* light blue */);
+
+ // Some process memory dumps don't have own VM regions.
+ checkByteStatColumnInfosAndColor([
+ FIELD | DUMP,
+ 0,
+ FIELD | DUMP
+ ], [EXPECTED_SOME_OLDER_VALUES], true /* light blue */);
+ checkByteStatColumnInfosAndColor([
+ FIELD | DUMP | HAS_OWN_VM_REGIONS,
+ FIELD | DUMP,
+ FIELD | DUMP | HAS_OWN_VM_REGIONS
+ ], [EXPECTED_SOME_OLDER_VALUES], false /* blue */);
+ });
+
+ test('allocatorColumn', function() {
+ const c = new AllocatorColumn('Allocator', 'bytes', (x => x),
+ AggregationMode.MAX);
+ checkColorLegend(c.title, 'Allocator');
+ checkColor(c.color(undefined /* contexts */),
+ undefined /* no color (column cells) */);
+
+ const HAS_HEAP_DUMPS = 1 << 2;
+ const HAS_ALLOCATOR_HEAP_DUMP = 1 << 3;
+ const MISSING_SIZE = 1 << 4;
+ function checkAllocatorColumnInfosAndColor(fieldAndDumpMask,
+ expectedInfos) {
+ checkOverviewColumnInfosAndColor(c,
+ fieldAndDumpMask,
+ function(pmd, mask) {
+ if (mask & HAS_HEAP_DUMPS) {
+ pmd.heapDumps = {};
+ }
+ if (mask & HAS_ALLOCATOR_HEAP_DUMP) {
+ pmd.heapDumps.Allocator = new HeapDump(pmd, 'Allocator');
+ }
+ const mad = new MemoryAllocatorDump(pmd, 'Allocator');
+ if (!(mask & MISSING_SIZE)) {
+ mad.addNumeric('size',
+ new Scalar(sizeInBytes_smallerIsBetter, 7));
+ }
+ pmd.memoryAllocatorDumps = [mad];
+ },
+ expectedInfos,
+ undefined /* no color */);
+ }
+
+ // No context.
+ checkOverviewColumnInfosAndColor(c,
+ [FIELD],
+ undefined /* no context */,
+ [] /* no infos */,
+ undefined /* no color */);
+ checkOverviewColumnInfosAndColor(c,
+ [FIELD, FIELD, 0, FIELD],
+ undefined /* no context */,
+ [] /* no infos */,
+ undefined /* no color */);
+
+ // No infos.
+ checkAllocatorColumnInfosAndColor([
+ FIELD | DUMP
+ ], [] /* no infos */);
+ checkAllocatorColumnInfosAndColor([
+ FIELD | DUMP,
+ FIELD | DUMP | HAS_HEAP_DUMPS,
+ 0,
+ FIELD | DUMP
+ ], [] /* infos */);
+
+ const EXPECTED_ALL_HAVE_ALLOCATOR_HEAP_DUMP = {
+ icon: '\u2630',
+ message: 'Heap dump provided.'
+ };
+ const EXPECTED_SOME_HAVE_ALLOCATOR_HEAP_DUMP = {
+ icon: '\u2630',
+ message: 'Heap dump provided at some selected timestamps.'
+ };
+
+ // All process memory dumps have heap dumps.
+ checkAllocatorColumnInfosAndColor([
+ FIELD | DUMP | HAS_HEAP_DUMPS | HAS_ALLOCATOR_HEAP_DUMP
+ ], [EXPECTED_ALL_HAVE_ALLOCATOR_HEAP_DUMP]);
+ checkAllocatorColumnInfosAndColor([
+ FIELD | DUMP | HAS_HEAP_DUMPS | HAS_ALLOCATOR_HEAP_DUMP,
+ FIELD | DUMP | HAS_HEAP_DUMPS | HAS_ALLOCATOR_HEAP_DUMP,
+ FIELD | DUMP | HAS_HEAP_DUMPS | HAS_ALLOCATOR_HEAP_DUMP
+ ], [EXPECTED_ALL_HAVE_ALLOCATOR_HEAP_DUMP]);
+
+ // Some process memory dumps have heap dumps.
+ checkAllocatorColumnInfosAndColor([
+ 0,
+ FIELD | DUMP | HAS_HEAP_DUMPS | HAS_ALLOCATOR_HEAP_DUMP
+ ], [EXPECTED_SOME_HAVE_ALLOCATOR_HEAP_DUMP]);
+ checkAllocatorColumnInfosAndColor([
+ FIELD | DUMP | HAS_HEAP_DUMPS | HAS_ALLOCATOR_HEAP_DUMP,
+ FIELD | DUMP | HAS_HEAP_DUMPS,
+ FIELD | DUMP | HAS_HEAP_DUMPS | HAS_ALLOCATOR_HEAP_DUMP
+ ], [EXPECTED_SOME_HAVE_ALLOCATOR_HEAP_DUMP]);
+
+ const EXPECTED_ALL_MISSING_SIZE = {
+ icon: '\u26A0',
+ message: 'Size was not provided.',
+ color: 'red'
+ };
+ const EXPECTED_SOME_MISSING_SIZE = {
+ icon: '\u26A0',
+ message: 'Size was not provided at some selected timestamps.',
+ color: 'red'
+ };
+
+ // All process memory dumps are missing allocator size.
+ checkAllocatorColumnInfosAndColor([
+ FIELD | DUMP | MISSING_SIZE
+ ], [EXPECTED_ALL_MISSING_SIZE]);
+ checkAllocatorColumnInfosAndColor([
+ FIELD | DUMP | MISSING_SIZE,
+ FIELD | DUMP | MISSING_SIZE,
+ FIELD | DUMP | MISSING_SIZE
+ ], [EXPECTED_ALL_MISSING_SIZE]);
+
+ // Some process memory dumps use Android memtrack PSS fallback.
+ checkAllocatorColumnInfosAndColor([
+ 0,
+ FIELD | DUMP | MISSING_SIZE
+ ], [EXPECTED_SOME_MISSING_SIZE]);
+ checkAllocatorColumnInfosAndColor([
+ FIELD | DUMP | MISSING_SIZE,
+ FIELD | DUMP,
+ FIELD | DUMP | MISSING_SIZE
+ ], [EXPECTED_SOME_MISSING_SIZE]);
+
+ // Combination of heap dump and memtrack fallback infos.
+ checkAllocatorColumnInfosAndColor([
+ FIELD | DUMP | MISSING_SIZE | HAS_HEAP_DUMPS |
+ HAS_ALLOCATOR_HEAP_DUMP
+ ], [
+ EXPECTED_ALL_HAVE_ALLOCATOR_HEAP_DUMP,
+ EXPECTED_ALL_MISSING_SIZE
+ ]);
+ checkAllocatorColumnInfosAndColor([
+ FIELD | DUMP | HAS_HEAP_DUMPS | HAS_ALLOCATOR_HEAP_DUMP,
+ FIELD | DUMP,
+ FIELD | DUMP | MISSING_SIZE
+ ], [
+ EXPECTED_SOME_HAVE_ALLOCATOR_HEAP_DUMP,
+ EXPECTED_SOME_MISSING_SIZE
+ ]);
+ });
+
+ test('tracingColumn', function() {
+ const c = new TracingColumn('Tracing', 'bytes', (x => x),
+ AggregationMode.DIFF);
+ checkSpanWithColor(c.title, 'Tracing',
+ TracingColumn.COLOR /* expected column title gray color */);
+ checkColor(c.color(undefined /* contexts */),
+ TracingColumn.COLOR /* expected column cells gray color */);
+ });
+});
+</script>