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

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

Issue 1124153006: Heap snapshot visualizations (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: sync Created 5 years, 7 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
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 library heap_snapshot_element;
6
7 import 'dart:async';
8 import 'dart:html';
9 import 'observatory_element.dart';
10 import 'heap_profile.dart';
11 import 'package:observatory/app.dart';
12 import 'package:observatory/service.dart';
13 import 'package:observatory/elements.dart';
14 import 'package:observatory/object_graph.dart';
15 import 'package:logging/logging.dart';
16 import 'package:polymer/polymer.dart';
17
18 class DominatorTreeRow extends TableTreeRow {
19 final ObjectVertex vertex;
20 final HeapSnapshot snapshot;
21
22 var _domTreeChildren;
23 get domTreeChildren {
24 if (_domTreeChildren == null) {
25 _domTreeChildren = vertex.dominatorTreeChildren();
26 }
27 return _domTreeChildren;
28 }
29
30 DominatorTreeRow(TableTree tree,
31 TableTreeRow parent,
32 this.vertex,
33 this.snapshot)
34 : super(tree, parent) {
35 }
36
37 bool hasChildren() {
38 return domTreeChildren.length > 0;
39 }
40
41 static const int kMaxChildren = 100;
42 static const int kMinRetainedSize = 4096;
43
44 void onShow() {
45 super.onShow();
46 if (children.length == 0) {
47 domTreeChildren.sort((a, b) => b.retainedSize - a.retainedSize);
48 int includedChildren = 0;
49 for (var childVertex in domTreeChildren) {
50 if (childVertex.retainedSize >= kMinRetainedSize) {
51 if (++includedChildren <= kMaxChildren) {
52 var row = new DominatorTreeRow(tree, this, childVertex, snapshot);
53 children.add(row);
54 }
55 }
56 }
57 }
58
59 var firstColumn = flexColumns[0];
60 firstColumn.style.justifyContent = 'flex-start';
61 firstColumn.style.position = 'relative';
62 firstColumn.style.alignItems = 'center';
63 firstColumn.style.setProperty('overflow-x', 'hidden');
64
65 var percentRetained = vertex.retainedSize / snapshot.graph.size;
66 var percentNode = new SpanElement();
67 percentNode.text = Utils.formatPercentNormalized(percentRetained);
68 percentNode.style.minWidth = '5em';
69 percentNode.style.textAlign = 'right';
70 percentNode.title = "Retaining x of y.";
71 percentNode.style.display = 'inline-block';
72 firstColumn.children.add(percentNode);
73
74 var gap = new SpanElement();
75 gap.style.minWidth = '1em';
76 gap.style.display = 'inline-block';
77 firstColumn.children.add(gap);
78
79 AnyServiceRefElement objectRef = new Element.tag("any-service-ref");
80 String hexAddress = vertex.address.toRadixString(16);
81 snapshot.isolate.getObjectByAddress(hexAddress).then((obj) {
82 objectRef.ref = obj;
83 });
84 objectRef.style.alignSelf = 'center';
85 firstColumn.children.add(objectRef);
86
87 var secondColumn = flexColumns[1];
88 secondColumn.style.justifyContent = 'flex-end';
89 secondColumn.style.position = 'relative';
90 secondColumn.style.alignItems = 'center';
91 secondColumn.style.paddingRight = '0.5em';
92 secondColumn.text = Utils.formatSize(vertex.retainedSize);
93 }
94 }
95
96
97 class MergedVerticesRow extends TableTreeRow {
98 final Isolate isolate;
99 final List<MergedVertex> mergedVertices;
100
101 MergedVerticesRow(TableTree tree,
102 TableTreeRow parent,
103 this.isolate,
104 this.mergedVertices)
105 : super(tree, parent) {
106 }
107
108 bool hasChildren() {
109 return mergedVertices.length > 0;
110 }
111
112 void onShow() {
113 super.onShow();
114
115 if (children.length == 0) {
116 mergedVertices.sort((a, b) => b.shallowSize - a.shallowSize);
117 for (var mergedVertex in mergedVertices) {
118 if (mergedVertex.instances > 0) {
119 var row = new MergedVertexRow(tree, this, isolate, mergedVertex);
120 children.add(row);
121 }
122 }
123 }
124 }
125 }
126
127 class MergedVertexRow extends TableTreeRow {
128 final Isolate isolate;
129 final MergedVertex vertex;
130
131 MergedVertexRow(TableTree tree,
132 TableTreeRow parent,
133 this.isolate,
134 this.vertex)
135 : super(tree, parent) {
136 }
137
138 bool hasChildren() {
139 return vertex.outgoingEdges.length > 0 ||
140 vertex.incomingEdges.length > 0;
141 }
142
143 void onShow() {
144 super.onShow();
145 if (children.length == 0) {
146 children.add(new MergedEdgesRow(tree, this, isolate, vertex, true));
147 children.add(new MergedEdgesRow(tree, this, isolate, vertex, false));
148 }
149
150
151 var firstColumn = flexColumns[0];
152 firstColumn.style.justifyContent = 'flex-start';
153 firstColumn.style.position = 'relative';
154 firstColumn.style.alignItems = 'center';
155
156 var percentNode = new SpanElement();
157 percentNode.text = "${vertex.instances} instances of";
158 percentNode.style.minWidth = '5em';
159 percentNode.style.textAlign = 'right';
160 firstColumn.children.add(percentNode);
161
162 var gap = new SpanElement();
163 gap.style.minWidth = '1em';
164 gap.style.display = 'inline-block';
165 firstColumn.children.add(gap);
166
167 ClassRefElement classRef = new Element.tag("class-ref");
168 classRef.ref = isolate.getClassByCid(vertex.cid);
169 classRef.style.alignSelf = 'center';
170 firstColumn.children.add(classRef);
171
172 var secondColumn = flexColumns[1];
173 secondColumn.style.justifyContent = 'flex-end';
174 secondColumn.style.position = 'relative';
175 secondColumn.style.alignItems = 'center';
176 secondColumn.style.paddingRight = '0.5em';
177 secondColumn.text = Utils.formatSize(vertex.shallowSize);
178 }
179 }
180
181 class MergedEdgesRow extends TableTreeRow {
182 final Isolate isolate;
183 final MergedVertex vertex;
184 final bool outgoing;
185
186 MergedEdgesRow(TableTree tree,
187 TableTreeRow parent,
188 this.isolate,
189 this.vertex,
190 this.outgoing)
191 : super(tree, parent) {
192 }
193
194 bool hasChildren() {
195 return outgoing
196 ? vertex.outgoingEdges.length > 0
197 : vertex.incomingEdges.length > 0;
198 }
199
200 void onShow() {
201 super.onShow();
202 if (children.length == 0) {
203 if (outgoing) {
204 var outgoingEdges = vertex.outgoingEdges.values.toList();
205 outgoingEdges.sort((a, b) => b.shallowSize - a.shallowSize);
206 for (var edge in outgoingEdges) {
207 if (edge.count > 0) {
208 var row = new MergedEdgeRow(tree, this, isolate, edge, true);
209 children.add(row);
210 }
211 }
212 } else {
213 vertex.incomingEdges.sort((a, b) => b.shallowSize - a.shallowSize);
214 for (var edge in vertex.incomingEdges) {
215 if (edge.count > 0) {
216 var row = new MergedEdgeRow(tree, this, isolate, edge, false);
217 children.add(row);
218 }
219 }
220 }
221 }
222
223 var count = 0;
224 var shallowSize = 0;
225 var edges = outgoing ? vertex.outgoingEdges.values : vertex.incomingEdges;
226 for (var edge in edges) {
227 count += edge.count;
228 shallowSize += edge.shallowSize;
229 }
230
231 var firstColumn = flexColumns[0];
232 firstColumn.style.justifyContent = 'flex-start';
233 firstColumn.style.position = 'relative';
234 firstColumn.style.alignItems = 'center';
235
236 var countNode = new SpanElement();
237 countNode.text = "$count";
238 countNode.style.minWidth = '5em';
239 countNode.style.textAlign = 'right';
240 firstColumn.children.add(countNode);
241
242 var gap = new SpanElement();
243 gap.style.minWidth = '1em';
244 gap.style.display = 'inline-block';
245 firstColumn.children.add(gap);
246
247 var labelNode = new SpanElement();
248 labelNode.text = outgoing ? "Outgoing references" : "Incoming references";
249 firstColumn.children.add(labelNode);
250
251 var secondColumn = flexColumns[1];
252 secondColumn.style.justifyContent = 'flex-end';
253 secondColumn.style.position = 'relative';
254 secondColumn.style.alignItems = 'center';
255 secondColumn.style.paddingRight = '0.5em';
256 secondColumn.text = Utils.formatSize(shallowSize);
257 }
258 }
259
260 class MergedEdgeRow extends TableTreeRow {
261 final Isolate isolate;
262 final MergedEdge edge;
263 final bool outgoing;
264
265 MergedEdgeRow(TableTree tree,
266 TableTreeRow parent,
267 this.isolate,
268 this.edge,
269 this.outgoing)
270 : super(tree, parent) {
271 }
272
273 bool hasChildren() => false;
274
275 void onShow() {
276 super.onShow();
277
278 var firstColumn = flexColumns[0];
279 firstColumn.style.justifyContent = 'flex-start';
280 firstColumn.style.position = 'relative';
281 firstColumn.style.alignItems = 'center';
282
283 var percentNode = new SpanElement();
284 var preposition = outgoing ? "to" : "from";
285 percentNode.text = "${edge.count} references $preposition instances of";
286 percentNode.style.minWidth = '5em';
287 percentNode.style.textAlign = 'right';
288 firstColumn.children.add(percentNode);
289
290 var gap = new SpanElement();
291 gap.style.minWidth = '1em';
292 gap.style.display = 'inline-block';
293 firstColumn.children.add(gap);
294
295 MergedVertex v = outgoing ? edge.target : edge.source;
296 if (v.cid == 0) {
297 var rootName = new SpanElement();
298 rootName.text = '<root>';
299 firstColumn.children.add(rootName);
300 } else {
301 ClassRefElement classRef = new Element.tag("class-ref");
302 classRef.ref = isolate.getClassByCid(v.cid);
303 classRef.style.alignSelf = 'center';
304 firstColumn.children.add(classRef);
305 }
306
307 var secondColumn = flexColumns[1];
308 secondColumn.style.justifyContent = 'flex-end';
309 secondColumn.style.position = 'relative';
310 secondColumn.style.alignItems = 'center';
311 secondColumn.style.paddingRight = '0.5em';
312 secondColumn.text = Utils.formatSize(edge.shallowSize);
313 }
314 }
315
316
317 class MergedEdge {
318 final MergedVertex source;
319 final MergedVertex target;
320 int count = 0;
321 int shallowSize = 0;
322 int retainedSize = 0;
323
324 MergedEdge(this.source, this.target);
325 }
326
327 class MergedVertex {
328 final int cid;
329 int instances = 0;
330 int shallowSize = 0;
331 int retainedSize = 0;
332
333 List<MergedEdge> incomingEdges = new List<MergedEdge>();
334 Map<int, MergedEdge> outgoingEdges = new Map<int, MergedEdge>();
335
336 MergedVertex(this.cid);
337 }
338
339
340 Future<List<MergedVertex>> buildMergedVertices(ObjectGraph graph) async {
341 var cidToMergedVertex = {};
342
343 for (var vertex in graph.vertices) {
344 var cid = vertex.vmCid;
345 MergedVertex source = cidToMergedVertex[cid];
346 if (source == null) {
347 cidToMergedVertex[cid] = source = new MergedVertex(cid);
348 }
349
350 source.instances++;
351 source.shallowSize += (vertex.shallowSize == null ? 0 : vertex.shallowSize);
352
353 for (var vertex2 in vertex.successors) {
354 var cid2 = vertex2.vmCid;
355 MergedEdge edge = source.outgoingEdges[cid2];
356 if (edge == null) {
357 MergedVertex target = cidToMergedVertex[cid2];
358 if (target == null) {
359 cidToMergedVertex[cid2] = target = new MergedVertex(cid2);
360 }
361 edge = new MergedEdge(source, target);
362 source.outgoingEdges[cid2] = edge;
363 target.incomingEdges.add(edge);
364 }
365 edge.count++;
366 // An over-estimate if there are multiple references to the same object.
367 edge.shallowSize += vertex2.shallowSize == null ? 0 : vertex2.shallowSize;
368 }
369 }
370
371 return cidToMergedVertex.values.toList();
372 }
373
374 @CustomTag('heap-snapshot')
375 class HeapSnapshotElement extends ObservatoryElement {
376 @published Isolate isolate;
377 @observable HeapSnapshot snapshot;
378
379 @published String state = 'Requested';
380 @published String analysisSelector = 'DominatorTree';
381
382 HeapSnapshotElement.created() : super.created();
383
384 void analysisSelectorChanged(oldValue) {
385 _update();
386 }
387
388 void isolateChanged(oldValue) {
389 if (isolate == null) return;
390
391 if (isolate.latestSnapshot == null) {
392 _getHeapSnapshot();
393 } else {
394 snapshot = isolate.latestSnapshot;
395 state = 'Loaded';
396 _update();
397 }
398 }
399
400 Future refresh() {
401 return _getHeapSnapshot();
402 }
403
404 Future _getHeapSnapshot() {
405 var completer = new Completer();
406 state = "Requesting heap snapshot...";
407 isolate.getClassRefs();
408 var stopwatch = new Stopwatch()..start();
409 isolate.fetchHeapSnapshot().listen((event) {
410 if (event is String) {
411 print("${stopwatch.elapsedMilliseconds} $event");
412 state = event;
413 } else if (event is HeapSnapshot) {
414 snapshot = event;
415 state = 'Loaded';
416 completer.complete(snapshot);
417 _update();
418 } else {
419 throw "Unexpected event $event";
420 }
421 });
422 return completer.future;
423 }
424
425 void _update() {
426 if (snapshot == null) {
427 return;
428 }
429
430 switch(analysisSelector) {
431 case 'DominatorTree':
432 _buildDominatorTree();
433 break;
434 case 'MergeByClass':
435 _buildMergedVertices();
436 break;
437 }
438 }
439
440 void _buildDominatorTree() {
441 var tableBody = shadowRoot.querySelector('#treeBody');
442 var tree = new TableTree(tableBody, 2);
443 var rootRow =
444 new DominatorTreeRow(tree, null, snapshot.graph.root, snapshot);
445 tree.initialize(rootRow);
446 return;
447 }
448
449 void _buildMergedVertices() {
450 state = 'Grouping...';
451 var tableBody = shadowRoot.querySelector('#treeBody');
452 var tree = new TableTree(tableBody, 2);
453 tableBody.children.clear();
454
455 new Future.delayed(const Duration(milliseconds: 500), () {
456 buildMergedVertices(snapshot.graph).then((vertices) {
457 state = 'Loaded';
458 var rootRow = new MergedVerticesRow(tree, null, isolate, vertices);
459 tree.initialize(rootRow);
460 });
461 });
462 }
463 }
OLDNEW
« no previous file with comments | « runtime/observatory/lib/src/elements/heap_profile.dart ('k') | runtime/observatory/lib/src/elements/heap_snapshot.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698