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

Side by Side Diff: runtime/bin/vmservice/client/lib/src/observatory_elements/heap_profile.dart

Issue 148153007: Add Google Charts to Observatory and use it in allocation profiler (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 10 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library heap_profile_element; 5 library heap_profile_element;
6 6
7 import 'dart:async';
7 import 'dart:html'; 8 import 'dart:html';
9 import 'dart:js';
8 import 'package:logging/logging.dart'; 10 import 'package:logging/logging.dart';
9 import 'package:polymer/polymer.dart'; 11 import 'package:polymer/polymer.dart';
10 import 'observatory_element.dart'; 12 import 'observatory_element.dart';
11 13
12 /// Displays an Error response. 14 /// Displays an Error response.
13 @CustomTag('heap-profile') 15 @CustomTag('heap-profile')
14 class HeapProfileElement extends ObservatoryElement { 16 class HeapProfileElement extends ObservatoryElement {
15 // Indexes into VM provided map. 17 // Indexes into VM provided map.
16 static const ALLOCATED_BEFORE_GC = 0; 18 static const ALLOCATED_BEFORE_GC = 0;
17 static const ALLOCATED_BEFORE_GC_SIZE = 1; 19 static const ALLOCATED_BEFORE_GC_SIZE = 1;
18 static const LIVE_AFTER_GC = 2; 20 static const LIVE_AFTER_GC = 2;
19 static const LIVE_AFTER_GC_SIZE = 3; 21 static const LIVE_AFTER_GC_SIZE = 3;
20 static const ALLOCATED_SINCE_GC = 4; 22 static const ALLOCATED_SINCE_GC = 4;
21 static const ALLOCATED_SINCE_GC_SIZE = 5; 23 static const ALLOCATED_SINCE_GC_SIZE = 5;
22 24
25 var _newPieOptions;
26 var _newPieDataTable;
27 var _newPieChart;
turnidge 2014/01/28 18:28:08 Instead of a making Chart all static, why not make
Cutch 2014/01/28 23:23:19 Refactored Chart wrapper.
28
29 var _oldPieOptions;
30 var _oldPieDataTable;
31 var _oldPieChart;
32
33 var _tableOptions;
34 var _tableDataTable;
35 var _tableChart;
36
23 @published Map profile; 37 @published Map profile;
24 @published List sortedProfile; 38 @published List sortedProfile;
25 int _sortColumnIndex = 1; 39 int _sortColumnIndex = 1;
26 HeapProfileElement.created() : super.created(); 40 HeapProfileElement.created() : super.created() {
41 _tableOptions = new JsObject.jsify({
42 'allowHtml': 'true',
43 'sortColumn': 1,
44 'sortAscending': false,
45 });
46 _newPieOptions = new JsObject.jsify({
47 'title': 'New Space'
48 });
49 _oldPieOptions = new JsObject.jsify({
50 'title': 'Old Space'
51 });
52 _tableDataTable = Chart.createDataTable();
53 Chart.addColumn(_tableDataTable, 'string', 'Class');
54 Chart.addColumn(_tableDataTable, 'number', 'Current (new)');
55 Chart.addColumn(_tableDataTable, 'number', 'Allocated Since GC (new)');
56 Chart.addColumn(_tableDataTable, 'number', 'Total before GC (new)');
57 Chart.addColumn(_tableDataTable, 'number', 'Survivors (new)');
58 Chart.addColumn(_tableDataTable, 'number', 'Current (old)');
59 Chart.addColumn(_tableDataTable, 'number', 'Allocated Since GC (old)');
60 Chart.addColumn(_tableDataTable, 'number', 'Total before GC (old)');
61 Chart.addColumn(_tableDataTable, 'number', 'Survivors (old)');
62 _newPieDataTable = Chart.createDataTable();
63 Chart.addColumn(_newPieDataTable, 'string', 'Type');
64 Chart.addColumn(_newPieDataTable, 'number', 'Size');
65 _oldPieDataTable = Chart.createDataTable();
66 Chart.addColumn(_oldPieDataTable, 'string', 'Type');
67 Chart.addColumn(_oldPieDataTable, 'number', 'Size');
68 }
27 69
28 // Display columns. 70 void enteredView() {
29 @observable final List<String> columns = [ 71 super.enteredView();
30 'Class', 72 _tableChart = Chart.createChart('Table',
31 'Current (new)', 73 shadowRoot.querySelector('#table'));
32 'Allocated Since GC (new)', 74 _newPieChart = Chart.createChart('PieChart',
33 'Total before GC (new)', 75 shadowRoot.querySelector('#newPieChart'));
34 'Survivors (new)', 76 _oldPieChart = Chart.createChart('PieChart',
35 'Current (old)', 77 shadowRoot.querySelector('#oldPieChart'));
36 'Allocated Since GC (old)', 78 _draw();
37 'Total before GC (old)', 79 }
38 'Survivors (old)', 80
39 ]; 81 void _updateChartData() {
82 if ((profile == null) || (profile['members'] is! List) ||
83 (profile['members'].length == 0)) {
84 sortedProfile = toObservable([]);
85 return;
86 }
87 assert(_tableDataTable != null);
88 Chart.clearRows(_tableDataTable);
89 for (Map cls in profile['members']) {
90 var url =
91 app.locationManager.currentIsolateRelativeLink(cls['class']['id']);
92 Chart.addRow(_tableDataTable,
93 ['<a href="$url">${_columnValue(cls, 0)}</a>',
94 _columnValue(cls, 1),
95 _columnValue(cls, 2),
96 _columnValue(cls, 3),
97 _columnValue(cls, 4),
98 _columnValue(cls, 5),
99 _columnValue(cls, 6),
100 _columnValue(cls, 7),
101 _columnValue(cls, 8)]);
102 }
103 Chart.clearRows(_newPieDataTable);
104 var heap = profile['heaps']['new'];
105 Chart.addRow(_newPieDataTable, ['Used', heap['used']]);
106 Chart.addRow(_newPieDataTable, ['Free', heap['capacity'] - heap['used']]);
107 Chart.clearRows(_oldPieDataTable);
108 heap = profile['heaps']['old'];
109 Chart.addRow(_oldPieDataTable, ['Used', heap['used']]);
110 Chart.addRow(_oldPieDataTable, ['Free', heap['capacity'] - heap['used']]);
111 _draw();
112 }
113
114 void _draw() {
115 if (_tableChart == null) {
116 return;
117 }
118 Chart.refreshTableChartDrawOptions(_tableChart, _tableOptions);
119 Chart.draw(_tableChart, _tableDataTable, _tableOptions);
120 Chart.draw(_newPieChart, _newPieDataTable, _newPieOptions);
121 Chart.draw(_oldPieChart, _oldPieDataTable, _oldPieOptions);
122 }
40 123
41 dynamic _columnValue(Map v, int index) { 124 dynamic _columnValue(Map v, int index) {
42 assert(columns.length == 9); 125 assert(index >= 0);
126 assert(index < 9);
43 switch (index) { 127 switch (index) {
44 case 0: 128 case 0:
45 return v['class']['user_name']; 129 return v['class']['user_name'];
46 case 1: 130 case 1:
47 return v['new'][LIVE_AFTER_GC_SIZE] + v['new'][ALLOCATED_SINCE_GC_SIZE]; 131 return v['new'][LIVE_AFTER_GC_SIZE] + v['new'][ALLOCATED_SINCE_GC_SIZE];
48 case 2: 132 case 2:
49 return v['new'][ALLOCATED_SINCE_GC_SIZE]; 133 return v['new'][ALLOCATED_SINCE_GC_SIZE];
50 case 3: 134 case 3:
51 return v['new'][ALLOCATED_BEFORE_GC_SIZE]; 135 return v['new'][ALLOCATED_BEFORE_GC_SIZE];
52 case 4: 136 case 4:
53 return v['new'][LIVE_AFTER_GC_SIZE]; 137 return v['new'][LIVE_AFTER_GC_SIZE];
54 case 5: 138 case 5:
55 return v['old'][LIVE_AFTER_GC_SIZE] + v['old'][ALLOCATED_SINCE_GC_SIZE]; 139 return v['old'][LIVE_AFTER_GC_SIZE] + v['old'][ALLOCATED_SINCE_GC_SIZE];
56 case 6: 140 case 6:
57 return v['old'][ALLOCATED_SINCE_GC_SIZE]; 141 return v['old'][ALLOCATED_SINCE_GC_SIZE];
58 case 7: 142 case 7:
59 return v['old'][ALLOCATED_BEFORE_GC_SIZE]; 143 return v['old'][ALLOCATED_BEFORE_GC_SIZE];
60 case 8: 144 case 8:
61 return v['old'][LIVE_AFTER_GC_SIZE]; 145 return v['old'][LIVE_AFTER_GC_SIZE];
62 } 146 }
63 } 147 }
64 148
65 int _sortColumn(Map a, Map b, int index) {
66 var aValue = _columnValue(a, index);
67 var bValue = _columnValue(b, index);
68 return Comparable.compare(bValue, aValue);
69 }
70
71 _sort() {
72 if ((profile == null) || (profile['members'] is! List) ||
73 (profile['members'].length == 0)) {
74 sortedProfile = toObservable([]);
75 return;
76 }
77 sortedProfile = profile['members'].toList();
78 sortedProfile.sort((a, b) => _sortColumn(a, b, _sortColumnIndex));
79 sortedProfile = toObservable(sortedProfile);
80 notifyPropertyChange(#sortedProfile, [], sortedProfile);
81 notifyPropertyChange(#current, 0, 1);
82 notifyPropertyChange(#allocated, 0, 1);
83 notifyPropertyChange(#beforeGC, 0, 1);
84 notifyPropertyChange(#afterGC, 0, 1);
85 }
86
87 void changeSortColumn(Event e, var detail, Element target) {
88 var message = target.attributes['data-msg'];
89 var index;
90 try {
91 index = int.parse(message);
92 } catch (e) {
93 return;
94 }
95 assert(index is int);
96 assert(index > 0);
97 assert(index < columns.length);
98 _sortColumnIndex = index;
99 _sort();
100 }
101
102 void refreshData(Event e, var detail, Node target) { 149 void refreshData(Event e, var detail, Node target) {
103 var isolateId = app.locationManager.currentIsolateId(); 150 var isolateId = app.locationManager.currentIsolateId();
104 var isolate = app.isolateManager.getIsolate(isolateId); 151 var isolate = app.isolateManager.getIsolate(isolateId);
105 if (isolate == null) { 152 if (isolate == null) {
106 Logger.root.info('No isolate found.'); 153 Logger.root.info('No isolate found.');
107 return; 154 return;
108 } 155 }
109 var request = '/$isolateId/allocationprofile'; 156 var request = '/$isolateId/allocationprofile';
110 app.requestManager.requestMap(request).then((Map response) { 157 app.requestManager.requestMap(request).then((Map response) {
111 assert(response['type'] == 'AllocationProfile'); 158 assert(response['type'] == 'AllocationProfile');
112 profile = response; 159 profile = response;
113 }).catchError((e, st) { 160 }).catchError((e, st) {
114 Logger.root.info('$e $st'); 161 Logger.root.info('$e $st');
115 }); 162 });
116 } 163 }
117 164
118 void profileChanged(oldValue) { 165 void profileChanged(oldValue) {
119 _sort(); 166 _updateChartData();
120 notifyPropertyChange(#status, [], status); 167 notifyPropertyChange(#formattedAverage, [], formattedAverage);
168 notifyPropertyChange(#formattedTotalCollectionTime, [],
169 formattedTotalCollectionTime);
170 notifyPropertyChange(#formattedCollections, [], formattedCollections);
121 } 171 }
122 172
123 String status(bool new_space) { 173 @observable String formattedAverage(bool newSpace) {
124 if (profile == null) { 174 if (profile == null) {
125 return ''; 175 return '';
126 } 176 }
127 String space = new_space ? 'new' : 'old'; 177 String space = newSpace ? 'new' : 'old';
128 Map heap = profile['heaps'][space]; 178 Map heap = profile['heaps'][space];
129 var usage = '${ObservatoryApplication.scaledSizeUnits(heap['used'])} / ' 179 var r = ((heap['time'] * 1000.0) / heap['collections']).toStringAsFixed(2);
130 '${ObservatoryApplication.scaledSizeUnits(heap['capacity'])}'; 180 return '$r ms';
131 var timings = '${ObservatoryApplication.timeUnits(heap['time'])} secs';
132 var collections = '${heap['collections']} collections';
133 var avgTime = '${(heap['time'] * 1000.0) / heap['collections']} ms';
134 return '$usage ($timings) [$collections] $avgTime';
135 } 181 }
136 182
137 String current(Map cls, bool new_space, [bool instances = false]) { 183 @observable String formattedCollections(bool newSpace) {
138 if (cls is !Map) { 184 if (profile == null) {
139 return ''; 185 return '';
140 } 186 }
141 List data = cls[new_space ? 'new' : 'old']; 187 String space = newSpace ? 'new' : 'old';
142 if (data == null) { 188 Map heap = profile['heaps'][space];
189 return '${heap['collections']}';
190 }
191
192 @observable String formattedTotalCollectionTime(bool newSpace) {
193 if (profile == null) {
143 return ''; 194 return '';
144 } 195 }
145 int current = data[instances ? LIVE_AFTER_GC : LIVE_AFTER_GC_SIZE] + 196 String space = newSpace ? 'new' : 'old';
146 data[instances ? ALLOCATED_SINCE_GC : ALLOCATED_SINCE_GC_SIZE]; 197 Map heap = profile['heaps'][space];
147 if (instances) { 198 return '${ObservatoryApplication.timeUnits(heap['time'])} secs';
148 return '$current';
149 }
150 return ObservatoryApplication.scaledSizeUnits(current);
151 }
152
153 String allocated(Map cls, bool new_space, [bool instances = false]) {
154 if (cls is !Map) {
155 return '';
156 }
157 List data = cls[new_space ? 'new' : 'old'];
158 if (data == null) {
159 return '';
160 }
161 int current =
162 data[instances ? ALLOCATED_SINCE_GC : ALLOCATED_SINCE_GC_SIZE];
163 if (instances) {
164 return '$current';
165 }
166 return ObservatoryApplication.scaledSizeUnits(current);
167 }
168
169 String beforeGC(Map cls, bool new_space, [bool instances = false]) {
170 if (cls is! Map) {
171 return '';
172 }
173 List data = cls[new_space ? 'new' : 'old'];
174 if (data == null) {
175 return '';
176 }
177 int current =
178 data[instances ? ALLOCATED_BEFORE_GC : ALLOCATED_BEFORE_GC_SIZE];
179 if (instances) {
180 return '$current';
181 }
182 return ObservatoryApplication.scaledSizeUnits(current);
183 }
184
185 String afterGC(Map cls, bool new_space, [bool instances = false]) {
186 if (cls is! Map) {
187 return '';
188 }
189 List data = cls[new_space ? 'new' : 'old'];
190 if (data == null) {
191 return '';
192 }
193 int current = data[instances ? LIVE_AFTER_GC : LIVE_AFTER_GC_SIZE];
194 if (instances) {
195 return '$current';
196 }
197 return ObservatoryApplication.scaledSizeUnits(current);
198 } 199 }
199 } 200 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698