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

Unified Diff: runtime/bin/vmservice/client/lib/src/elements/heap_profile.dart

Issue 342513004: Visual refresh of allocation profile page (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 6 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: runtime/bin/vmservice/client/lib/src/elements/heap_profile.dart
diff --git a/runtime/bin/vmservice/client/lib/src/elements/heap_profile.dart b/runtime/bin/vmservice/client/lib/src/elements/heap_profile.dart
index 9626735d85fe3643820573a6b94d3c03f006dcf5..850710c2321446c2894e2a1fba9eef7bcda3b455 100644
--- a/runtime/bin/vmservice/client/lib/src/elements/heap_profile.dart
+++ b/runtime/bin/vmservice/client/lib/src/elements/heap_profile.dart
@@ -8,21 +8,28 @@ import 'dart:html';
import 'observatory_element.dart';
import 'package:observatory/app.dart';
import 'package:observatory/service.dart';
-import 'package:logging/logging.dart';
+import 'package:observatory/elements.dart';
import 'package:polymer/polymer.dart';
+class ClassSortedTable extends SortedTable {
+
+ ClassSortedTable(columns) : super(columns);
+
+ @override
+ dynamic getSortKeyFor(int row, int col) {
+ if (col == 0) {
+ // Use class name as sort key.
+ return rows[row].values[col].name;
+ }
+ return super.getSortKeyFor(row, col);
+ }
+}
+
/// Displays an Error response.
@CustomTag('heap-profile')
class HeapProfileElement extends ObservatoryElement {
- // Indexes into VM provided map.
- static const ALLOCATED_BEFORE_GC = 0;
- static const ALLOCATED_BEFORE_GC_SIZE = 1;
- static const LIVE_AFTER_GC = 2;
- static const LIVE_AFTER_GC_SIZE = 3;
- static const ALLOCATED_SINCE_GC = 4;
- static const ALLOCATED_SINCE_GC_SIZE = 5;
- static const ACCUMULATED = 6;
- static const ACCUMULATED_SIZE = 7;
+ @observable String lastServiceGC = '---';
+ @observable String lastAccumulatorReset = '---';
// Pie chart of new space usage.
var _newPieDataTable;
@@ -32,92 +39,219 @@ class HeapProfileElement extends ObservatoryElement {
var _oldPieDataTable;
var _oldPieChart;
- @observable SortedTable classTable;
+ @observable ClassSortedTable classTable;
+ var _classTableBody;
@published ServiceMap profile;
+ @observable Isolate isolate;
+
+ void _trace(Stopwatch sw, String label) {
+ print('$label took ${sw.elapsedMicroseconds} us.');
+ sw.reset();
+ }
+
+ void _traceMillis(Stopwatch sw, String label) {
+ print('$label took ${sw.elapsedMilliseconds} ms.');
+ sw.reset();
+ }
+
HeapProfileElement.created() : super.created() {
+ // Create pie chart models.
_newPieDataTable = new DataTable();
_newPieDataTable.addColumn('string', 'Type');
_newPieDataTable.addColumn('number', 'Size');
_oldPieDataTable = new DataTable();
_oldPieDataTable.addColumn('string', 'Type');
_oldPieDataTable.addColumn('number', 'Size');
+
+ // Create class table model.
var columns = [
new SortedTableColumn('Class'),
- new SortedTableColumn.withFormatter('Accumulator Size (New)',
+ new SortedTableColumn(''), // Spacer column.
+ new SortedTableColumn.withFormatter('Accumulated Size (New)',
Utils.formatSize),
- new SortedTableColumn.withFormatter('Accumulator (New)',
+ new SortedTableColumn.withFormatter('Accumulated Instances',
Utils.formatCommaSeparated),
- new SortedTableColumn.withFormatter('Current Size (New)',
+ new SortedTableColumn.withFormatter('Current Size',
Utils.formatSize),
- new SortedTableColumn.withFormatter('Current (New)',
+ new SortedTableColumn.withFormatter('Current Instances',
Utils.formatCommaSeparated),
+ new SortedTableColumn(''), // Spacer column.
new SortedTableColumn.withFormatter('Accumulator Size (Old)',
Utils.formatSize),
- new SortedTableColumn.withFormatter('Accumulator (Old)',
+ new SortedTableColumn.withFormatter('Accumulator Instances',
Utils.formatCommaSeparated),
- new SortedTableColumn.withFormatter('Current Size (Old)',
+ new SortedTableColumn.withFormatter('Current Size',
Utils.formatSize),
- new SortedTableColumn.withFormatter('Current (Old)',
+ new SortedTableColumn.withFormatter('Current Instances',
Utils.formatCommaSeparated)
];
- classTable = new SortedTable(columns);
- classTable.sortColumnIndex = 1;
+ classTable = new ClassSortedTable(columns);
+ // By default, start with accumulated new space bytes.
+ classTable.sortColumnIndex = 2;
}
- void enteredView() {
- super.enteredView();
+ @override
+ void attached() {
+ super.attached();
+ // Grab the pie chart divs.
_newPieChart = new Chart('PieChart',
shadowRoot.querySelector('#newPieChart'));
- _newPieChart.options['title'] = 'New Space';
_oldPieChart = new Chart('PieChart',
shadowRoot.querySelector('#oldPieChart'));
- _oldPieChart.options['title'] = 'Old Space';
- _draw();
+ _classTableBody = shadowRoot.querySelector('#classTableBody');
}
- void _updateChartData() {
- if ((profile == null) || (profile['members'] is! List) ||
- (profile['members'].length == 0)) {
- return;
+ void _updatePieCharts() {
+ assert(profile != null);
+ _newPieDataTable.clearRows();
+ var isolate = profile.isolate;
+ _newPieDataTable.addRow(['Used', isolate.newSpace.used]);
+ _newPieDataTable.addRow(['Free',
+ isolate.newSpace.capacity - isolate.newSpace.used]);
+ _newPieDataTable.addRow(['External', isolate.newSpace.external]);
+ _oldPieDataTable.clearRows();
+ _oldPieDataTable.addRow(['Used', isolate.oldSpace.used]);
+ _oldPieDataTable.addRow(['Free',
+ isolate.oldSpace.capacity - isolate.oldSpace.used]);
+ _oldPieDataTable.addRow(['External', isolate.oldSpace.external]);
+ }
+
+ void _updateClasses() {
+ for (ServiceMap clsAllocations in profile['members']) {
+ Class cls = clsAllocations['class'];
+ if (cls == null) {
+ continue;
+ }
+ cls.newSpace.update(clsAllocations['new']);
+ cls.oldSpace.update(clsAllocations['old']);
}
- assert(classTable != null);
+ }
+
+ void _updateClassTable() {
classTable.clearRows();
- for (ServiceMap cls in profile['members']) {
- if (_classHasNoAllocations(cls)) {
+ for (ServiceMap clsAllocations in profile['members']) {
+ Class cls = clsAllocations['class'];
+ if (cls == null) {
+ continue;
+ }
+ if (cls.hasNoAllocations) {
// If a class has no allocations, don't display it.
continue;
}
- var row = [cls['class'],
- _combinedTableColumnValue(cls, 1),
- _combinedTableColumnValue(cls, 2),
- _combinedTableColumnValue(cls, 3),
- _combinedTableColumnValue(cls, 4),
- _combinedTableColumnValue(cls, 5),
- _combinedTableColumnValue(cls, 6),
- _combinedTableColumnValue(cls, 7),
- _combinedTableColumnValue(cls, 8)];
+ var row = [cls,
+ '', // Spacer column.
+ cls.newSpace.accumulated.bytes,
+ cls.newSpace.accumulated.instances,
+ cls.newSpace.current.bytes,
+ cls.newSpace.current.instances,
+ '', // Spacer column.
+ cls.oldSpace.accumulated.bytes,
+ cls.oldSpace.accumulated.instances,
+ cls.oldSpace.current.bytes,
+ cls.oldSpace.current.instances];
classTable.addRow(new SortedTableRow(row));
}
classTable.sort();
- _newPieDataTable.clearRows();
- var heap = profile['heaps']['new'];
- _newPieDataTable.addRow(['Used', heap['used']]);
- _newPieDataTable.addRow(['Free', heap['capacity'] - heap['used']]);
- _newPieDataTable.addRow(['External', heap['external']]);
- _oldPieDataTable.clearRows();
- heap = profile['heaps']['old'];
- _oldPieDataTable.addRow(['Used', heap['used']]);
- _oldPieDataTable.addRow(['Free', heap['capacity'] - heap['used']]);
- _oldPieDataTable.addRow(['External', heap['external']]);
- _draw();
}
- void _draw() {
- if (_newPieChart == null) {
- return;
+ void _addClassTableDomRow() {
+ assert(_classTableBody != null);
+ var tr = new TableRowElement();
+
+ // Add class ref.
+ var cell = tr.insertCell(-1);
+ ClassRefElement classRef = new Element.tag('class-ref');
+ cell.children.add(classRef);
+
+ // Add spacer.
+ cell = tr.insertCell(-1);
+ cell.classes.add('left-border-spacer');
+
+ // Add new space.
+ cell = tr.insertCell(-1);
+ cell = tr.insertCell(-1);
+ cell = tr.insertCell(-1);
+ cell = tr.insertCell(-1);
+
+ // Add spacer.
+ cell = tr.insertCell(-1);
+ cell.classes.add('left-border-spacer');
+
+ // Add old space.
+ cell = tr.insertCell(-1);
+ cell = tr.insertCell(-1);
+ cell = tr.insertCell(-1);
+ cell = tr.insertCell(-1);
+
+ // Add row to table.
+ _classTableBody.children.add(tr);
+ }
+
+ void _fillClassTableDomRow(TableRowElement tr, int rowIndex) {
+ var row = classTable.rows[rowIndex];
+ // Add class ref.
+ ClassRefElement classRef = tr.children[0].children[0];
+ classRef.ref = row.values[0];
+
+ var cell = tr.children[2];
+ cell.title = row.values[2].toString();
+ cell.text = classTable.getFormattedValue(rowIndex, 2);
+ cell = tr.children[3];
+ cell.title = row.values[3].toString();
+ cell.text = classTable.getFormattedValue(rowIndex, 3);
+ cell = tr.children[4];
+ cell.title = row.values[4].toString();
+ cell.text = classTable.getFormattedValue(rowIndex, 4);
+ cell = tr.children[5];
+ cell.title = row.values[5].toString();
+ cell.text = classTable.getFormattedValue(rowIndex, 5);
+
+ cell = tr.children[7];
+ cell.title = row.values[7].toString();
+ cell.text = classTable.getFormattedValue(rowIndex, 7);
+ cell = tr.children[8];
+ cell.title = row.values[8].toString();
+ cell.text = classTable.getFormattedValue(rowIndex, 8);
+
+ cell = tr.children[9];
+ cell.title = row.values[9].toString();
+ cell.text = classTable.getFormattedValue(rowIndex, 9);
+
+ cell = tr.children[10];
+ cell.title = row.values[10].toString();
+ cell.text = classTable.getFormattedValue(rowIndex, 10);
koda 2014/06/18 17:04:02 Consider having a constant SPACER_COLUMNS = [1, 6]
Cutch 2014/06/18 17:13:03 Done.
+ }
+
+ void _updateClassTableInDom() {
+ assert(_classTableBody != null);
+ // Resize DOM table.
+ if (_classTableBody.children.length > classTable.sortedRows.length) {
+ // Shrink the table.
+ var deadRows =
+ _classTableBody.children.length - classTable.sortedRows.length;
+ for (var i = 0; i < deadRows; i++) {
+ _classTableBody.children.removeLast();
+ }
+ } else if (_classTableBody.children.length < classTable.sortedRows.length) {
+ // Grow table.
+ var newRows =
+ classTable.sortedRows.length - _classTableBody.children.length;
+ for (var i = 0; i < newRows; i++) {
+ _addClassTableDomRow();
+ }
+ }
+ assert(_classTableBody.children.length == classTable.sortedRows.length);
+ // Fill table.
+ for (var i = 0; i < classTable.sortedRows.length; i++) {
+ var rowIndex = classTable.sortedRows[i];
+ var tr = _classTableBody.children[i];
+ _fillClassTableDomRow(tr, rowIndex);
}
+ }
+
+ void _drawCharts() {
_newPieChart.draw(_newPieDataTable);
_oldPieChart.draw(_oldPieDataTable);
}
@@ -126,133 +260,101 @@ class HeapProfileElement extends ObservatoryElement {
if (target is TableCellElement) {
if (classTable.sortColumnIndex != target.cellIndex) {
classTable.sortColumnIndex = target.cellIndex;
- classTable.sort();
+ classTable.sortDescending = true;
+ } else {
+ classTable.sortDescending = !classTable.sortDescending;
}
+ classTable.sort();
+ Stopwatch sw = new Stopwatch()..start();
+ _updateClassTableInDom();
+ _traceMillis(sw, 'Inserting class table into DOM');
}
}
- bool _classHasNoAllocations(Map v) {
- var newSpace = v['new'];
- var oldSpace = v['old'];
- for (var allocation in newSpace) {
- if (allocation != 0) {
- return false;
- }
- }
- for (var allocation in oldSpace) {
- if (allocation != 0) {
- return false;
- }
- }
- return true;
- }
-
- dynamic _combinedTableColumnValue(Map v, int index) {
- assert(index >= 0);
- assert(index < 9);
- switch (index) {
- case 0:
- return v['class']['user_name'];
- case 1:
- return v['new'][ACCUMULATED_SIZE];
- case 2:
- return v['new'][ACCUMULATED];
- case 3:
- return v['new'][LIVE_AFTER_GC_SIZE] +
- v['new'][ALLOCATED_SINCE_GC_SIZE];
- case 4:
- return v['new'][LIVE_AFTER_GC] +
- v['new'][ALLOCATED_SINCE_GC];
- case 5:
- return v['old'][ACCUMULATED_SIZE];
- case 6:
- return v['old'][ACCUMULATED];
- case 7:
- return v['old'][LIVE_AFTER_GC_SIZE] +
- v['old'][ALLOCATED_SINCE_GC_SIZE];
- case 8:
- return v['old'][LIVE_AFTER_GC] +
- v['old'][ALLOCATED_SINCE_GC];
- }
- throw new FallThroughError();
- }
-
void refresh(var done) {
if (profile == null) {
return;
}
var isolate = profile.isolate;
- isolate.get('/allocationprofile').then((ServiceMap response) {
- assert(response['type'] == 'AllocationProfile');
- profile = response;
- }).catchError((e, st) {
- Logger.root.info('$e $st');
- }).whenComplete(done);
+ isolate.get('/allocationprofile').then(_update).whenComplete(done);
}
void refreshGC(var done) {
- if (profile == null) {
- return;
- }
- var isolate = profile.isolate;
- isolate.get('/allocationprofile?gc=full').then((ServiceMap response) {
- assert(response['type'] == 'AllocationProfile');
- profile = response;
- }).catchError((e, st) {
- Logger.root.info('$e $st');
- }).whenComplete(done);
+ if (profile == null) {
+ return;
}
+ var isolate = profile.isolate;
+ isolate.get('/allocationprofile?gc=full').then(_update).whenComplete(done);
+ }
void resetAccumulator(var done) {
if (profile == null) {
return;
}
var isolate = profile.isolate;
- isolate.get('/allocationprofile?reset=true').then((ServiceMap response) {
- assert(response['type'] == 'AllocationProfile');
- profile = response;
- }).catchError((e, st) {
- Logger.root.info('$e $st');
- }).whenComplete(done);
+ isolate.get('/allocationprofile?reset=true').then(_update).
+ whenComplete(done);
+ }
+
+ void _update(ServiceMap newProfile) {
+ profile = newProfile;
}
void profileChanged(oldValue) {
- try {
- _updateChartData();
- } catch (e, st) {
- Logger.root.info('$e $st');
+ if (profile == null) {
+ return;
+ }
+ Stopwatch sw = new Stopwatch()..start();
+ isolate = profile.isolate;
+ isolate.updateHeapsFromMap(profile['heaps']);
+ var millis = int.parse(profile['dateLastAccumulatorReset']);
+ if (millis != 0) {
+ lastAccumulatorReset =
+ new DateTime.fromMillisecondsSinceEpoch(millis).toString();
+ }
+ millis = int.parse(profile['dateLastServiceGC']);
+ if (millis != 0) {
+ lastServiceGC =
+ new DateTime.fromMillisecondsSinceEpoch(millis).toString();
}
- notifyPropertyChange(#formattedAverage, [], formattedAverage);
- notifyPropertyChange(#formattedTotalCollectionTime, [],
- formattedTotalCollectionTime);
- notifyPropertyChange(#formattedCollections, [], formattedCollections);
+ _trace(sw, 'Updating heaps');
+ _updatePieCharts();
+ _trace(sw, 'Updating pie charts');
+ _updateClasses();
+ _trace(sw, 'Updating classes');
+ _updateClassTable();
+ _trace(sw, 'Updating class table');
+ _updateClassTableInDom();
+ _traceMillis(sw, 'Inserting class table into DOM');
+ _drawCharts();
+ _traceMillis(sw, 'Drawing pie charts');
+ notifyPropertyChange(#formattedAverage, 0, 1);
+ notifyPropertyChange(#formattedTotalCollectionTime, 0, 1);
+ notifyPropertyChange(#formattedCollections, 0, 1);
}
@observable String formattedAverage(bool newSpace) {
if (profile == null) {
return '';
}
- String space = newSpace ? 'new' : 'old';
- Map heap = profile['heaps'][space];
- var r = ((heap['time'] * 1000.0) / heap['collections']).toStringAsFixed(2);
- return '$r ms';
+ var heap = newSpace ? profile.isolate.newSpace : profile.isolate.oldSpace;
+ var avg = ((heap.totalCollectionTimeInSeconds * 1000.0) / heap.collections);
+ return '${avg.toStringAsFixed(2)} ms';
}
@observable String formattedCollections(bool newSpace) {
if (profile == null) {
return '';
}
- String space = newSpace ? 'new' : 'old';
- Map heap = profile['heaps'][space];
- return '${heap['collections']}';
+ var heap = newSpace ? profile.isolate.newSpace : profile.isolate.oldSpace;
+ return heap.collections.toString();
}
@observable String formattedTotalCollectionTime(bool newSpace) {
if (profile == null) {
return '';
}
- String space = newSpace ? 'new' : 'old';
- Map heap = profile['heaps'][space];
- return '${Utils.formatSeconds(heap['time'])} secs';
+ var heap = newSpace ? profile.isolate.newSpace : profile.isolate.oldSpace;
+ return '${Utils.formatSeconds(heap.totalCollectionTimeInSeconds)} secs';
}
}

Powered by Google App Engine
This is Rietveld 408576698