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

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

Issue 2480293003: Observatory: Add view of dominator tree with siblings merged by class. (Closed)
Patch Set: . Created 4 years, 1 month 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 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 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 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. 7 // BSD-style license that can be found in the LICENSE file.
8 8
9 import 'dart:async'; 9 import 'dart:async';
10 import 'dart:html'; 10 import 'dart:html';
11 import 'dart:math' as Math; 11 import 'dart:math' as Math;
12 import 'package:observatory/models.dart' as M; 12 import 'package:observatory/models.dart' as M;
13 import 'package:observatory/src/elements/class_ref.dart'; 13 import 'package:observatory/src/elements/class_ref.dart';
14 import 'package:observatory/src/elements/containers/virtual_tree.dart'; 14 import 'package:observatory/src/elements/containers/virtual_tree.dart';
15 import 'package:observatory/src/elements/helpers/any_ref.dart'; 15 import 'package:observatory/src/elements/helpers/any_ref.dart';
16 import 'package:observatory/src/elements/helpers/nav_bar.dart'; 16 import 'package:observatory/src/elements/helpers/nav_bar.dart';
17 import 'package:observatory/src/elements/helpers/nav_menu.dart'; 17 import 'package:observatory/src/elements/helpers/nav_menu.dart';
18 import 'package:observatory/src/elements/helpers/rendering_scheduler.dart'; 18 import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
19 import 'package:observatory/src/elements/helpers/tag.dart'; 19 import 'package:observatory/src/elements/helpers/tag.dart';
20 import 'package:observatory/src/elements/nav/isolate_menu.dart'; 20 import 'package:observatory/src/elements/nav/isolate_menu.dart';
21 import 'package:observatory/src/elements/nav/notify.dart'; 21 import 'package:observatory/src/elements/nav/notify.dart';
22 import 'package:observatory/src/elements/nav/refresh.dart'; 22 import 'package:observatory/src/elements/nav/refresh.dart';
23 import 'package:observatory/src/elements/nav/top_menu.dart'; 23 import 'package:observatory/src/elements/nav/top_menu.dart';
24 import 'package:observatory/src/elements/nav/vm_menu.dart'; 24 import 'package:observatory/src/elements/nav/vm_menu.dart';
25 import 'package:observatory/utils.dart'; 25 import 'package:observatory/utils.dart';
26 26
27 enum HeapSnapshotTreeMode { dominatorTree, groupByClass } 27 enum HeapSnapshotTreeMode { dominatorTree, mergedDominatorTree, groupByClass }
28 28
29 class HeapSnapshotElement extends HtmlElement implements Renderable { 29 class HeapSnapshotElement extends HtmlElement implements Renderable {
30 static const tag = 30 static const tag =
31 const Tag<HeapSnapshotElement>('heap-snapshot', dependencies: const [ 31 const Tag<HeapSnapshotElement>('heap-snapshot', dependencies: const [
32 ClassRefElement.tag, 32 ClassRefElement.tag,
33 NavTopMenuElement.tag, 33 NavTopMenuElement.tag,
34 NavVMMenuElement.tag, 34 NavVMMenuElement.tag,
35 NavIsolateMenuElement.tag, 35 NavIsolateMenuElement.tag,
36 NavRefreshElement.tag, 36 NavRefreshElement.tag,
37 NavNotifyElement.tag, 37 NavNotifyElement.tag,
(...skipping 221 matching lines...) Expand 10 before | Expand all | Expand 10 after
259 'plus its own shallow size, and is the amount of memory ' 259 'plus its own shallow size, and is the amount of memory '
260 'that would be freed if the object became garbage.'; 260 'that would be freed if the object became garbage.';
261 report.addAll([ 261 report.addAll([
262 new DivElement() 262 new DivElement()
263 ..classes = ['content-centered-big', 'explanation'] 263 ..classes = ['content-centered-big', 'explanation']
264 ..text = text 264 ..text = text
265 ..title = text, 265 ..title = text,
266 _tree 266 _tree
267 ]); 267 ]);
268 break; 268 break;
269 case HeapSnapshotTreeMode.mergedDominatorTree:
270 _tree = new VirtualTreeElement(
271 _createMergedDominator, _updateMergedDominator,
272 _getChildrenMergedDominator,
273 items: _getChildrenMergedDominator(_snapshot.mergedDominatorTree),
274 queue: _r.queue);
275 _tree.expand(_snapshot.mergedDominatorTree);
276 final text = 'In a heap dominator tree, an object X is a parent of '
277 'object Y if every path from the root to Y goes through '
278 'X. This allows you to find "choke points" that are '
279 'holding onto a lot of memory. If an object becomes '
280 'garbage, all its children in the dominator tree become '
281 'garbage as well. '
282 'The retained size of an object is the sum of the '
283 'retained sizes of its children in the dominator tree '
Cutch 2016/11/14 17:24:13 This text should explain what "merge" means in mor
rmacnak 2016/11/15 00:44:15 Done.
284 'plus its own shallow size, and is the amount of memory '
285 'that would be freed if the object became garbage.';
286 report.addAll([
287 new DivElement()
288 ..classes = ['content-centered-big', 'explanation']
289 ..text = text
290 ..title = text,
291 _tree
292 ]);
293 break;
269 case HeapSnapshotTreeMode.groupByClass: 294 case HeapSnapshotTreeMode.groupByClass:
270 final items = _snapshot.classReferences.toList(); 295 final items = _snapshot.classReferences.toList();
271 items.sort((a, b) => b.shallowSize - a.shallowSize); 296 items.sort((a, b) => b.shallowSize - a.shallowSize);
272 _tree = new VirtualTreeElement( 297 _tree = new VirtualTreeElement(
273 _createGroup, _updateGroup, _getChildrenGroup, 298 _createGroup, _updateGroup, _getChildrenGroup,
274 items: items, queue: _r.queue); 299 items: items, queue: _r.queue);
275 _tree.expand(_snapshot.dominatorTree); 300 _tree.expand(_snapshot.dominatorTree);
276 report.add(_tree); 301 report.add(_tree);
277 break; 302 break;
278 default: 303 default:
(...skipping 13 matching lines...) Expand all
292 new ButtonElement() 317 new ButtonElement()
293 ..classes = ['expander'] 318 ..classes = ['expander']
294 ..onClick.listen((_) => toggle(autoToggleSingleChildNodes: true)), 319 ..onClick.listen((_) => toggle(autoToggleSingleChildNodes: true)),
295 new SpanElement() 320 new SpanElement()
296 ..classes = ['percentage'] 321 ..classes = ['percentage']
297 ..title = 'percentage of heap being retained', 322 ..title = 'percentage of heap being retained',
298 new SpanElement()..classes = ['name'] 323 new SpanElement()..classes = ['name']
299 ]; 324 ];
300 } 325 }
301 326
327 static Element _createMergedDominator(toggle) {
328 return new DivElement()
329 ..classes = ['tree-item']
330 ..children = [
331 new SpanElement()
332 ..classes = ['size']
333 ..title = 'retained size',
334 new SpanElement()..classes = ['lines'],
335 new ButtonElement()
336 ..classes = ['expander']
337 ..onClick.listen((_) => toggle(autoToggleSingleChildNodes: true)),
338 new SpanElement()
339 ..classes = ['percentage']
340 ..title = 'percentage of heap being retained',
341 new SpanElement()..classes = ['name']
342 ];
343 }
344
302 static Element _createGroup(toggle) { 345 static Element _createGroup(toggle) {
303 return new DivElement() 346 return new DivElement()
304 ..classes = ['tree-item'] 347 ..classes = ['tree-item']
305 ..children = [ 348 ..children = [
306 new SpanElement() 349 new SpanElement()
307 ..classes = ['size'] 350 ..classes = ['size']
308 ..title = 'shallow size', 351 ..title = 'shallow size',
309 new SpanElement()..classes = ['lines'], 352 new SpanElement()..classes = ['lines'],
310 new ButtonElement() 353 new ButtonElement()
311 ..classes = ['expander'] 354 ..classes = ['expander']
312 ..onClick.listen((_) => toggle(autoToggleSingleChildNodes: true)), 355 ..onClick.listen((_) => toggle(autoToggleSingleChildNodes: true)),
313 new SpanElement() 356 new SpanElement()
314 ..classes = ['count'] 357 ..classes = ['count']
315 ..title = 'shallow size', 358 ..title = 'shallow size',
316 new SpanElement()..classes = ['name'] 359 new SpanElement()..classes = ['name']
317 ]; 360 ];
318 } 361 }
319 362
320 static const int kMaxChildren = 100; 363 static const int kMaxChildren = 100;
321 static const int kMinRetainedSize = 4096; 364 static const int kMinRetainedSize = 4096;
322 365
323 static _getChildrenDominator(M.HeapSnapshotDominatorNode node) { 366 static _getChildrenDominator(M.HeapSnapshotDominatorNode node) {
324 final list = node.children.toList(); 367 final list = node.children.toList();
325 list.sort((a, b) => b.retainedSize - a.retainedSize); 368 list.sort((a, b) => b.retainedSize - a.retainedSize);
326 return list 369 return list
327 .where((child) => child.retainedSize >= kMinRetainedSize) 370 .where((child) => child.retainedSize >= kMinRetainedSize)
328 .take(kMaxChildren); 371 .take(kMaxChildren);
329 } 372 }
373 static _getChildrenMergedDominator(M.HeapSnapshotMergedDominatorNode node) {
374 final list = node.children.toList();
375 list.sort((a, b) => b.retainedSize - a.retainedSize);
376 return list
377 .where((child) => child.retainedSize >= kMinRetainedSize)
378 .take(kMaxChildren);
379 }
330 380
331 static _getChildrenGroup(item) { 381 static _getChildrenGroup(item) {
332 if (item is M.HeapSnapshotClassReferences) { 382 if (item is M.HeapSnapshotClassReferences) {
333 if (item.inbounds.isNotEmpty || item.outbounds.isNotEmpty) { 383 if (item.inbounds.isNotEmpty || item.outbounds.isNotEmpty) {
334 return [item.inbounds, item.outbounds]; 384 return [item.inbounds, item.outbounds];
335 } 385 }
336 } else if (item is Iterable) { 386 } else if (item is Iterable) {
337 return item.toList()..sort((a, b) => b.shallowSize - a.shallowSize); 387 return item.toList()..sort((a, b) => b.shallowSize - a.shallowSize);
338 } 388 }
339 return const []; 389 return const [];
(...skipping 14 matching lines...) Expand all
354 ..classes = ['name'] 404 ..classes = ['name']
355 ..text = 'Loading...'; 405 ..text = 'Loading...';
356 element.children[4] = wrapper; 406 element.children[4] = wrapper;
357 node.object.then((object) { 407 node.object.then((object) {
358 wrapper 408 wrapper
359 ..text = '' 409 ..text = ''
360 ..children = [anyRef(_isolate, object, _instances, queue: _r.queue)]; 410 ..children = [anyRef(_isolate, object, _instances, queue: _r.queue)];
361 }); 411 });
362 } 412 }
363 413
414 void _updateMergedDominator(
415 HtmlElement element, M.HeapSnapshotMergedDominatorNode node, int depth) {
416 element.children[0].text = Utils.formatSize(node.retainedSize);
417 _updateLines(element.children[1].children, depth);
418 if (_getChildrenMergedDominator(node).isNotEmpty) {
419 element.children[2].text = _tree.isExpanded(node) ? '▼' : '►';
420 } else {
421 element.children[2].text = '';
422 }
423 element.children[3].text =
424 Utils.formatPercentNormalized(node.retainedSize * 1.0 / _snapshot.size);
425 final wrapper = new SpanElement()
426 ..classes = ['name']
427 ..text = 'Loading...';
428 element.children[4] = wrapper;
429 node.klass.then((klass) {
430 wrapper
431 ..text = ''
432 ..children = [
433 new SpanElement()..text = '${node.instanceCount} instances of ',
434 anyRef(_isolate, klass, _instances, queue: _r.queue)
435 ];
436 });
437 }
438
364 void _updateGroup(HtmlElement element, item, int depth) { 439 void _updateGroup(HtmlElement element, item, int depth) {
365 _updateLines(element.children[1].children, depth); 440 _updateLines(element.children[1].children, depth);
366 if (item is M.HeapSnapshotClassReferences) { 441 if (item is M.HeapSnapshotClassReferences) {
367 element.children[0].text = Utils.formatSize(item.shallowSize); 442 element.children[0].text = Utils.formatSize(item.shallowSize);
368 element.children[2].text = _tree.isExpanded(item) ? '▼' : '►'; 443 element.children[2].text = _tree.isExpanded(item) ? '▼' : '►';
369 element.children[3].text = '${item.instances} instances of '; 444 element.children[3].text = '${item.instances} instances of ';
370 element.children[4] = new ClassRefElement(_isolate, item.clazz, 445 element.children[4] = new ClassRefElement(_isolate, item.clazz,
371 queue: _r.queue)..classes = ['name']; 446 queue: _r.queue)..classes = ['name'];
372 } else if (item is Iterable) { 447 } else if (item is Iterable) {
373 element.children[0].text = ''; 448 element.children[0].text = '';
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
417 } 492 }
418 while (lines.length < n) { 493 while (lines.length < n) {
419 lines.add(new SpanElement()); 494 lines.add(new SpanElement());
420 } 495 }
421 } 496 }
422 497
423 static String modeToString(HeapSnapshotTreeMode mode) { 498 static String modeToString(HeapSnapshotTreeMode mode) {
424 switch (mode) { 499 switch (mode) {
425 case HeapSnapshotTreeMode.dominatorTree: 500 case HeapSnapshotTreeMode.dominatorTree:
426 return 'Dominator tree'; 501 return 'Dominator tree';
502 case HeapSnapshotTreeMode.mergedDominatorTree:
503 return 'Dominator tree (merged siblings by class)';
427 case HeapSnapshotTreeMode.groupByClass: 504 case HeapSnapshotTreeMode.groupByClass:
428 return 'Group by class'; 505 return 'Group by class';
429 } 506 }
430 throw new Exception('Unknown ProfileTreeMode'); 507 throw new Exception('Unknown ProfileTreeMode');
431 } 508 }
432 509
433 List<Element> _createModeSelect() { 510 List<Element> _createModeSelect() {
434 var s; 511 var s;
435 return [ 512 return [
436 s = new SelectElement() 513 s = new SelectElement()
437 ..classes = ['analysis-select'] 514 ..classes = ['analysis-select']
438 ..value = modeToString(_mode) 515 ..value = modeToString(_mode)
439 ..children = HeapSnapshotTreeMode.values.map((mode) { 516 ..children = HeapSnapshotTreeMode.values.map((mode) {
440 return new OptionElement( 517 return new OptionElement(
441 value: modeToString(mode), 518 value: modeToString(mode),
442 selected: _mode == mode)..text = modeToString(mode); 519 selected: _mode == mode)..text = modeToString(mode);
443 }).toList(growable: false) 520 }).toList(growable: false)
444 ..onChange.listen((_) { 521 ..onChange.listen((_) {
445 _mode = HeapSnapshotTreeMode.values[s.selectedIndex]; 522 _mode = HeapSnapshotTreeMode.values[s.selectedIndex];
446 _r.dirty(); 523 _r.dirty();
447 }) 524 })
448 ]; 525 ];
449 } 526 }
450 } 527 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698