Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(300)

Unified Diff: tracing/tracing/ui/analysis/memory_dump_heap_details_pane.html

Issue 2347813002: [tracing] Heap dump UI overhaul (Closed)
Patch Set: Improve focus retention and add tests Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: tracing/tracing/ui/analysis/memory_dump_heap_details_pane.html
diff --git a/tracing/tracing/ui/analysis/memory_dump_heap_details_pane.html b/tracing/tracing/ui/analysis/memory_dump_heap_details_pane.html
index ac842ce8d61ff85d89a7eedac85c59a3bb250221..7b53e66ab5cfa3f0693b40460410aec31fcff5c4 100644
--- a/tracing/tracing/ui/analysis/memory_dump_heap_details_pane.html
+++ b/tracing/tracing/ui/analysis/memory_dump_heap_details_pane.html
@@ -8,12 +8,14 @@ found in the LICENSE file.
<link rel="import" href="/tracing/base/color_scheme.html">
<link rel="import" href="/tracing/base/iteration_helpers.html">
<link rel="import" href="/tracing/base/multi_dimensional_view.html">
-<link rel="import" href="/tracing/base/unit.html">
+<link rel="import" href="/tracing/ui/analysis/memory_dump_heap_details_breakdown_view.html">
+<link rel="import" href="/tracing/ui/analysis/memory_dump_heap_details_path_view.html">
+<link rel="import" href="/tracing/ui/analysis/memory_dump_heap_details_util.html">
<link rel="import" href="/tracing/ui/analysis/memory_dump_sub_view_util.html">
<link rel="import" href="/tracing/ui/analysis/stacked_pane.html">
<link rel="import" href="/tracing/ui/base/dom_helpers.html">
+<link rel="import" href="/tracing/ui/base/drag_handle.html">
<link rel="import" href="/tracing/ui/base/info_bar.html">
-<link rel="import" href="/tracing/ui/base/table.html">
<link rel="import" href="/tracing/value/numeric.html">
<dom-module id='tr-ui-a-memory-dump-heap-details-pane'>
@@ -38,7 +40,7 @@ found in the LICENSE file.
#label {
flex: 1 1 auto;
padding: 8px;
- font-size: 15px;
+ font-size: 15px;
font-weight: bold;
}
@@ -62,11 +64,24 @@ found in the LICENSE file.
text-align: center;
}
- #table {
+ #split_view {
display: none; /* Hide until memory allocator dumps are set. */
flex: 1 0 auto;
align-self: stretch;
- font-size: 12px;
+ flex-direction: row;
+ }
+
+ #path_view {
+ width: 50%;
+ }
+
+ #breakdown_view {
+ flex: 1 1 auto;
+ width: 0;
+ }
+
+ #path_view, #breakdown_view {
+ overflow-x: auto; /* Show scrollbar if necessary. */
}
</style>
<div id="header">
@@ -79,8 +94,16 @@ found in the LICENSE file.
<div id="contents">
<tr-ui-b-info-bar id="info_bar" class="info-bar-hidden">
</tr-ui-b-info-bar>
+
<div id="info_text">No heap dump selected</div>
- <tr-ui-b-table id="table"></tr-ui-b-table>
+
+ <div id="split_view">
+ <tr-ui-a-memory-dump-heap-details-path-view id="path_view">
+ </tr-ui-a-memory-dump-heap-details-path-view>
+ <tr-ui-b-drag-handle id="drag_handle"></tr-ui-b-drag-handle>
+ <tr-ui-a-memory-dump-heap-details-breakdown-view id="breakdown_view">
+ </tr-ui-a-memory-dump-heap-details-breakdown-view>
+ </div>
</div>
</template>
</dom-module>
@@ -96,107 +119,160 @@ tr.exportTo('tr.ui.analysis', function() {
var MultiDimensionalViewBuilder = tr.b.MultiDimensionalViewBuilder;
var TotalState = tr.b.MultiDimensionalViewNode.TotalState;
- /** @{enum} */
- var RowDimension = {
- ROOT: -1,
- STACK_FRAME: 0,
- OBJECT_TYPE: 1
- };
-
- var LATIN_SMALL_LETTER_F_WITH_HOOK = String.fromCharCode(0x0192);
- var CIRCLED_LATIN_CAPITAL_LETTER_T = String.fromCharCode(0x24C9);
-
/** @{constructor} */
- function HeapDumpNodeTitleColumn(title) {
- tr.ui.analysis.TitleColumn.call(this, title);
+ function HeapDumpTreeNode(
+ stackFrameNodes, dimension, title, heavyView, parentNode) {
+ this.dimension = dimension;
+ this.title = title;
+ this.parentNode = parentNode;
+
+ this.heavyView_ = heavyView;
+ this.stackFrameNodes_ = stackFrameNodes;
+ this.lazyCells_ = undefined;
+ this.lazyChildNodes_ = undefined;
}
- HeapDumpNodeTitleColumn.prototype = {
- __proto__: tr.ui.analysis.TitleColumn.prototype,
+ HeapDumpTreeNode.prototype = {
+ get minDisplayedTotalState_() {
+ if (this.heavyView_) {
+ // Show lower-bound and exact values in heavy views.
+ return TotalState.LOWER_BOUND;
+ } else {
+ // Show only exact values in tree view.
+ return TotalState.EXACT;
+ }
+ },
- formatTitle: function(row) {
- var title = row.title;
- var dimension = row.dimension;
- switch (dimension) {
- case RowDimension.ROOT:
- return title;
+ get childNodes() {
+ if (!this.lazyChildNodes_) {
+ this.lazyChildNodes_ = new Map();
+ this.addDimensionChildNodes_(
+ tr.ui.analysis.HeapDetailsRowDimension.STACK_FRAME, 0);
+ this.addDimensionChildNodes_(
+ tr.ui.analysis.HeapDetailsRowDimension.OBJECT_TYPE, 1);
+ this.releaseStackFrameNodesIfPossible_();
+ }
+ return this.lazyChildNodes_;
+ },
- case RowDimension.STACK_FRAME:
- case RowDimension.OBJECT_TYPE:
- return this.formatSubRow_(title, dimension);
+ get cells() {
+ if (!this.lazyCells_) {
+ this.addCells_();
+ this.releaseStackFrameNodesIfPossible_();
+ }
+ return this.lazyCells_;
+ },
- default:
- throw new Error('Invalid row dimension: ' + row.dimension);
+ releaseStackFrameNodesIfPossible_: function() {
+ if (this.lazyCells_ && this.lazyChildNodes_) {
+ // Don't unnecessarily hold a reference to the stack frame nodes when
+ // we don't need them anymore.
+ this.stackFrameNodes_ = undefined;
}
},
- cmp: function(rowA, rowB) {
- if (rowA.dimension !== rowB.dimension)
- return rowA.dimension - rowB.dimension;
- return tr.ui.analysis.TitleColumn.prototype.cmp.call(this, rowA, rowB);
+ addDimensionChildNodes_: function(dimension, dimensionIndex) {
+ // Child title -> Timestamp (list index) -> Child
+ // MultiDimensionalViewNode.
+ var dimensionChildTitleToStackFrameNodes = tr.b.invertArrayOfDicts(
+ this.stackFrameNodes_,
+ node => this.convertStackFrameNodeDimensionToChildDict_(
+ node, dimensionIndex));
+
+ // Child title (list index) -> Child HeapDumpTreeNode.
+ var dimensionChildNodes = [];
+ tr.b.iterItems(dimensionChildTitleToStackFrameNodes,
+ function(childTitle, childStackFrameNodes) {
+ dimensionChildNodes.push(new HeapDumpTreeNode(childStackFrameNodes,
+ dimension, childTitle, this.heavyView_, this));
+ }, this);
+ this.lazyChildNodes_.set(dimension, dimensionChildNodes);
},
- formatSubRow_: function(title, dimension) {
- var titleEl = document.createElement('span');
+ convertStackFrameNodeDimensionToChildDict_: function(
+ stackFrameNode, dimensionIndex) {
+ var childDict = {};
+
+ var displayedChildrenTotalSize = 0;
+ var displayedChildrenTotalCount = 0;
+ var hasDisplayedChildren = false;
+ var allDisplayedChildrenHaveDisplayedCounts = true;
+ for (var child of stackFrameNode.children[dimensionIndex].values()) {
+ if (child.values[0].totalState < this.minDisplayedTotalState_)
+ continue;
+ if (child.values[1].totalState < this.minDisplayedTotalState_)
+ allDisplayedChildrenHaveDisplayedCounts = false;
+ childDict[child.title[dimensionIndex]] = child;
+ displayedChildrenTotalSize += child.values[0].total;
+ displayedChildrenTotalCount += child.values[1].total;
+ hasDisplayedChildren = true;
+ }
- var symbolEl = document.createElement('span');
- var symbolColorName;
- if (dimension === RowDimension.STACK_FRAME) {
- Polymer.dom(symbolEl).textContent = LATIN_SMALL_LETTER_F_WITH_HOOK;
- symbolEl.title = 'Stack frame';
- symbolColorName = 'heap_dump_stack_frame';
- } else {
- Polymer.dom(symbolEl).textContent = CIRCLED_LATIN_CAPITAL_LETTER_T;
- symbolEl.title = 'Object type';
- symbolColorName = 'heap_dump_object_type';
+ var nodeTotalSize = stackFrameNode.values[0].total;
+ var nodeTotalCount = stackFrameNode.values[1].total;
+
+ // Add '<other>' node if necessary in tree-view.
+ var hasUnclassifiedSizeOrCount =
+ displayedChildrenTotalSize < nodeTotalSize ||
+ displayedChildrenTotalCount < nodeTotalCount;
+ if (!this.heavyView_ && hasUnclassifiedSizeOrCount &&
+ hasDisplayedChildren) {
+ var otherTitle = stackFrameNode.title.slice();
+ otherTitle[dimensionIndex] = '<other>';
+ var otherNode = new tr.b.MultiDimensionalViewNode(otherTitle, 2);
+ childDict[otherTitle[dimensionIndex]] = otherNode;
+
+ // '<other>' node size.
+ otherNode.values[0].total = nodeTotalSize - displayedChildrenTotalSize;
+ otherNode.values[0].totalState = this.minDisplayedTotalState_;
+
+ // '<other>' node allocation count.
+ otherNode.values[1].total =
+ nodeTotalCount - displayedChildrenTotalCount;
+ // Don't show allocation count of the '<other>' node if there is a
+ // displayed child node that did NOT display allocation count.
+ otherNode.values[1].totalState =
+ allDisplayedChildrenHaveDisplayedCounts ?
+ this.minDisplayedTotalState_ : TotalState.NOT_PROVIDED;
}
- symbolEl.style.color =
- tr.b.ColorScheme.getColorForReservedNameAsString(symbolColorName);
- symbolEl.style.paddingRight = '4px';
- symbolEl.style.cursor = 'help';
- symbolEl.style.weight = 'bold';
- Polymer.dom(titleEl).appendChild(symbolEl);
- Polymer.dom(titleEl).appendChild(document.createTextNode(title));
+ return childDict;
+ },
- return titleEl;
+ addCells_: function() {
+ // Transform a chronological list of heap stack frame tree nodes into a
+ // dictionary of cells (where each cell contains a chronological list
+ // of the values of its numeric).
+ this.lazyCells_ = tr.ui.analysis.createCells(this.stackFrameNodes_,
+ function(stackFrameNode) {
+ var size = stackFrameNode.values[0].total;
+ var numerics = {
+ 'Size': new ScalarNumeric(sizeInBytes_smallerIsBetter, size)
+ };
+ var countValue = stackFrameNode.values[1];
+ if (countValue.totalState >= this.minDisplayedTotalState_) {
+ var count = countValue.total;
+ numerics['Count'] = new ScalarNumeric(
+ count_smallerIsBetter, count);
+ numerics['Average size per allocation'] = new ScalarNumeric(
+ sizeInBytes_smallerIsBetter, count === 0 ? 0 : size / count);
+ }
+ return numerics;
+ }, this);
}
};
- var COLUMN_RULES = [
- {
- condition: 'Size',
- importance: 3,
- columnConstructor: tr.ui.analysis.DetailsNumericMemoryColumn
- },
- {
- condition: 'Count',
- importance: 2,
- columnConstructor: tr.ui.analysis.DetailsNumericMemoryColumn
- },
- {
- condition: 'Average size per allocation',
- importance: 1,
- columnConstructor: tr.ui.analysis.DetailsNumericMemoryColumn
- },
- {
- importance: 0,
- columnConstructor: tr.ui.analysis.DetailsNumericMemoryColumn
- }
- ];
-
Polymer({
is: 'tr-ui-a-memory-dump-heap-details-pane',
behaviors: [tr.ui.analysis.StackedPane],
created: function() {
this.heapDumps_ = undefined;
- this.aggregationMode_ = undefined;
this.viewMode_ = undefined;
+ this.aggregationMode_ = undefined;
},
ready: function() {
- this.$.table.selectionMode = tr.ui.b.TableFormat.SelectionMode.ROW;
this.$.info_bar.message = 'Note: Values displayed in the heavy view ' +
'are lower bounds (except for the root).';
@@ -211,15 +287,30 @@ tr.exportTo('tr.ui.analysis', function() {
},
{
label: 'Top-down (Heavy)',
- value: MultiDimensionalViewBuilder.ViewType
- .TOP_DOWN_HEAVY_VIEW
+ value:
+ MultiDimensionalViewBuilder.ViewType.TOP_DOWN_HEAVY_VIEW
},
{
label: 'Bottom-up (Heavy)',
- value: MultiDimensionalViewBuilder.ViewType
- .BOTTOM_UP_HEAVY_VIEW
+ value:
+ MultiDimensionalViewBuilder.ViewType.BOTTOM_UP_HEAVY_VIEW
}
]));
+
+ this.$.drag_handle.target = this.$.path_view;
+ this.$.drag_handle.horizontal = false;
+
+ // If the user selects a node in the path view, show its children in the
+ // breakdown view.
+ this.$.path_view.addEventListener('selected-node-changed', (function(e) {
+ this.$.breakdown_view.displayedNode = this.$.path_view.selectedNode;
+ }).bind(this));
+
+ // If the user double-clicks on a node in the breakdown view, select the
+ // node in the path view.
+ this.$.breakdown_view.addEventListener('enter-node', (function(e) {
+ this.$.path_view.selectedNode = e.node;
+ }).bind(this));
},
/**
@@ -246,7 +337,8 @@ tr.exportTo('tr.ui.analysis', function() {
set aggregationMode(aggregationMode) {
this.aggregationMode_ = aggregationMode;
- this.scheduleRebuild_();
+ this.$.path_view.aggregationMode = aggregationMode;
+ this.$.breakdown_view.aggregationMode = aggregationMode;
},
get aggregationMode() {
@@ -277,31 +369,39 @@ tr.exportTo('tr.ui.analysis', function() {
this.heapDumps_.length === 0) {
// Show the info text (hide the table and the view mode selector).
this.$.info_text.style.display = 'block';
- this.$.table.style.display = 'none';
+ this.$.split_view.style.display = 'none';
this.$.view_mode_container.style.display = 'none';
this.$.info_bar.visible = false;
-
- this.$.table.clear();
- this.$.table.rebuild();
+ this.$.path_view.selectedNode = undefined;
return;
}
// Show the table and the view mode selector (hide the info text).
this.$.info_text.style.display = 'none';
- this.$.table.style.display = 'block';
+ this.$.split_view.style.display = 'flex';
this.$.view_mode_container.style.display = 'block';
// Show the info bar if in heavy view mode.
this.$.info_bar.visible = this.heavyView;
+ this.$.path_view.selectedNode = this.createHeapTree_();
+ this.$.path_view.rebuild();
+ this.$.breakdown_view.rebuild();
+ },
+
+ createHeapTree_: function() {
+ var definedHeapDump = tr.b.findFirstInArray(this.heapDumps_);
+ if (definedHeapDump === undefined)
+ return undefined;
+
+ // The title of the root node is the name of the allocator.
+ var rootRowTitle = definedHeapDump.allocatorName;
+
var stackFrameTrees = this.createStackFrameTrees_(this.heapDumps_);
- var rows = this.createRows_(stackFrameTrees);
- var columns = this.createColumns_(rows);
- this.$.table.tableRows = rows;
- this.$.table.tableColumns = columns;
- this.$.table.rebuild();
- tr.ui.analysis.expandTableRowsRecursively(this.$.table);
+ return new HeapDumpTreeNode(stackFrameTrees,
+ tr.ui.analysis.HeapDetailsRowDimension.ROOT, rootRowTitle,
+ this.heavyView);
},
createStackFrameTrees_: function(heapDumps) {
@@ -330,151 +430,9 @@ tr.exportTo('tr.ui.analysis', function() {
return builder.buildView(this.viewMode);
}, this);
- },
-
- createRows_: function(stackFrameTrees) {
- var definedHeapDump = tr.b.findFirstInArray(this.heapDumps);
- if (definedHeapDump === undefined)
- return [];
-
- // The title of the root row is the name of the allocator.
- var rootRowTitle = definedHeapDump.allocatorName;
- return [this.createHeapRowRecursively_(
- stackFrameTrees, RowDimension.ROOT, rootRowTitle)];
- },
-
- createHeapRowRecursively_: function(nodes, dimension, title) {
- // Transform a chronological list of stack frame tree nodes into a
- // dictionary of cells (where each cell contains a chronological list
- // of the values of its numeric).
- var cells = tr.ui.analysis.createCells(nodes, function(node) {
- var size = node.values[0].total;
- var row = {
- 'Size': new ScalarNumeric(sizeInBytes_smallerIsBetter, size)
- };
- var countValue = node.values[1];
- if (countValue.totalState >= this.minDisplayedTotalState_) {
- var count = countValue.total;
- row['Count'] = new ScalarNumeric(count_smallerIsBetter, count);
- row['Average size per allocation'] = new ScalarNumeric(
- sizeInBytes_smallerIsBetter, count === 0 ? 0 : size / count);
- }
- return row;
- }, this);
-
- var row = {
- dimension: dimension,
- title: title,
- contexts: nodes,
- cells: cells
- };
-
- // Recursively create sub-rows for children (if applicable).
- var stackFrameSubRows = this.createHeapDimensionSubRowsRecursively_(
- nodes, RowDimension.STACK_FRAME);
- var objectTypeSubRows = this.createHeapDimensionSubRowsRecursively_(
- nodes, RowDimension.OBJECT_TYPE);
- var subRows = stackFrameSubRows.concat(objectTypeSubRows);
- if (subRows.length > 0)
- row.subRows = subRows;
-
- return row;
- },
-
- get minDisplayedTotalState_() {
- if (this.heavyView) {
- // Show lower-bound and exact values in heavy views.
- return TotalState.LOWER_BOUND;
- } else {
- // Show only exact values in tree view.
- return TotalState.EXACT;
- }
- },
-
- createHeapDimensionSubRowsRecursively_: function(nodes, dimension) {
- // Sub-row name (list index) -> Timestamp (list index) -> Child
- // MultiDimensionalViewNode.
- var dimensionGroupedChildNodes = tr.b.dictionaryValues(
- tr.b.invertArrayOfDicts(nodes, function(node) {
- var childDict = {};
- var displayedChildrenTotalSize = 0;
- var displayedChildrenTotalCount = 0;
- var hasDisplayedChildren = false;
- var allDisplayedChildrenHaveDisplayedCounts = true;
- for (var child of node.children[dimension].values()) {
- if (child.values[0].totalState < this.minDisplayedTotalState_)
- continue;
- if (child.values[1].totalState < this.minDisplayedTotalState_)
- allDisplayedChildrenHaveDisplayedCounts = false;
- childDict[child.title[dimension]] = child;
- displayedChildrenTotalSize += child.values[0].total;
- displayedChildrenTotalCount += child.values[1].total;
- hasDisplayedChildren = true;
- }
-
- var nodeTotalSize = node.values[0].total;
- var nodeTotalCount = node.values[1].total;
-
- // Add '<other>' node if necessary in tree-view.
- var hasUnclassifiedSizeOrCount =
- displayedChildrenTotalSize < nodeTotalSize ||
- displayedChildrenTotalCount < nodeTotalCount;
- if (!this.heavyView && hasUnclassifiedSizeOrCount &&
- hasDisplayedChildren) {
- var otherTitle = node.title.slice();
- otherTitle[dimension] = '<other>';
- childDict['<other>'] = {
- title: otherTitle,
- values: [
- {
- self: 0,
- total: nodeTotalSize - displayedChildrenTotalSize,
- totalState: this.minDisplayedTotalState_
- },
- {
- self: 0,
- total: nodeTotalCount - displayedChildrenTotalCount,
- // Don't show allocation count of the '<other>' node if
- // there is a displayed child node that did NOT display
- // allocation count.
- totalState: allDisplayedChildrenHaveDisplayedCounts ?
- this.minDisplayedTotalState_ : TotalState.NOT_PROVIDED
- }
- ],
- children: [new Map(), new Map()]
- };
- }
-
- return childDict;
- }, this));
-
- // Sub-row name (list index) -> Sub-row.
- return dimensionGroupedChildNodes.map(function(subRowNodes) {
- var subRowTitle = tr.b.findFirstInArray(subRowNodes).title[dimension];
- return this.createHeapRowRecursively_(
- subRowNodes, dimension, subRowTitle);
- }, this);
- },
-
- createColumns_: function(rows) {
- var titleColumn = new HeapDumpNodeTitleColumn('Stack frame');
- titleColumn.width = '500px';
-
- var numericColumns = tr.ui.analysis.MemoryColumn.fromRows(rows, {
- cellKey: 'cells',
- aggregationMode: this.aggregationMode_,
- rules: COLUMN_RULES
- });
- tr.ui.analysis.MemoryColumn.spaceEqually(numericColumns);
-
- var columns = [titleColumn].concat(numericColumns);
- return columns;
}
- });
+ });
- return {
- // Exported for testing.
- RowDimension: RowDimension
- };
+ return {};
});
</script>

Powered by Google App Engine
This is Rietveld 408576698