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

Side by Side Diff: runtime/observatory/lib/src/elements/heap_snapshot.dart

Issue 2266343002: Converted Observatory heap-snapshot element (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Added missing explanation text Created 4 years, 4 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
OLDNEW
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2015, 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_snapshot_element; 5 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
6 // for details. All rights reserved. Use of this source code is governed by a
7 // BSD-style license that can be found in the LICENSE file.
6 8
7 import 'dart:async'; 9 import 'dart:async';
8 import 'dart:html'; 10 import 'dart:html';
9 import 'class_ref_wrapper.dart'; 11 import 'dart:math' as Math;
10 import 'observatory_element.dart'; 12 import 'package:observatory/models.dart' as M;
11 import 'package:observatory/app.dart'; 13 import 'package:observatory/src/elements/class_ref.dart';
12 import 'package:observatory/service.dart'; 14 import 'package:observatory/src/elements/containers/virtual_tree.dart';
13 import 'package:observatory/elements.dart'; 15 import 'package:observatory/src/elements/helpers/any_ref.dart';
14 import 'package:observatory/object_graph.dart'; 16 import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
15 import 'package:polymer/polymer.dart'; 17 import 'package:observatory/src/elements/helpers/tag.dart';
16 import 'package:logging/logging.dart'; 18 import 'package:observatory/src/elements/helpers/uris.dart';
17 19 import 'package:observatory/src/elements/nav/bar.dart';
18 class DominatorTreeRow extends TableTreeRow { 20 import 'package:observatory/src/elements/nav/isolate_menu.dart';
19 final ObjectVertex vertex; 21 import 'package:observatory/src/elements/nav/menu.dart';
20 final HeapSnapshot snapshot; 22 import 'package:observatory/src/elements/nav/notify.dart';
21 23 import 'package:observatory/src/elements/nav/refresh.dart';
22 var _domTreeChildren; 24 import 'package:observatory/src/elements/nav/top_menu.dart';
23 get domTreeChildren { 25 import 'package:observatory/src/elements/nav/vm_menu.dart';
24 if (_domTreeChildren == null) { 26 import 'package:observatory/utils.dart';
25 _domTreeChildren = vertex.dominatorTreeChildren(); 27
26 } 28 enum HeapSnapshotTreeMode {
27 return _domTreeChildren; 29 dominatorTree,
28 } 30 groupByClass
29 31 }
30 DominatorTreeRow(TableTree tree, 32
31 TableTreeRow parent, 33 class HeapSnapshotElement extends HtmlElement implements Renderable {
32 this.vertex, 34 static const tag = const Tag<HeapSnapshotElement>('heap-snapshot',
33 this.snapshot) 35 dependencies: const [
34 : super(tree, parent) { 36 ClassRefElement.tag,
35 } 37 NavBarElement.tag,
36 38 NavTopMenuElement.tag,
37 bool hasChildren() { 39 NavVMMenuElement.tag,
38 return domTreeChildren.length > 0; 40 NavIsolateMenuElement.tag,
41 NavMenuElement.tag,
42 NavRefreshElement.tag,
43 NavNotifyElement.tag,
44 VirtualTreeElement.tag,
45 ]);
46
47 RenderingScheduler<HeapSnapshotElement> _r;
48
49 Stream<RenderedEvent<HeapSnapshotElement>> get onRendered => _r.onRendered;
50
51 M.VM _vm;
52 M.IsolateRef _isolate;
53 M.EventRepository _events;
54 M.NotificationRepository _notifications;
55 M.HeapSnapshotRepository _snapshots;
56 M.InstanceRepository _instances;
57 M.HeapSnapshot _snapshot;
58 Stream<M.HeapSnapshotLoadingProgressEvent> _progressStream;
59 M.HeapSnapshotLoadingProgress _progress;
60 HeapSnapshotTreeMode _mode = HeapSnapshotTreeMode.dominatorTree;
61
62
63 M.IsolateRef get isolate => _isolate;
64 M.NotificationRepository get notifications => _notifications;
65 M.HeapSnapshotRepository get profiles => _snapshots;
66 M.VMRef get vm => _vm;
67
68 factory HeapSnapshotElement(M.VM vm, M.IsolateRef isolate,
69 M.EventRepository events,
70 M.NotificationRepository notifications,
71 M.HeapSnapshotRepository snapshots,
72 M.InstanceRepository instances,
73 {RenderingQueue queue}) {
74 assert(vm != null);
75 assert(isolate != null);
76 assert(events != null);
77 assert(notifications != null);
78 assert(snapshots != null);
79 assert(instances != null);
80 HeapSnapshotElement e = document.createElement(tag.name);
81 e._r = new RenderingScheduler(e, queue: queue);
82 e._vm = vm;
83 e._isolate = isolate;
84 e._events = events;
85 e._notifications = notifications;
86 e._snapshots = snapshots;
87 e._instances = instances;
88 return e;
89 }
90
91 HeapSnapshotElement.created() : super.created();
92
93 @override
94 attached() {
95 super.attached();
96 _r.enable();
97 _refresh();
98 }
99
100 @override
101 detached() {
102 super.detached();
103 _r.disable(notify: true);
104 children = [];
105 }
106
107 void render() {
108 var content = [
109 new NavBarElement(queue: _r.queue)
110 ..children = [
111 new NavTopMenuElement(queue: _r.queue),
112 new NavVMMenuElement(_vm, _events, queue: _r.queue),
113 new NavIsolateMenuElement(_isolate, _events, queue: _r.queue),
114 new NavMenuElement('heap snapshot', link: Uris.profiler(_isolate),
115 last: true, queue: _r.queue),
116 new NavRefreshElement(queue: _r.queue)
117 ..disabled = M.isHeapSnapshotProgressRunning(_progress?.status)
118 ..onRefresh.listen((e) {
119 _refresh();
120 }),
121 new NavNotifyElement(_notifications, queue: _r.queue)
122 ],
123 ];
124 if (_progress == null) {
125 children = content;
126 return;
127 }
128 switch (_progress.status) {
rmacnak 2016/08/23 16:57:09 Because computing the dominator tree can take quit
cbernaschina 2016/08/23 17:28:28 Done.
129 case M.HeapSnapshotLoadingStatus.fetching :
130 content.addAll(_createStatusMessage('Fetching profile from VM...'));
rmacnak 2016/08/23 16:57:09 profile -> snapshot
cbernaschina 2016/08/23 17:28:28 Done.
131 break;
132 case M.HeapSnapshotLoadingStatus.loading :
133 content.addAll(_createStatusMessage('Loading profile...',
134 progress: _progress.progress));
135 break;
136 case M.HeapSnapshotLoadingStatus.disabled :
137 content.addAll(_createDisabledMessage());
138 break;
139 case M.HeapSnapshotLoadingStatus.loaded:
140 content.addAll(_createReport());
141 break;
142 }
143 children = content;
144 }
145
146 Future _refresh() async {
147 _progress = null;
148 _progressStream = _snapshots.get(isolate);
149 _r.dirty();
150 _progressStream.listen((_) => _r.dirty());
151 _progress = (await _progressStream.first).progress;
152 _r.dirty();
153 if (M.isHeapSnapshotProgressRunning(_progress.status)) {
154 _progress = (await _progressStream.last).progress;
155 _snapshot = _progress.snapshot;
156 _r.dirty();
157 }
158 }
159
160 static List<Element> _createStatusMessage(String message,
161 {double progress: 0.0}) {
162 return [
163 new DivElement()..classes = ['content-centered-big']
164 ..children = [
165 new DivElement()..classes = ['statusBox', 'shadow', 'center']
166 ..children = [
167 new DivElement()..classes = ['statusMessage']
168 ..text = message,
169 new DivElement()..style.background = '#0489c3'
170 ..style.width = '$progress%'
171 ..style.height = '15px'
172 ..style.borderRadius = '4px'
173 ]
174 ]
175 ];
176 }
177
178 static List<Element> _createDisabledMessage() {
rmacnak 2016/08/23 16:57:09 The profile flag has no effect on the heap snapsho
cbernaschina 2016/08/23 17:28:28 Done
179 return [
180 new DivElement()..classes = ['content-centered-big']
181 ..children = [
182 new DivElement()..classes = ['statusBox' 'shadow' 'center']
183 ..children = [
184 new DivElement()
185 ..children = [
186 new HeadingElement.h1()
187 ..text = 'Profiling is disabled',
188 new BRElement(),
189 new DivElement()
190 ..innerHtml = 'Perhaps the <b>profile</b> '
191 'flag has been disabled for this VM.',
192 new BRElement(),
193 new SpanElement()..text = 'See all',
194 new AnchorElement(href: Uris.flags())..text = 'vm flags'
195 ]
196 ]
197 ]
198 ];
199 }
200
201 VirtualTreeElement _tree;
202
203 List<Element> _createReport() {
204 var report = [
205 new DivElement()..classes = ['content-centered-big']
206 ..children = [
207 new DivElement()..classes = ['memberList']
208 ..children = [
209 new DivElement()..classes = ['memberItem']
210 ..children = [
211 new DivElement()..classes = ['memberName']
212 ..text = 'Refreshed ',
213 new DivElement()..classes = ['memberName']
214 ..text = Utils.formatDateTime(_snapshot.timestamp)
215 ],
216 new DivElement()..classes = ['memberItem']
217 ..children = [
218 new DivElement()..classes = ['memberName']
219 ..text = 'Objects ',
220 new DivElement()..classes = ['memberName']
221 ..text = '${_snapshot.objects}'
222 ],
223 new DivElement()..classes = ['memberItem']
224 ..children = [
225 new DivElement()..classes = ['memberName']
226 ..text = 'References ',
227 new DivElement()..classes = ['memberName']
228 ..text = '${_snapshot.references}'
229 ],
230 new DivElement()..classes = ['memberItem']
231 ..children = [
232 new DivElement()..classes = ['memberName']
233 ..text = 'Size ',
234 new DivElement()..classes = ['memberName']
235 ..text = Utils.formatSize(_snapshot.size)
236 ],
237 new DivElement()..classes = ['memberItem']
238 ..children = [
239 new DivElement()..classes = ['memberName']
240 ..text = 'Analysis ',
241 new DivElement()..classes = ['memberName']
242 ..children = _createModeSelect()
243 ]
244 ]
245 ],
246 ];
247 switch (_mode) {
248 case HeapSnapshotTreeMode.dominatorTree:
249 _tree = new VirtualTreeElement(_createDominator, _updateDominator,
250 _getChildrenDominator,
251 items: _getChildrenDominator(_snapshot.dominatorTree),
252 queue: _r.queue);
253 _tree.expand(_snapshot.dominatorTree);
254 final text = 'In a heap dominator tree, an object X is a parent of '
255 'object Y if every path from the root to Y goes through '
256 'X. This allows you to find "choke points" that are '
257 'holding onto a lot memory. If an object becomes garbage, '
rmacnak 2016/08/23 16:57:09 a lot of memory
cbernaschina 2016/08/23 17:28:28 Done.
258 'all its children in the dominator tree become garbage as '
259 'well. The retained size of an object is the sum of the '
260 'retained sizes of its children in the dominator tree '
261 'plus its own shallow size, and is the amount of memory '
262 'that would be freed if the object became garbage.';
263 report.addAll([
264 new DivElement()..classes = ['content-centered-big', 'explanation']
265 ..text = text
266 ..title = text,
267 _tree
268 ]);
269 break;
270 case HeapSnapshotTreeMode.groupByClass:
271 final items = _snapshot.classReferences.toList();
272 items.sort((a, b) => b.shallowSize - a.shallowSize);
273 _tree = new VirtualTreeElement(_createGroup, _updateGroup,
274 _getChildrenGroup, items: items, queue: _r.queue);
275 _tree.expand(_snapshot.dominatorTree);
276 report.add(_tree);
277 break;
278 default:
279 break;
280 }
281 return report;
282 }
283
284 static Element _createDominator(toggle) {
285 return new DivElement()
286 ..classes = const ['tree-item']
287 ..children = [
288 new SpanElement()..classes = const ['size']
289 ..title = 'retained size',
290 new SpanElement()..classes = const ['lines'],
291 new ButtonElement()..classes = const ['expander']
292 ..onClick.listen((_) => toggle(autoToggleSingleChildNodes: true)),
293 new SpanElement()..classes = const ['percentage']
294 ..title = 'percentage of heap being retained',
295 new SpanElement()..classes = const ['name']
296 ];
297 }
298
299 static Element _createGroup(toggle) {
300 return new DivElement()
301 ..classes = const ['tree-item']
302 ..children = [
303 new SpanElement()..classes = const ['size']
304 ..title = 'shallow size',
305 new SpanElement()..classes = const ['lines'],
306 new ButtonElement()..classes = const ['expander']
307 ..onClick.listen((_) => toggle(autoToggleSingleChildNodes: true)),
308 new SpanElement()..classes = const ['count']
309 ..title = 'shallow size',
310 new SpanElement()..classes = const ['name']
311 ];
39 } 312 }
40 313
41 static const int kMaxChildren = 100; 314 static const int kMaxChildren = 100;
42 static const int kMinRetainedSize = 4096; 315 static const int kMinRetainedSize = 4096;
43 316
44 void onShow() { 317 static _getChildrenDominator(M.HeapSnapshotDominatorNode node) {
45 super.onShow(); 318 final list = node.children.toList();
46 if (children.length == 0) { 319 list.sort((a, b) => b.retainedSize - a.retainedSize);
47 domTreeChildren.sort((a, b) => b.retainedSize - a.retainedSize); 320 return list.where((child) => child.retainedSize >= kMinRetainedSize)
48 int includedChildren = 0; 321 .take(kMaxChildren);
49 for (var childVertex in domTreeChildren) { 322 }
50 if (childVertex.retainedSize >= kMinRetainedSize) { 323
51 if (++includedChildren <= kMaxChildren) { 324 static _getChildrenGroup(item) {
52 var row = new DominatorTreeRow(tree, this, childVertex, snapshot); 325 if (item is M.HeapSnapshotClassReferences) {
53 children.add(row); 326 if (item.inbounds.isNotEmpty || item.outbounds.isNotEmpty) {
54 } 327 return [item.inbounds, item.outbounds];
55 }
56 } 328 }
57 } 329 } else if (item is Iterable) {
58 330 return item.toList()..sort((a, b) => b.shallowSize - a.shallowSize);
59 var firstColumn = flexColumns[0]; 331 }
60 firstColumn.style.justifyContent = 'flex-start'; 332 return const [];
61 firstColumn.style.position = 'relative'; 333 }
62 firstColumn.style.alignItems = 'center'; 334
63 firstColumn.style.setProperty('overflow-x', 'hidden'); 335 void _updateDominator(HtmlElement element, M.HeapSnapshotDominatorNode node,
64 336 int depth) {
65 var percentRetained = vertex.retainedSize / snapshot.graph.size; 337 element.children[0].text = Utils.formatSize(node.shallowSize);
rmacnak 2016/08/23 17:01:02 retainedSize
cbernaschina 2016/08/23 17:28:28 Done.
66 var percentNode = new SpanElement(); 338 _updateLines(element.children[1].children, depth);
67 percentNode.text = Utils.formatPercentNormalized(percentRetained); 339 if (_getChildrenDominator(node).isNotEmpty) {
68 percentNode.style.minWidth = '5em'; 340 element.children[2].text = _tree.isExpanded(node) ? 'â–¼' : 'â–º';
69 percentNode.style.textAlign = 'right'; 341 } else {
70 percentNode.title = "Percent of heap being retained"; 342 element.children[2].text = '';
71 percentNode.style.display = 'inline-block'; 343 }
72 firstColumn.children.add(percentNode); 344 element.children[3].text = Utils.formatPercentNormalized(
73 345 node.retainedSize * 1.0 / _snapshot.size);
74 var gap = new SpanElement(); 346 final wrapper = new SpanElement()..classes = const ['name']
75 gap.style.minWidth = '1em'; 347 ..text = 'Loading...';
76 gap.style.display = 'inline-block'; 348 element.children[4] = wrapper;
77 firstColumn.children.add(gap); 349 node.object.then((object) {
78 350 wrapper..text = ''
79 AnyServiceRefElement objectRef = new Element.tag("any-service-ref"); 351 ..children = [anyRef(_isolate, object, _instances, queue: _r.queue)];
80 snapshot.isolate.getObjectByAddress(vertex.address).then((obj) {
81 objectRef.ref = obj;
82 }); 352 });
83 objectRef.style.alignSelf = 'center'; 353 }
84 firstColumn.children.add(objectRef); 354
85 355 void _updateGroup(HtmlElement element, item, int depth) {
86 var secondColumn = flexColumns[1]; 356 _updateLines(element.children[1].children, depth);
87 secondColumn.style.justifyContent = 'flex-end'; 357 if (item is M.HeapSnapshotClassReferences) {
88 secondColumn.style.position = 'relative'; 358 element.children[0].text = Utils.formatSize(item.shallowSize);
89 secondColumn.style.alignItems = 'center'; 359 element.children[2].text = _tree.isExpanded(item) ? 'â–¼' : 'â–º';
90 secondColumn.style.paddingRight = '0.5em'; 360 element.children[3].text = '${item.instances} instances of ';
91 secondColumn.text = Utils.formatSize(vertex.retainedSize); 361 element.children[4] = new ClassRefElement(_isolate, item.clazz,
362 queue: _r.queue)..classes = const ['name'];
363 } else if (item is Iterable) {
364 element.children[0].text = '';
365 if (item.isNotEmpty) {
366 element.children[2].text = _tree.isExpanded(item) ? 'â–¼' : 'â–º';
367 } else {
368 element.children[2].text = '';
369 }
370 element.children[3].text = '';
371 if (item is Iterable<M.HeapSnapshotClassInbound>) {
372 element.children[4] = new SpanElement()..classes = const ['name']
373 ..text = '${item.length} Ingoing references';
374 } else {
375 element.children[4] = new SpanElement()..classes = const ['name']
376 ..text = '${item.length} Outgoing references';
377 }
378 } else {
379 element.children[0].text = '';
380 element.children[2].text = '';
381 element.children[3].text = '';
382 element.children[4] = new SpanElement()..classes = const ['name'];
383 if (item is M.HeapSnapshotClassInbound){
384 element.children[3].text =
385 '${item.count} references from instances of ';
386 element.children[4].children = [
387 new ClassRefElement(_isolate, item.source,
388 queue: _r.queue)
389 ];
390 } else if (item is M.HeapSnapshotClassOutbound){
391 element.children[3]..text = '${item.count} references to instances of ';
392 element.children[4].children = [
393 new ClassRefElement(_isolate, item.target,
394 queue: _r.queue)
395 ];
396 }
397 }
398 }
399
400 static _updateLines(List<Element> lines, int n) {
401 n = Math.max(0, n);
402 while (lines.length > n) {
403 lines.removeLast();
404 }
405 while (lines.length < n) {
406 lines.add(new SpanElement());
407 }
408 }
409
410 static String modeToString(HeapSnapshotTreeMode mode) {
411 switch (mode) {
412 case HeapSnapshotTreeMode.dominatorTree: return 'Dominator tree';
413 case HeapSnapshotTreeMode.groupByClass: return 'Group by class';
414 }
415 throw new Exception('Unknown ProfileTreeMode');
416 }
417
418 List<Element> _createModeSelect() {
419 var s;
420 return [
421 s = new SelectElement()..classes = ['analysis-select']
422 ..value = modeToString(_mode)
423 ..children = HeapSnapshotTreeMode.values.map((mode) {
424 return new OptionElement(value: modeToString(mode),
425 selected: _mode == mode)
426 ..text = modeToString(mode);
427 }).toList(growable: false)
428 ..onChange.listen((_) {
429 _mode = HeapSnapshotTreeMode.values[s.selectedIndex];
430 _r.dirty();
431 })
432 ];
92 } 433 }
93 } 434 }
94
95
96 class MergedVerticesRow extends TableTreeRow {
97 final Isolate isolate;
98 final List<MergedVertex> mergedVertices;
99
100 MergedVerticesRow(TableTree tree,
101 TableTreeRow parent,
102 this.isolate,
103 this.mergedVertices)
104 : super(tree, parent) {
105 }
106
107 bool hasChildren() {
108 return mergedVertices.length > 0;
109 }
110
111 void onShow() {
112 super.onShow();
113
114 if (children.length == 0) {
115 mergedVertices.sort((a, b) => b.shallowSize - a.shallowSize);
116 for (var mergedVertex in mergedVertices) {
117 if (mergedVertex.instances > 0) {
118 var row = new MergedVertexRow(tree, this, isolate, mergedVertex);
119 children.add(row);
120 }
121 }
122 }
123 }
124 }
125
126 class MergedVertexRow extends TableTreeRow {
127 final Isolate isolate;
128 final MergedVertex vertex;
129
130 MergedVertexRow(TableTree tree,
131 TableTreeRow parent,
132 this.isolate,
133 this.vertex)
134 : super(tree, parent) {
135 }
136
137 bool hasChildren() {
138 return vertex.outgoingEdges.length > 0 ||
139 vertex.incomingEdges.length > 0;
140 }
141
142 void onShow() {
143 super.onShow();
144 if (children.length == 0) {
145 children.add(new MergedEdgesRow(tree, this, isolate, vertex, true));
146 children.add(new MergedEdgesRow(tree, this, isolate, vertex, false));
147 }
148
149
150 var firstColumn = flexColumns[0];
151 firstColumn.style.justifyContent = 'flex-start';
152 firstColumn.style.position = 'relative';
153 firstColumn.style.alignItems = 'center';
154
155 var percentNode = new SpanElement();
156 percentNode.text = "${vertex.instances} instances of";
157 percentNode.style.minWidth = '5em';
158 percentNode.style.textAlign = 'right';
159 firstColumn.children.add(percentNode);
160
161 var gap = new SpanElement();
162 gap.style.minWidth = '1em';
163 gap.style.display = 'inline-block';
164 firstColumn.children.add(gap);
165
166 ClassRefElementWrapper classRef = new Element.tag("class-ref");
167 classRef.ref = isolate.getClassByCid(vertex.cid);
168 classRef.style.alignSelf = 'center';
169 firstColumn.children.add(classRef);
170
171 var secondColumn = flexColumns[1];
172 secondColumn.style.justifyContent = 'flex-end';
173 secondColumn.style.position = 'relative';
174 secondColumn.style.alignItems = 'center';
175 secondColumn.style.paddingRight = '0.5em';
176 secondColumn.text = Utils.formatSize(vertex.shallowSize);
177 }
178 }
179
180 class MergedEdgesRow extends TableTreeRow {
181 final Isolate isolate;
182 final MergedVertex vertex;
183 final bool outgoing;
184
185 MergedEdgesRow(TableTree tree,
186 TableTreeRow parent,
187 this.isolate,
188 this.vertex,
189 this.outgoing)
190 : super(tree, parent) {
191 }
192
193 bool hasChildren() {
194 return outgoing
195 ? vertex.outgoingEdges.length > 0
196 : vertex.incomingEdges.length > 0;
197 }
198
199 void onShow() {
200 super.onShow();
201 if (children.length == 0) {
202 if (outgoing) {
203 var outgoingEdges = vertex.outgoingEdges.values.toList();
204 outgoingEdges.sort((a, b) => b.shallowSize - a.shallowSize);
205 for (var edge in outgoingEdges) {
206 if (edge.count > 0) {
207 var row = new MergedEdgeRow(tree, this, isolate, edge, true);
208 children.add(row);
209 }
210 }
211 } else {
212 vertex.incomingEdges.sort((a, b) => b.shallowSize - a.shallowSize);
213 for (var edge in vertex.incomingEdges) {
214 if (edge.count > 0) {
215 var row = new MergedEdgeRow(tree, this, isolate, edge, false);
216 children.add(row);
217 }
218 }
219 }
220 }
221
222 var count = 0;
223 var shallowSize = 0;
224 var edges = outgoing ? vertex.outgoingEdges.values : vertex.incomingEdges;
225 for (var edge in edges) {
226 count += edge.count;
227 shallowSize += edge.shallowSize;
228 }
229
230 var firstColumn = flexColumns[0];
231 firstColumn.style.justifyContent = 'flex-start';
232 firstColumn.style.position = 'relative';
233 firstColumn.style.alignItems = 'center';
234
235 var countNode = new SpanElement();
236 countNode.text = "$count";
237 countNode.style.minWidth = '5em';
238 countNode.style.textAlign = 'right';
239 firstColumn.children.add(countNode);
240
241 var gap = new SpanElement();
242 gap.style.minWidth = '1em';
243 gap.style.display = 'inline-block';
244 firstColumn.children.add(gap);
245
246 var labelNode = new SpanElement();
247 labelNode.text = outgoing ? "Outgoing references" : "Incoming references";
248 firstColumn.children.add(labelNode);
249
250 var secondColumn = flexColumns[1];
251 secondColumn.style.justifyContent = 'flex-end';
252 secondColumn.style.position = 'relative';
253 secondColumn.style.alignItems = 'center';
254 secondColumn.style.paddingRight = '0.5em';
255 secondColumn.text = Utils.formatSize(shallowSize);
256 }
257 }
258
259 class MergedEdgeRow extends TableTreeRow {
260 final Isolate isolate;
261 final MergedEdge edge;
262 final bool outgoing;
263
264 MergedEdgeRow(TableTree tree,
265 TableTreeRow parent,
266 this.isolate,
267 this.edge,
268 this.outgoing)
269 : super(tree, parent) {
270 }
271
272 bool hasChildren() => false;
273
274 void onShow() {
275 super.onShow();
276
277 var firstColumn = flexColumns[0];
278 firstColumn.style.justifyContent = 'flex-start';
279 firstColumn.style.position = 'relative';
280 firstColumn.style.alignItems = 'center';
281
282 var percentNode = new SpanElement();
283 var preposition = outgoing ? "to" : "from";
284 percentNode.text = "${edge.count} references $preposition instances of";
285 percentNode.style.minWidth = '5em';
286 percentNode.style.textAlign = 'right';
287 firstColumn.children.add(percentNode);
288
289 var gap = new SpanElement();
290 gap.style.minWidth = '1em';
291 gap.style.display = 'inline-block';
292 firstColumn.children.add(gap);
293
294 MergedVertex v = outgoing ? edge.target : edge.source;
295 if (v.cid == 0) {
296 var rootName = new SpanElement();
297 rootName.text = '<root>';
298 firstColumn.children.add(rootName);
299 } else {
300 ClassRefElementWrapper classRef = new Element.tag("class-ref");
301 classRef.ref = isolate.getClassByCid(v.cid);
302 classRef.style.alignSelf = 'center';
303 firstColumn.children.add(classRef);
304 }
305
306 var secondColumn = flexColumns[1];
307 secondColumn.style.justifyContent = 'flex-end';
308 secondColumn.style.position = 'relative';
309 secondColumn.style.alignItems = 'center';
310 secondColumn.style.paddingRight = '0.5em';
311 secondColumn.text = Utils.formatSize(edge.shallowSize);
312 }
313 }
314
315
316 class MergedEdge {
317 final MergedVertex source;
318 final MergedVertex target;
319 int count = 0;
320 int shallowSize = 0;
321 int retainedSize = 0;
322
323 MergedEdge(this.source, this.target);
324 }
325
326 class MergedVertex {
327 final int cid;
328 int instances = 0;
329 int shallowSize = 0;
330 int retainedSize = 0;
331
332 List<MergedEdge> incomingEdges = new List<MergedEdge>();
333 Map<int, MergedEdge> outgoingEdges = new Map<int, MergedEdge>();
334
335 MergedVertex(this.cid);
336 }
337
338
339 Future<List<MergedVertex>> buildMergedVertices(ObjectGraph graph) async {
340 Logger.root.info("Start merge vertices");
341
342 var cidToMergedVertex = {};
343
344 for (var vertex in graph.vertices) {
345 var cid = vertex.vmCid;
346 MergedVertex source = cidToMergedVertex[cid];
347 if (source == null) {
348 cidToMergedVertex[cid] = source = new MergedVertex(cid);
349 }
350
351 source.instances++;
352 source.shallowSize += (vertex.shallowSize == null ? 0 : vertex.shallowSize);
353
354 for (var vertex2 in vertex.successors) {
355 var cid2 = vertex2.vmCid;
356 MergedEdge edge = source.outgoingEdges[cid2];
357 if (edge == null) {
358 MergedVertex target = cidToMergedVertex[cid2];
359 if (target == null) {
360 cidToMergedVertex[cid2] = target = new MergedVertex(cid2);
361 }
362 edge = new MergedEdge(source, target);
363 source.outgoingEdges[cid2] = edge;
364 target.incomingEdges.add(edge);
365 }
366 edge.count++;
367 // An over-estimate if there are multiple references to the same object.
368 edge.shallowSize += vertex2.shallowSize == null ? 0 : vertex2.shallowSize;
369 }
370 }
371
372 Logger.root.info("End merge vertices");
373
374 return cidToMergedVertex.values.toList();
375 }
376
377 @CustomTag('heap-snapshot')
378 class HeapSnapshotElement extends ObservatoryElement {
379 @published Isolate isolate;
380 @observable HeapSnapshot snapshot;
381
382 @published String state = 'Requested';
383 @published String analysisSelector = 'DominatorTree';
384
385 HeapSnapshotElement.created() : super.created();
386
387 void analysisSelectorChanged(oldValue) {
388 _update();
389 }
390
391 void isolateChanged(oldValue) {
392 if (isolate == null) return;
393
394 if (isolate.latestSnapshot == null) {
395 _getHeapSnapshot();
396 } else {
397 snapshot = isolate.latestSnapshot;
398 state = 'Loaded';
399 _update();
400 }
401 }
402
403 Future refresh() {
404 return _getHeapSnapshot();
405 }
406
407 Future _getHeapSnapshot() {
408 var completer = new Completer();
409 state = "Requesting heap snapshot...";
410 isolate.getClassRefs();
411
412 bool collectGarbage =
413 app.locationManager.getBoolParameter('collectGarbage', true);
414
415 var stopwatch = new Stopwatch()..start();
416 isolate.fetchHeapSnapshot(collectGarbage).listen((event) {
417 if (event is String) {
418 print("${stopwatch.elapsedMilliseconds} $event");
419 state = event;
420 } else if (event is HeapSnapshot) {
421 snapshot = event;
422 state = 'Loaded';
423 completer.complete(snapshot);
424 _update();
425 } else {
426 throw "Unexpected event $event";
427 }
428 });
429 return completer.future;
430 }
431
432 void _update() {
433 if (snapshot == null) {
434 return;
435 }
436
437 switch(analysisSelector) {
438 case 'DominatorTree':
439 _buildDominatorTree();
440 break;
441 case 'MergeByClass':
442 _buildMergedVertices();
443 break;
444 }
445 }
446
447 void _buildDominatorTree() {
448 var tableBody = shadowRoot.querySelector('#treeBody');
449 var tree = new TableTree(tableBody, 2);
450 var rootRow =
451 new DominatorTreeRow(tree, null, snapshot.graph.root, snapshot);
452 tree.initialize(rootRow);
453 return;
454 }
455
456 void _buildMergedVertices() {
457 state = 'Grouping...';
458 var tableBody = shadowRoot.querySelector('#treeBody');
459 var tree = new TableTree(tableBody, 2);
460 tableBody.children.clear();
461
462 new Future.delayed(const Duration(milliseconds: 500), () {
463 buildMergedVertices(snapshot.graph).then((vertices) {
464 state = 'Loaded';
465 var rootRow = new MergedVerticesRow(tree, null, isolate, vertices);
466 tree.initialize(rootRow);
467 });
468 });
469 }
470 }
OLDNEW
« no previous file with comments | « runtime/observatory/lib/src/elements/css/shared.css ('k') | runtime/observatory/lib/src/elements/heap_snapshot.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698