blob: 21bf30c37aaf5ddafc4ae6aed1827ab85b46514d [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="/base/base.html">
<link rel="import" href="/base/units/size_in_bytes_span.html">
<script>
'use strict';
/**
* @fileoverview Helper code for memory dump sub-views.
*/
tr.exportTo('tr.c.analysis', function() {
/**
* A table builder column for displaying memory dump data.
*
* @constructor
*/
function MemoryColumn(name, title, units, cellGetter) {
this.name = name;
this.title = title;
this.units = units;
this.cell = cellGetter;
}
MemoryColumn.fromRows = function(rows, cellKey, opt_titleBuilder) {
var columnTraits = {};
function gatherTraits(row) {
if (row === undefined)
return;
var attrCells = row[cellKey];
tr.b.iterItems(attrCells, function(attrName, attrCell) {
if (attrCell === undefined)
return;
var attrValue = attrCell.attr;
if (attrValue === undefined)
return;
var existingTraits = columnTraits[attrName];
if (existingTraits === undefined) {
columnTraits[attrName] = {
constructor: attrValue.constructor,
units: attrValue.units
};
return;
}
if (existingTraits.constructor !== attrValue.constructor ||
existingTraits.units !== attrValue.units) {
existingTraits.constructor = tr.model.UnknownAttribute;
existingTraits.units = undefined;
}
});
if (row.subRows !== undefined)
row.subRows.forEach(gatherTraits);
};
rows.forEach(gatherTraits);
var titleBuilder = opt_titleBuilder || tr.b.identity;
var columns = [];
tr.b.iterItems(columnTraits, function(columnName, columnTraits) {
var cellGetter = fieldGetter(cellKey, columnName);
var title = titleBuilder(columnName);
columns.push(MemoryColumn.fromAttributeTraits(
columnName, title, columnTraits, cellGetter));
});
return columns;
};
MemoryColumn.fromAttributeTraits = function(name, title, traits, cellGetter) {
var constructor;
if (traits.constructor === tr.model.ScalarAttribute)
constructor = ScalarMemoryColumn;
else
constructor = MemoryColumn;
return new constructor(name, title, traits.units, cellGetter);
};
MemoryColumn.spaceEqually = function(columns) {
var columnWidth = (100 / columns.length).toFixed(3) + '%';
columns.forEach(function(column) {
column.width = columnWidth;
});
};
/**
* Sort a list of memory columns according to a list of importance rules.
* This function modifies the original array and doesn't return anything.
*
* The list of importance rules contains objects with mandatory 'importance'
* numeric fields and optional 'condition' string or regex fields. Example:
*
* var importanceRules = [
* {
* condition: 'page_size',
* importance: 8
* },
* {
* condition: /size/,
* importance: 10
* },
* {
* // No condition: matches all columns.
* importance: 9
* }
* ];
*
* The importance of a column is determined by the first rule whose condition
* matches the column's name, so the rules above will sort a generic list of
* columns into three groups as follows:
*
* [most important, left in the resulting table]
* 1. columns whose name contains 'size' excluding 'page_size' because it
* would have already matched the first rule (Note that string matches
* must be exact so a column named 'page_size2' would not match the
* first rule and would therefore belong to this group).
* 2. columns whose name does not contain 'size'.
* 3. columns whose name is 'page_size'.
* [least important, right in the resulting table]
*
* where the order within each group is unmodified (stable sort).
*/
MemoryColumn.sortByImportance = function(columns, importanceRules) {
var positions = columns.map(function(column, srcIndex) {
return {
importance: column.getImportance(importanceRules),
srcIndex: srcIndex,
column: column
};
});
positions.sort(function(a, b) {
// Keep existing order of columns if they have the same importance
// (stable sort).
if (a.importance === b.importance)
return a.srcIndex - b.srcIndex;
// Sort columns in descending order of importance.
return b.importance - a.importance;
});
positions.forEach(function(position, dstIndex) {
columns[dstIndex] = position.column;
});
};
MemoryColumn.prototype = {
attr: function(row) {
var cell = this.cell(row);
if (cell === undefined)
return undefined;
return cell.attr;
},
value: function(row) {
var attr = this.attr(row);
if (attr === undefined)
return '';
return this.formatDefinedAttribute(attr);
},
formatDefinedAttribute: function(attr) {
return String(attr.value);
},
cmp: function(rowA, rowB) {
var attrA = this.attr(rowA);
var attrB = this.attr(rowB);
if (attrA === undefined && attrB === undefined)
return 0;
if (attrA === undefined)
return -1;
if (attrB === undefined)
return 1;
return this.compareDefinedAttributes(attrA, attrB);
},
compareDefinedAttributes: function(attrA, attrB) {
var strA = String(attrA.value);
var strB = String(attrB.value);
return strA.localeCompare(strB);
},
getImportance: function(importanceRules) {
if (importanceRules.length === 0)
return 0;
// Find the first matching rule.
for (var i = 0; i < importanceRules.length; i++) {
var importanceRule = importanceRules[i];
if (this.matchesNameCondition(importanceRule.condition))
return importanceRule.importance;
}
// No matching rule. Return lower importance than all rules.
var minImportance = importanceRules[0].importance;
for (var i = 1; i < importanceRules.length; i++) {
minImportance = Math.min(minImportance, importanceRules[i].importance);
}
return minImportance - 1;
},
matchesNameCondition: function(condition) {
// Rules without conditions match all columns.
if (condition === undefined)
return true;
// String conditions must match the column name exactly.
if (typeof(condition) === 'string')
return this.name === condition;
// If the condition is not a string, assume it is a RegExp.
return condition.test(this.name);
}
};
/**
* @constructor
*/
function ScalarMemoryColumn(name, title, units, cellGetter) {
MemoryColumn.call(this, name, title, units, cellGetter);
}
ScalarMemoryColumn.prototype = {
__proto__: MemoryColumn.prototype,
formatDefinedAttribute: function(attr) {
if (this.units === 'bytes') {
var sizeEl = document.createElement('tr-b-u-size-in-bytes-span');
sizeEl.numBytes = attr.value;
return sizeEl;
}
return MemoryColumn.prototype.formatDefinedAttribute.call(this, attr);
},
compareDefinedAttributes: function(attrA, attrB) {
return attrA.value - attrB.value;
}
};
/**
* @constructor
*/
function MemoryCell(attr) {
this.attr = attr;
}
function fieldGetter(/* fields */) {
var fields = tr.b.asArray(arguments);
return function(row) {
var value = row;
for (var i = 0; i < fields.length; i++)
value = value[fields[i]];
return value;
};
}
/** Limit for the number of sub-rows for recursive table row expansion. */
var RECURSIVE_EXPANSION_MAX_SUB_ROW_COUNT = 10;
function expandTableRowsRecursively(table) {
function expandRowRecursively(row) {
if (row.subRows === undefined || row.subRows.length === 0)
return;
if (row.subRows.length > RECURSIVE_EXPANSION_MAX_SUB_ROW_COUNT)
return;
table.setExpandedForTableRow(row, true);
row.subRows.forEach(expandRowRecursively);
}
table.tableRows.forEach(expandRowRecursively);
}
return {
MemoryColumn: MemoryColumn,
ScalarMemoryColumn: ScalarMemoryColumn,
MemoryCell: MemoryCell,
fieldGetter: fieldGetter,
expandTableRowsRecursively: expandTableRowsRecursively
};
});
</script>