[observatory] Include garbage and fragmentation in the heap snapshot view.

A few people have expressed confusion about why the top-level size shown in the heap snapshot view does not correspond to the total in the allocation table view. The missing size is now shown.

Change-Id: Idc7e53cb6098263f9809d4bb7c3154a97c4ba3e8
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/152408
Commit-Queue: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Ben Konyi <bkonyi@google.com>
This commit is contained in:
Ryan Macnak
2020-06-25 21:29:12 +00:00
committed by commit-bot@chromium.org
parent ffa7969bb8
commit 9700d7ff63
2 changed files with 159 additions and 8 deletions
+151 -2
View File
@@ -333,6 +333,36 @@ class _SnapshotObject implements SnapshotObject {
}
}
class _SyntheticSnapshotObject implements SnapshotObject {
String _description;
SnapshotClass _klass;
int _internalSize;
int _externalSize;
int _retainedSize;
List<SnapshotObject> _successors;
List<SnapshotObject> _predecessors;
SnapshotObject _parent;
List<SnapshotObject> _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<SnapshotObject> get successors => _successors;
Iterable<SnapshotObject> get predecessors => _predecessors;
SnapshotObject get parent => _parent;
Iterable<SnapshotObject> get children => _children;
Iterable<SnapshotObject> 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<SnapshotObject> _objects;
SnapshotMergedDominator _parent;
List<SnapshotMergedDominator> _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<SnapshotMergedDominator> get children => _children;
Iterable<SnapshotObject> get objects => _objects;
}
/// A class in a heap snapshot.
abstract class SnapshotClass {
String get name;
@@ -533,7 +589,9 @@ abstract class SnapshotGraph {
Iterable<SnapshotObject> 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 = "<omitted-object>";
const _kRootName = "Root";
const _kRootName = "Live Objects + External";
const _kUnknownFieldName = "<unknown>";
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 = <SnapshotObject>[live, uncollected, fragmentation];
capacity._predecessors = <SnapshotObject>[];
capacity._children = <SnapshotObject>[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 = <SnapshotMergedDominator>[
mlive,
muncollected,
mfragmentation
];
mcapacity._objects = <SnapshotObject>[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 = <SnapshotObject>[];
uncollected._predecessors = <SnapshotObject>[capacity];
uncollected._parent = capacity;
uncollected._children = <SnapshotObject>[];
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 = <SnapshotMergedDominator>[];
muncollected._objects = <SnapshotObject>[uncollected];
fragmentation._description = "Free";
fragmentation._klass = live.klass;
fragmentation._internalSize = _capacity - _totalInternalSize;
fragmentation._externalSize = 0;
fragmentation._retainedSize = fragmentation._internalSize;
fragmentation._successors = <SnapshotObject>[];
fragmentation._predecessors = <SnapshotObject>[capacity];
fragmentation._parent = capacity;
fragmentation._children = <SnapshotObject>[];
mfragmentation._description = "Free";
mfragmentation._klass = mlive.klass;
mfragmentation._internalSize = _capacity - _totalInternalSize;
mfragmentation._externalSize = 0;
mfragmentation._retainedSize = mfragmentation._internalSize;
mfragmentation._parent = mcapacity;
mfragmentation._children = <SnapshotMergedDominator>[];
mfragmentation._objects = <SnapshotObject>[fragmentation];
_extendedRoot = capacity;
_extendedMergedRoot = mcapacity;
}
Iterable<SnapshotObject> get objects sync* {
final N = _N;
for (var id = 1; id <= N; id++) {
@@ -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<Element> _createSnapshotSelectA() {