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..972b4dd72b43a8ec9b40b82e00fb1633352f6f01 100644
--- a/runtime/bin/vmservice/client/lib/src/elements/heap_profile.dart
+++ b/runtime/bin/vmservice/client/lib/src/elements/heap_profile.dart
@@ -8,9 +8,23 @@ 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 {
@@ -24,6 +38,9 @@ class HeapProfileElement extends ObservatoryElement {
static const ACCUMULATED = 6;
static const ACCUMULATED_SIZE = 7;
+ @observable String lastForcedGC = '---';
+ @observable String lastAccumulatorReset = '---';
+
// Pie chart of new space usage.
var _newPieDataTable;
var _newPieChart;
@@ -32,75 +49,71 @@ 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(''),
koda 2014/06/17 21:04:06 Why this empty column?
Cutch 2014/06/18 14:35:03 Spacer column, added comment.
+ 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(''),
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);
+ classTable.sortColumnIndex = 2;
koda 2014/06/17 21:04:06 Remind the reader which column this is.
Cutch 2014/06/18 14:35:03 Done.
}
- 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;
- }
- assert(classTable != null);
- classTable.clearRows();
- for (ServiceMap cls in profile['members']) {
- if (_classHasNoAllocations(cls)) {
- // 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)];
- classTable.addRow(new SortedTableRow(row));
- }
- classTable.sort();
+ void _updatePieCharts() {
+ assert(profile != null);
_newPieDataTable.clearRows();
var heap = profile['heaps']['new'];
_newPieDataTable.addRow(['Used', heap['used']]);
@@ -111,70 +124,133 @@ class HeapProfileElement extends ObservatoryElement {
_oldPieDataTable.addRow(['Used', heap['used']]);
_oldPieDataTable.addRow(['Free', heap['capacity'] - heap['used']]);
_oldPieDataTable.addRow(['External', heap['external']]);
- _draw();
}
- void _draw() {
- if (_newPieChart == null) {
- return;
- }
- _newPieChart.draw(_newPieDataTable);
- _oldPieChart.draw(_oldPieDataTable);
+ void _updateClassStats(Class cls, List newSpace, List oldSpace) {
koda 2014/06/17 21:04:06 Any way to share more code between new and old? It
Cutch 2014/06/18 14:35:03 Done.
+ cls.accumulatedNewSpace.instances = newSpace[ACCUMULATED];
+ cls.accumulatedNewSpace.bytes = newSpace[ACCUMULATED_SIZE];
+ cls.currentNewSpace.instances =
+ newSpace[LIVE_AFTER_GC] + newSpace[ALLOCATED_SINCE_GC];
+ cls.currentNewSpace.bytes =
+ newSpace[LIVE_AFTER_GC_SIZE] + newSpace[ALLOCATED_SINCE_GC_SIZE];
+
+ cls.accumulatedOldSpace.instances = oldSpace[ACCUMULATED];
+ cls.accumulatedOldSpace.bytes = oldSpace[ACCUMULATED_SIZE];
+ cls.currentOldSpace.instances =
+ oldSpace[LIVE_AFTER_GC] + oldSpace[ALLOCATED_SINCE_GC];
+ cls.currentOldSpace.bytes =
+ oldSpace[LIVE_AFTER_GC_SIZE] + oldSpace[ALLOCATED_SINCE_GC_SIZE];
}
- @observable void changeSort(Event e, var detail, Element target) {
- if (target is TableCellElement) {
- if (classTable.sortColumnIndex != target.cellIndex) {
- classTable.sortColumnIndex = target.cellIndex;
- classTable.sort();
+ void _updateClasses() {
+ for (ServiceMap clsAllocations in profile['members']) {
+ Class cls = clsAllocations['class'];
+ if (cls == null) {
+ continue;
}
+ _updateClassStats(cls, clsAllocations['new'], clsAllocations['old']);
}
}
- bool _classHasNoAllocations(Map v) {
- var newSpace = v['new'];
- var oldSpace = v['old'];
- for (var allocation in newSpace) {
- if (allocation != 0) {
- return false;
+ void _updateClassTable() {
+ classTable.clearRows();
+ for (ServiceMap clsAllocations in profile['members']) {
+ Class cls = clsAllocations['class'];
+ if (cls == null) {
+ continue;
}
- }
- for (var allocation in oldSpace) {
- if (allocation != 0) {
- return false;
+ if (cls.hasNoAllocations) {
+ // If a class has no allocations, don't display it.
+ continue;
}
+ var row = [cls,
+ '',
+ cls.accumulatedNewSpace.bytes,
+ cls.accumulatedNewSpace.instances,
+ cls.currentNewSpace.bytes,
+ cls.currentNewSpace.instances,
+ '',
+ cls.accumulatedOldSpace.bytes,
+ cls.accumulatedOldSpace.instances,
+ cls.currentOldSpace.bytes,
+ cls.currentOldSpace.instances];
+ classTable.addRow(new SortedTableRow(row));
}
- return true;
+ classTable.sort();
+ }
+
+ void _updateClassTableInDom() {
+ assert(_classTableBody != null);
+ _classTableBody.children.clear();
+ for (var rowIndex in classTable.sortedRows) {
+ var row = classTable.rows[rowIndex];
+ var tr = new TableRowElement();
+
+ // Add class ref.
+ var cell = tr.insertCell(-1);
+ ClassRefElement classRef = new Element.tag('class-ref');
+ classRef.ref = row.values[0];
+ cell.children.add(classRef);
+
+ // Add spacer.
+ cell = tr.insertCell(-1);
+ cell.classes.add('left-border-spacer');
+
+ // Add new space.
+ cell = tr.insertCell(-1);
+ cell.title = row.values[2].toString();
+ cell.innerHtml = classTable.getFormattedValue(rowIndex, 2);
+ cell = tr.insertCell(-1);
+ cell.title = row.values[3].toString();
+ cell.innerHtml = classTable.getFormattedValue(rowIndex, 3);
+ cell = tr.insertCell(-1);
+ cell.title = row.values[4].toString();
+ cell.innerHtml = classTable.getFormattedValue(rowIndex, 4);
+ cell = tr.insertCell(-1);
+ cell.title = row.values[5].toString();
+ cell.innerHtml = classTable.getFormattedValue(rowIndex, 5);
+
+ // Add spacer.
+ cell = tr.insertCell(-1);
+ cell.classes.add('left-border-spacer');
+
+ // Add old space.
+ cell = tr.insertCell(-1);
+ cell.title = row.values[7].toString();
+ cell.innerHtml = classTable.getFormattedValue(rowIndex, 7);
+ cell = tr.insertCell(-1);
+ cell.title = row.values[8].toString();
+ cell.innerHtml = classTable.getFormattedValue(rowIndex, 8);
+ cell = tr.insertCell(-1);
+ cell.title = row.values[9].toString();
+ cell.innerHtml = classTable.getFormattedValue(rowIndex, 9);
+ cell = tr.insertCell(-1);
+ cell.title = row.values[10].toString();
+ cell.innerHtml = classTable.getFormattedValue(rowIndex, 10);
+
+ // Add row to table.
+ _classTableBody.children.add(tr);
+ }
+ }
+
+ void _drawCharts() {
+ _newPieChart.draw(_newPieDataTable);
+ _oldPieChart.draw(_oldPieDataTable);
}
- 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];
+ @observable void changeSort(Event e, var detail, Element target) {
+ if (target is TableCellElement) {
+ if (classTable.sortColumnIndex != target.cellIndex) {
+ classTable.sortColumnIndex = target.cellIndex;
+ classTable.sortDescending = true;
+ } else {
+ classTable.sortDescending = !classTable.sortDescending;
+ }
+ classTable.sort();
+ Stopwatch sw = new Stopwatch()..start();
+ _updateClassTableInDom();
+ _traceMillis(sw, 'Inserting class table into DOM');
}
- throw new FallThroughError();
}
void refresh(var done) {
@@ -182,50 +258,61 @@ class HeapProfileElement extends ObservatoryElement {
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['dateLastGC']);
+ if (millis != 0) {
+ lastForcedGC =
+ 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) {

Powered by Google App Engine
This is Rietveld 408576698