diff --git a/runtime/observatory/lib/object_graph.dart b/runtime/observatory/lib/object_graph.dart index caf100aebc8..6e448723905 100644 --- a/runtime/observatory/lib/object_graph.dart +++ b/runtime/observatory/lib/object_graph.dart @@ -333,6 +333,36 @@ class _SnapshotObject implements SnapshotObject { } } +class _SyntheticSnapshotObject implements SnapshotObject { + String _description; + SnapshotClass _klass; + int _internalSize; + int _externalSize; + int _retainedSize; + List _successors; + List _predecessors; + SnapshotObject _parent; + List _children; + + String get label => null; + String get description => _description; + SnapshotClass get klass => _klass; + + int get shallowSize => internalSize + externalSize; + int get internalSize => _internalSize; + int get externalSize => _externalSize; + int get retainedSize => _retainedSize; + + Iterable get successors => _successors; + Iterable get predecessors => _predecessors; + SnapshotObject get parent => _parent; + Iterable get children => _children; + + Iterable get objects sync* { + yield this; + } +} + /// A set of sibling objects in the graph's dominator tree that have the same /// class. abstract class SnapshotMergedDominator { @@ -387,7 +417,11 @@ class _SnapshotMergedDominator implements SnapshotMergedDominator { int get hashCode => _id ^ _graph.hashCode; - String get description => "$instanceCount instances of ${klass.name}"; + String get description { + return _id == _ROOT + ? "Live Objects + External" + : "$instanceCount instances of ${klass.name}"; + } SnapshotClass get klass => _graph._classes[_graph._cids[_id]]; @@ -465,6 +499,28 @@ class _SnapshotMergedDominator implements SnapshotMergedDominator { } } +class _SyntheticSnapshotMergedDominator implements SnapshotMergedDominator { + String _description; + SnapshotClass _klass; + int _internalSize; + int _externalSize; + int _retainedSize; + List _objects; + SnapshotMergedDominator _parent; + List _children; + + SnapshotClass get klass => _klass; + String get description => _description; + int get shallowSize => internalSize + externalSize; + int get internalSize => _internalSize; + int get externalSize => _externalSize; + int get retainedSize => _retainedSize; + int get instanceCount => _objects.length; + SnapshotMergedDominator get parent => _parent; + Iterable get children => _children; + Iterable get objects => _objects; +} + /// A class in a heap snapshot. abstract class SnapshotClass { String get name; @@ -533,7 +589,9 @@ abstract class SnapshotGraph { Iterable get objects; SnapshotObject get root; + SnapshotObject get extendedRoot; SnapshotMergedDominator get mergedRoot; + SnapshotMergedDominator get extendedMergedRoot; // TODO: Insist that the client remember the chunks if needed? Always keeping // this increasing the peak memory usage during analysis. @@ -551,7 +609,7 @@ const _tagLength = 7; const _tagName = 8; const _kSentinelName = ""; -const _kRootName = "Root"; +const _kRootName = "Live Objects + External"; const _kUnknownFieldName = ""; class _SnapshotGraph implements SnapshotGraph { @@ -571,6 +629,97 @@ class _SnapshotGraph implements SnapshotGraph { SnapshotMergedDominator get mergedRoot => _SnapshotMergedDominator._new(_ROOT, this, null); + SnapshotObject _extendedRoot; + SnapshotObject get extendedRoot { + if (_extendedRoot == null) { + _createExtended(); + } + return _extendedRoot; + } + + SnapshotMergedDominator _extendedMergedRoot; + SnapshotMergedDominator get extendedMergedRoot { + if (_extendedMergedRoot == null) { + _createExtended(); + } + return _extendedMergedRoot; + } + + void _createExtended() { + var capacity = new _SyntheticSnapshotObject(); + var uncollected = new _SyntheticSnapshotObject(); + var fragmentation = new _SyntheticSnapshotObject(); + var live = root; + var mcapacity = new _SyntheticSnapshotMergedDominator(); + var muncollected = new _SyntheticSnapshotMergedDominator(); + var mfragmentation = new _SyntheticSnapshotMergedDominator(); + var mlive = mergedRoot; + + capacity._description = "Capacity + External"; + capacity._klass = live.klass; + capacity._internalSize = _capacity; + capacity._externalSize = _totalExternalSize; + capacity._retainedSize = capacity._internalSize + capacity._externalSize; + capacity._successors = [live, uncollected, fragmentation]; + capacity._predecessors = []; + capacity._children = [live, uncollected, fragmentation]; + + mcapacity._description = "Capacity + External"; + mcapacity._klass = mlive.klass; + mcapacity._internalSize = _capacity; + mcapacity._externalSize = _totalExternalSize; + mcapacity._retainedSize = mcapacity._internalSize + mcapacity._externalSize; + mcapacity._children = [ + mlive, + muncollected, + mfragmentation + ]; + mcapacity._objects = [capacity]; + + uncollected._description = "Uncollected Garbage"; + uncollected._klass = live.klass; + uncollected._internalSize = _totalInternalSize - _liveInternalSize; + uncollected._externalSize = _totalExternalSize - _liveExternalSize; + uncollected._retainedSize = + uncollected._internalSize + uncollected._externalSize; + uncollected._successors = []; + uncollected._predecessors = [capacity]; + uncollected._parent = capacity; + uncollected._children = []; + + muncollected._description = "Uncollected Garbage"; + muncollected._klass = mlive.klass; + muncollected._internalSize = _totalInternalSize - _liveInternalSize; + muncollected._externalSize = _totalExternalSize - _liveExternalSize; + muncollected._retainedSize = + muncollected._internalSize + muncollected._externalSize; + muncollected._parent = mcapacity; + muncollected._children = []; + muncollected._objects = [uncollected]; + + fragmentation._description = "Free"; + fragmentation._klass = live.klass; + fragmentation._internalSize = _capacity - _totalInternalSize; + fragmentation._externalSize = 0; + fragmentation._retainedSize = fragmentation._internalSize; + fragmentation._successors = []; + fragmentation._predecessors = [capacity]; + fragmentation._parent = capacity; + fragmentation._children = []; + + mfragmentation._description = "Free"; + mfragmentation._klass = mlive.klass; + mfragmentation._internalSize = _capacity - _totalInternalSize; + mfragmentation._externalSize = 0; + mfragmentation._retainedSize = mfragmentation._internalSize; + mfragmentation._parent = mcapacity; + mfragmentation._children = []; + mfragmentation._objects = [fragmentation]; + + _extendedRoot = capacity; + _extendedMergedRoot = mcapacity; + } + Iterable get objects sync* { final N = _N; for (var id = 1; id <= N; id++) { diff --git a/runtime/observatory/lib/src/elements/heap_snapshot.dart b/runtime/observatory/lib/src/elements/heap_snapshot.dart index 9a1841af3d3..5cb36e8242e 100644 --- a/runtime/observatory/lib/src/elements/heap_snapshot.dart +++ b/runtime/observatory/lib/src/elements/heap_snapshot.dart @@ -797,7 +797,7 @@ class HeapSnapshotElement extends CustomElement implements Renderable { switch (_mode) { case HeapSnapshotTreeMode.dominatorTree: if (selection == null) { - selection = List.from(_snapshotA.root.objects); + selection = List.from(_snapshotA.extendedRoot.objects); } _tree = new VirtualTreeElement( _createDominator, _updateDominator, _getChildrenDominator, @@ -820,16 +820,16 @@ class HeapSnapshotElement extends CustomElement implements Renderable { break; case HeapSnapshotTreeMode.dominatorTreeMap: if (selection == null) { - selection = List.from(_snapshotA.root.objects); + selection = List.from(_snapshotA.extendedRoot.objects); } _createTreeMap(report, new DominatorTreeMap(this), selection.first); break; case HeapSnapshotTreeMode.mergedDominatorTree: _tree = new VirtualTreeElement(_createMergedDominator, _updateMergedDominator, _getChildrenMergedDominator, - items: _getChildrenMergedDominator(_snapshotA.mergedRoot), + items: _getChildrenMergedDominator(_snapshotA.extendedMergedRoot), queue: _r.queue); - _tree.expand(_snapshotA.mergedRoot); + _tree.expand(_snapshotA.extendedMergedRoot); final text = 'A heap dominator tree, where siblings with the same class' ' have been merged into a single node.'; report.addAll([ @@ -857,7 +857,7 @@ class HeapSnapshotElement extends CustomElement implements Renderable { break; case HeapSnapshotTreeMode.mergedDominatorTreeMap: if (mergedSelection == null) { - mergedSelection = _snapshotA.mergedRoot; + mergedSelection = _snapshotA.extendedMergedRoot; } _createTreeMap( report, new MergedDominatorTreeMap(this), mergedSelection); @@ -1489,7 +1489,9 @@ class HeapSnapshotElement extends CustomElement implements Renderable { String snapshotToString(snapshot) { if (snapshot == null) return "None"; - return snapshot.description + " " + Utils.formatSize(snapshot.size); + return snapshot.description + + " " + + Utils.formatSize(snapshot.capacity + snapshot.externalSize); } List _createSnapshotSelectA() {