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

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: 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}";
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 percentNode.text = "${edge.count}";
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
296 ClassRefElement classRef = new Element.tag("class-ref");
297 classRef.ref = isolate.getClassByCid(v.cid);
298 classRef.style.alignSelf = 'center';
299 firstColumn.children.add(classRef);
300
301 var secondColumn = flexColumns[1];
302 secondColumn.style.justifyContent = 'flex-end';
303 secondColumn.style.position = 'relative';
304 secondColumn.style.alignItems = 'center';
305 secondColumn.style.paddingRight = '0.5em';
306 secondColumn.text = Utils.formatSize(v.shallowSize);
307 }
308 }
309
310
311 class MergedEdge {
312 final MergedVertex source;
313 final MergedVertex target;
314 int count = 0;
315 int shallowSize = 0;
316 int retainedSize = 0;
317
318 MergedEdge(this.source, this.target);
319 }
320
321 class MergedVertex {
322 final int cid;
323 int instances = 0;
324 int shallowSize = 0;
325 int retainedSize = 0;
326
327 List<MergedEdge> incomingEdges = new List<MergedEdge>();
328 Map<int, MergedEdge> outgoingEdges = new Map<int, MergedEdge>();
329
330 MergedVertex(this.cid);
331 }
332
333
334 List<MergedVertex> buildMergedVertices(ObjectGraph graph) {
335 var cidToMergedVertex = {};
336
337 for (var vertex in graph.vertices) {
338 var cid = vertex.vmCid;
339 MergedVertex source = cidToMergedVertex[cid];
340 if (source == null) {
341 cidToMergedVertex[cid] = source = new MergedVertex(cid);
342 }
343
344 source.instances++;
345 source.shallowSize += (vertex.shallowSize == null ? 0 : vertex.shallowSize);
346
347 for (var vertex2 in vertex.successors) {
348 var cid2 = vertex2.vmCid;
349 MergedEdge edge = source.outgoingEdges[cid2];
350 if (edge == null) {
351 MergedVertex target = cidToMergedVertex[cid2];
352 if (target == null) {
353 cidToMergedVertex[cid2] = target = new MergedVertex(cid2);
354 }
355 edge = new MergedEdge(source, target);
356 source.outgoingEdges[cid2] = edge;
357 target.incomingEdges.add(edge);
358 }
359 edge.count++;
360 // An over-estimate if there are multiple references to the same object.
361 edge.shallowSize += vertex2.shallowSize == null ? 0 : vertex2.shallowSize;
362 }
363 }
364
365 return cidToMergedVertex.values.toList();
366 }
367
368 @CustomTag('heap-snapshot')
369 class HeapSnapshotElement extends ObservatoryElement {
370 @published Isolate isolate;
371 @observable HeapSnapshot snapshot;
372
373 @published String state = 'Requested';
374 @published String analysisSelector = 'DominatorTree';
375
376 HeapSnapshotElement.created() : super.created();
377
378 void analysisSelectorChanged(oldValue) {
379 _update();
380 }
381
382 void isolateChanged(oldValue) {
383 if (isolate == null) return;
384
385 if (isolate.latestSnapshot == null) {
386 _getHeapSnapshot();
387 } else {
388 snapshot = isolate.latestSnapshot;
389 state = 'Loaded';
390 _update();
391 }
392 }
393
394 void refresh(var done) {
395 _getHeapSnapshot().whenComplete(done);
396 }
397
398 void _getHeapSnapshot() {
399 state = "Requesting heap snapshot...";
400 isolate.getClassRefs();
401 var stopwatch = new Stopwatch()..start();
402 isolate.fetchHeapSnapshot().listen((event) {
403 if (event is String) {
404 print("${stopwatch.elapsedMilliseconds} $event");
405 state = event;
406 } else if (event is HeapSnapshot) {
407 snapshot = event;
408 _update();
409 } else {
410 throw "Unexpected event $event";
411 }
412 });
413 }
414
415 void _update() {
416 if (snapshot == null) {
417 return;
418 }
419
420 switch(analysisSelector) {
421 case 'DominatorTree':
422 _buildDominatorTree();
423 break;
424 case 'MergeByClass':
425 _buildMergedVertices();
426 break;
427 }
428 }
429
430 void _buildDominatorTree() {
431 var tableBody = shadowRoot.querySelector('#treeBody');
432 var tree = new TableTree(tableBody, 2);
433 var rootRow =
434 new DominatorTreeRow(tree, null, snapshot.graph.root, snapshot);
435 tree.initialize(rootRow);
436 return;
437 }
438
439 void _buildMergedVertices() {
440 var vertices = buildMergedVertices(snapshot.graph);
441 var tableBody = shadowRoot.querySelector('#treeBody');
442 var tree = new TableTree(tableBody, 2);
443 var rootRow = new MergedVerticesRow(tree, null, isolate, vertices);
444 tree.initialize(rootRow);
445 return;
446 }
447 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698