Index: runtime/observatory/lib/object_graph.dart |
diff --git a/runtime/observatory/lib/object_graph.dart b/runtime/observatory/lib/object_graph.dart |
index d1c171bfe017a6ceb8118ba67054e412d412b80c..146badcaf156795c762bb4be3c15e20747ae015c 100644 |
--- a/runtime/observatory/lib/object_graph.dart |
+++ b/runtime/observatory/lib/object_graph.dart |
@@ -8,6 +8,8 @@ import 'dart:async'; |
import 'dart:collection'; |
import 'dart:typed_data'; |
+import 'package:logging/logging.dart'; |
+ |
class _JenkinsSmiHash { |
static int combine(int hash, int value) { |
hash = 0x1fffffff & (hash + value); |
@@ -227,6 +229,7 @@ class ObjectVertex { |
ObjectVertex._(this._id, this._graph); |
bool get isRoot => ROOT == _id; |
+ bool get isStack => vmCid == _graph._kStackCid; |
bool operator ==(other) => _id == other._id && _graph == other._graph; |
int get hashCode => _id; |
@@ -300,6 +303,7 @@ class MergedObjectVertex { |
MergedObjectVertex._(this._id, this._graph); |
bool get isRoot => ROOT == _id; |
+ bool get isStack => vmCid == _graph._kStackCid; |
bool operator ==(other) => _id == other._id && _graph == other._graph; |
int get hashCode => _id; |
@@ -497,8 +501,10 @@ class ObjectGraph { |
List<ByteData> _chunks; |
int _kObjectAlignment; |
- int _N; |
- int _E; |
+ int _kStackCid; |
+ int _N; // Objects in the snapshot. |
+ int _Nconnected; // Objects reachable from root. |
+ int _E; // References in the snapshot. |
int _size; |
// Indexed by node id, with id 0 representing invalid/uninitialized. |
@@ -537,6 +543,8 @@ class ObjectGraph { |
var stream = new ReadStream(_chunks); |
stream.readUnsigned(); |
_kObjectAlignment = stream.clampedUint32; |
+ stream.readUnsigned(); |
+ _kStackCid = stream.clampedUint32; |
var id = ROOT; |
while (stream.pendingBytes > 0) { |
@@ -577,7 +585,8 @@ class ObjectGraph { |
var succs = new Uint32List(E); |
var stream = new ReadStream(_chunks); |
- stream.skipUnsigned(); // addr alignment |
+ stream.skipUnsigned(); // kObjectAlignment |
+ stream.skipUnsigned(); // kStackCid |
var id = 1, edge = 0; |
while (stream.pendingBytes > 0) { |
@@ -660,15 +669,40 @@ class ObjectGraph { |
} |
} |
- assert(dfsNumber == N); |
- for (var i = ROOT; i <= N; i++) { |
- assert(semi[i] != SENTINEL); |
+ if (dfsNumber != N) { |
+ // This may happen in filtered snapshots. |
+ Logger.root.warning('Heap snapshot contains unreachable nodes.'); |
} |
- assert(parent[ROOT] == SENTINEL); |
- for (var i = ROOT + 1; i <= N; i++) { |
- assert(parent[i] != SENTINEL); |
+ |
+ assert(() { |
+ for (var i = 1; i <= dfsNumber; i++) { |
+ var v = vertex[i]; |
+ assert(semi[v] != SENTINEL); |
+ } |
+ assert(parent[1] == SENTINEL); |
+ for (var i = 2; i <= dfsNumber; i++) { |
+ var v = vertex[i]; |
+ assert(parent[v] != SENTINEL); |
+ } |
+ return true; |
+ }); |
+ |
+ if (dfsNumber != N) { |
+ // Remove successors of unconnected nodes |
+ for (var i = ROOT + 1; i <= N; i++) { |
+ if (parent[i] == SENTINEL) { |
+ var startSuccIndex = firstSuccs[i]; |
+ var limitSuccIndex = firstSuccs[i + 1]; |
+ for (var succIndex = startSuccIndex; |
+ succIndex < limitSuccIndex; |
+ succIndex++) { |
+ succs[succIndex] = SENTINEL; |
+ } |
+ } |
+ } |
} |
+ _Nconnected = dfsNumber; |
_vertex = vertex; |
_semi = semi; |
_parent = parent; |
@@ -676,6 +710,7 @@ class ObjectGraph { |
void _buildPredecessors() { |
var N = _N; |
+ var Nconnected = _Nconnected; |
var E = _E; |
var firstSuccs = _firstSuccs; |
var succs = _succs; |
@@ -691,7 +726,9 @@ class ObjectGraph { |
// Count predecessors of each node. |
for (var succIndex = 0; succIndex < E; succIndex++) { |
var succId = succs[succIndex]; |
- numPreds[succId]++; |
+ if (succId != SENTINEL) { |
+ numPreds[succId]++; |
+ } |
} |
// Assign indices into predecessors array. |
@@ -704,8 +741,10 @@ class ObjectGraph { |
firstPreds[i] = thisPredIndex; |
nextPreds[i] = thisPredIndex; |
} |
- assert(predIndex == E); |
- firstPreds[N + 1] = E; // Extra entry for cheap boundary detection. |
+ if (N == Nconnected) { |
+ assert(predIndex == E); |
+ } |
+ firstPreds[N + 1] = predIndex; // Extra entry for cheap boundary detection. |
// Fill predecessors array. |
for (var i = 1; i <= N; i++) { |
@@ -715,8 +754,10 @@ class ObjectGraph { |
succIndex < limitSuccIndex; |
succIndex++) { |
var succId = succs[succIndex]; |
- var predIndex = nextPreds[succId]++; |
- preds[predIndex] = i; |
+ if (succId != SENTINEL) { |
+ var predIndex = nextPreds[succId]++; |
+ preds[predIndex] = i; |
+ } |
} |
} |
@@ -802,6 +843,7 @@ class ObjectGraph { |
// in a Flowgraph." |
void _buildDominators() { |
var N = _N; |
+ var Nconnected = _Nconnected; |
var vertex = _vertex; |
var semi = _semi; |
@@ -825,7 +867,7 @@ class ObjectGraph { |
var stackNode = new Uint32List(N + 1); |
var stackState = new Uint8List(N + 1); |
- for (var i = N; i > 1; i--) { |
+ for (var i = Nconnected; i > 1; i--) { |
var w = vertex[i]; |
assert(w != ROOT); |
@@ -864,7 +906,7 @@ class ObjectGraph { |
assert(buckets[i] == null); |
} |
// Lengauer & Tarjan Step 4. |
- for (var i = 2; i <= N; i++) { |
+ for (var i = 2; i <= Nconnected; i++) { |
var w = vertex[i]; |
if (dom[w] != vertex[semi[w]]) { |
dom[w] = dom[dom[w]]; |
@@ -876,6 +918,7 @@ class ObjectGraph { |
void _calculateRetainedSizes() { |
var N = _N; |
+ var Nconnected = _Nconnected; |
var size = 0; |
var shallowSizes = _shallowSizes; |
@@ -883,8 +926,9 @@ class ObjectGraph { |
var doms = _doms; |
// Sum shallow sizes. |
- for (var i = ROOT; i < N; i++) { |
- size += shallowSizes[i]; |
+ for (var i = 1; i <= Nconnected; i++) { |
+ var v = vertex[i]; |
+ size += shallowSizes[v]; |
} |
// Start with retained size as shallow size. |
@@ -892,12 +936,14 @@ class ObjectGraph { |
// In post order (bottom up), add retained size to dominator's retained |
// size, skipping root. |
- for (var i = N; i > 1; i--) { |
+ for (var i = Nconnected; i > 1; i--) { |
var v = vertex[i]; |
assert(v != ROOT); |
- retainedSizes[doms[i]] += retainedSizes[i]; |
+ retainedSizes[doms[v]] += retainedSizes[v]; |
} |
+ assert(retainedSizes[ROOT] == size); // Root retains everything. |
+ |
_retainedSizes = retainedSizes; |
_size = size; |
} |