Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 ); | |
|
koda
2015/05/19 20:12:35
Line length.
rmacnak
2015/05/19 22:16:08
Done.
| |
| 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 = new DominatorTreeRow(tree, null, snapshot.graph.root, snapshot ); | |
|
koda
2015/05/19 20:12:35
Ditto.
rmacnak
2015/05/19 22:16:08
Done.
| |
| 434 tree.initialize(rootRow); | |
| 435 return; | |
| 436 } | |
| 437 | |
| 438 void _buildMergedVertices() { | |
| 439 var vertices = buildMergedVertices(snapshot.graph); | |
| 440 var tableBody = shadowRoot.querySelector('#treeBody'); | |
| 441 var tree = new TableTree(tableBody, 2); | |
| 442 var rootRow = new MergedVerticesRow(tree, null, isolate, vertices); | |
| 443 tree.initialize(rootRow); | |
| 444 return; | |
| 445 } | |
| 446 } | |
| OLD | NEW |