| <!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> |