Index: runtime/observatory/lib/src/service/object.dart |
diff --git a/runtime/observatory/lib/src/service/object.dart b/runtime/observatory/lib/src/service/object.dart |
index 4a006d2e11cfd2880bf4157df1aaff0eaee2dedd..4603ece0e3d010d5315be9e9c15c4f340122a114 100644 |
--- a/runtime/observatory/lib/src/service/object.dart |
+++ b/runtime/observatory/lib/src/service/object.dart |
@@ -347,11 +347,13 @@ abstract class VM extends ServiceObjectOwner { |
@observable String version = 'unknown'; |
@observable String targetCPU; |
@observable int architectureBits; |
- @observable double uptime = 0.0; |
@observable bool assertsEnabled = false; |
@observable bool typeChecksEnabled = false; |
@observable String pid = ''; |
- @observable DateTime lastUpdate; |
+ @observable DateTime startTime; |
+ @observable DateTime refreshTime; |
+ @observable Duration get upTime => |
+ (new DateTime.now().difference(startTime)); |
VM() : super._empty(null) { |
name = 'vm'; |
@@ -367,19 +369,6 @@ abstract class VM extends ServiceObjectOwner { |
final StreamController<ServiceEvent> events = |
new StreamController.broadcast(); |
- bool _isIsolateLifecycleEvent(String eventType) { |
- return _isIsolateExitEvent(eventType) || |
- _isIsolateStartEvent(eventType); |
- } |
- |
- bool _isIsolateExitEvent(String eventType) { |
- return (eventType == ServiceEvent.kIsolateExit); |
- } |
- |
- bool _isIsolateStartEvent(String eventType) { |
- return (eventType == ServiceEvent.kIsolateStart); |
- } |
- |
void postServiceEvent(String response, ByteData data) { |
var map; |
try { |
@@ -398,98 +387,42 @@ abstract class VM extends ServiceObjectOwner { |
return; |
} |
- var eventType = map['eventType']; |
- |
- if (_isIsolateLifecycleEvent(eventType)) { |
- String isolateId = map['isolate']['id']; |
- var event; |
- if (_isIsolateStartEvent(eventType)) { |
- _onIsolateStart(map['isolate']); |
- // By constructing the event *after* adding the isolate to the |
- // isolate cache, the call to getFromMap will use the cached Isolate. |
- event = new ServiceObject._fromMap(this, map); |
- } else { |
- assert(_isIsolateExitEvent(eventType)); |
- // By constructing the event *before* removing the isolate from the |
- // isolate cache, the call to getFromMap will use the cached Isolate. |
- event = new ServiceObject._fromMap(this, map); |
- _onIsolateExit(isolateId); |
+ var eventIsolate = map['isolate']; |
+ if (eventIsolate == null) { |
+ var event = new ServiceObject._fromMap(vm, map); |
+ events.add(event); |
+ } else { |
+ // getFromMap creates the Isolate if it hasn't been seen already. |
+ var isolate = getFromMap(map['isolate']); |
+ var event = new ServiceObject._fromMap(isolate, map); |
+ if (event.eventType == ServiceEvent.kIsolateExit) { |
+ _removeIsolate(isolate.id); |
} |
- assert(event != null); |
+ isolate._onEvent(event); |
events.add(event); |
- return; |
} |
- |
- // Extract the owning isolate from the event itself. |
- String owningIsolateId = map['isolate']['id']; |
- getIsolate(owningIsolateId).then((owningIsolate) { |
- if (owningIsolate == null) { |
- // TODO(koda): Do we care about GC events in VM isolate? |
- Logger.root.severe('Ignoring event with unknown isolate id: ' |
- '$owningIsolateId'); |
- return; |
- } |
- var event = new ServiceObject._fromMap(owningIsolate, map); |
- owningIsolate._onEvent(event); |
- events.add(event); |
- }); |
} |
- Isolate _onIsolateStart(Map isolateMap) { |
- var isolateId = isolateMap['id']; |
- assert(!_isolateCache.containsKey(isolateId)); |
- Isolate isolate = new ServiceObject._fromMap(this, isolateMap); |
- _isolateCache[isolateId] = isolate; |
- notifyPropertyChange(#isolates, true, false); |
- // Eagerly load the isolate. |
- isolate.load().catchError((e) { |
- Logger.root.info('Eagerly loading an isolate failed: $e'); |
- }); |
- return isolate; |
- } |
- |
- void _onIsolateExit(String isolateId) { |
+ void _removeIsolate(String isolateId) { |
assert(_isolateCache.containsKey(isolateId)); |
_isolateCache.remove(isolateId); |
notifyPropertyChange(#isolates, true, false); |
} |
- void _updateIsolatesFromList(List isolateList) { |
- var shutdownIsolates = <String>[]; |
- var createdIsolates = <Map>[]; |
- var isolateStillExists = <String, bool>{}; |
- |
- // Start with the assumption that all isolates are gone. |
- for (var isolateId in _isolateCache.keys) { |
- isolateStillExists[isolateId] = false; |
- } |
- |
- // Find created isolates and mark existing isolates as living. |
- for (var isolateMap in isolateList) { |
- var isolateId = isolateMap['id']; |
- if (!_isolateCache.containsKey(isolateId)) { |
- createdIsolates.add(isolateMap); |
- } else { |
- isolateStillExists[isolateId] = true; |
- } |
- } |
+ void _removeDeadIsolates(List newIsolates) { |
+ // Build a set of new isolates. |
+ var newIsolateSet = new Set(); |
+ newIsolates.forEach((iso) => newIsolateSet.add(iso.id)); |
- // Find shutdown isolates. |
- isolateStillExists.forEach((isolateId, exists) { |
- if (!exists) { |
- shutdownIsolates.add(isolateId); |
+ // Remove any old isolates which no longer exist. |
+ List toRemove = []; |
+ _isolateCache.forEach((id, _) { |
+ if (!newIsolateSet.contains(id)) { |
+ toRemove.add(id); |
} |
}); |
- |
- // Process shutdown. |
- for (var isolateId in shutdownIsolates) { |
- _onIsolateExit(isolateId); |
- } |
- |
- // Process creation. |
- for (var isolateMap in createdIsolates) { |
- _onIsolateStart(isolateMap); |
- } |
+ toRemove.forEach((id) => _removeIsolate(id)); |
+ notifyPropertyChange(#isolates, true, false); |
} |
static final String _isolateIdPrefix = 'isolates/'; |
@@ -507,11 +440,16 @@ abstract class VM extends ServiceObjectOwner { |
// Check cache. |
var isolate = _isolateCache[id]; |
if (isolate == null) { |
- // We should never see an unknown isolate here. |
- throw new UnimplementedError(); |
- } |
- var mapIsRef = _hasRef(map['type']); |
- if (!mapIsRef) { |
+ // Add new isolate to the cache. |
+ isolate = new ServiceObject._fromMap(this, map); |
+ _isolateCache[id] = isolate; |
+ notifyPropertyChange(#isolates, true, false); |
+ |
+ // Eagerly load the isolate. |
+ isolate.load().catchError((e, stack) { |
+ Logger.root.info('Eagerly loading an isolate failed: $e\n$stack'); |
+ }); |
+ } else { |
isolate.update(map); |
} |
return isolate; |
@@ -624,17 +562,23 @@ abstract class VM extends ServiceObjectOwner { |
if (mapIsRef) { |
return; |
} |
+ // Note that upgrading the collection creates any isolates in the |
+ // isolate list which are new. |
+ _upgradeCollection(map, vm); |
+ |
_loaded = true; |
version = map['version']; |
targetCPU = map['targetCPU']; |
architectureBits = map['architectureBits']; |
- uptime = map['uptime']; |
- var dateInMillis = int.parse(map['date']); |
- lastUpdate = new DateTime.fromMillisecondsSinceEpoch(dateInMillis); |
+ var startTimeMillis = map['startTime']; |
+ startTime = new DateTime.fromMillisecondsSinceEpoch(startTimeMillis); |
+ var refreshTimeMillis = map['refreshTime']; |
+ refreshTime = new DateTime.fromMillisecondsSinceEpoch(refreshTimeMillis); |
+ notifyPropertyChange(#upTime, 0, 1); |
assertsEnabled = map['assertsEnabled']; |
pid = map['pid']; |
typeChecksEnabled = map['typeChecksEnabled']; |
- _updateIsolatesFromList(map['isolates']); |
+ _removeDeadIsolates(map['isolates']); |
} |
// Reload all isolates. |
@@ -780,9 +724,12 @@ class HeapSnapshot { |
class Isolate extends ServiceObjectOwner with Coverage { |
@reflectable VM get vm => owner; |
@reflectable Isolate get isolate => this; |
- @observable ObservableMap counters = new ObservableMap(); |
+ @observable int number; |
+ @observable DateTime startTime; |
+ @observable Duration get upTime => |
+ (new DateTime.now().difference(startTime)); |
- @observable ServiceEvent pauseEvent = null; |
+ @observable ObservableMap counters = new ObservableMap(); |
void _updateRunState() { |
topFrame = (pauseEvent != null ? pauseEvent.topFrame : null); |
@@ -796,6 +743,7 @@ class Isolate extends ServiceObjectOwner with Coverage { |
notifyPropertyChange(#idle, 0, 1); |
} |
+ @observable ServiceEvent pauseEvent = null; |
@observable bool paused = false; |
@observable bool running = false; |
@observable bool idle = false; |
@@ -863,13 +811,15 @@ class Isolate extends ServiceObjectOwner with Coverage { |
if (map == null) { |
return null; |
} |
+ var mapType = _stripRef(map['type']); |
+ if (mapType == 'Isolate') { |
+ // There are sometimes isolate refs in ServiceEvents. |
+ return vm.getFromMap(map); |
+ } |
String mapId = map['id']; |
var obj = (mapId != null) ? _cache[mapId] : null; |
if (obj != null) { |
- var mapIsRef = _hasRef(map['type']); |
- if (!mapIsRef) { |
- obj.update(map); |
- } |
+ obj.update(map); |
return obj; |
} |
// Build the object from the map directly. |
@@ -917,7 +867,6 @@ class Isolate extends ServiceObjectOwner with Coverage { |
@observable String name; |
@observable String vmName; |
- @observable String mainPort; |
@observable ServiceFunction entry; |
@observable final Map<String, double> timers = |
@@ -953,9 +902,9 @@ class Isolate extends ServiceObjectOwner with Coverage { |
} |
void _update(ObservableMap map, bool mapIsRef) { |
- mainPort = map['mainPort']; |
name = map['name']; |
vmName = map['name']; |
+ number = int.parse(map['number'], onError:(_) => null); |
if (mapIsRef) { |
return; |
} |
@@ -973,7 +922,9 @@ class Isolate extends ServiceObjectOwner with Coverage { |
if (map['entry'] != null) { |
entry = map['entry']; |
} |
- |
+ var startTimeInMillis = map['startTime']; |
+ startTime = new DateTime.fromMillisecondsSinceEpoch(startTimeInMillis); |
+ notifyPropertyChange(#upTime, 0, 1); |
var countersMap = map['tagCounters']; |
if (countersMap != null) { |
var names = countersMap['names']; |
@@ -1041,21 +992,21 @@ class Isolate extends ServiceObjectOwner with Coverage { |
ObservableMap<int, Breakpoint> breakpoints = new ObservableMap(); |
void _updateBreakpoints(List newBpts) { |
- // Build a map of new breakpoints. |
- var newBptMap = {}; |
- newBpts.forEach((bpt) => (newBptMap[bpt.number] = bpt)); |
+ // Build a set of new breakpoints. |
+ var newBptSet = new Set(); |
+ newBpts.forEach((bpt) => newBptSet.add(bpt.number)); |
// Remove any old breakpoints which no longer exist. |
List toRemove = []; |
breakpoints.forEach((key, _) { |
- if (!newBptMap.containsKey(key)) { |
+ if (!newBptSet.contains(key)) { |
toRemove.add(key); |
} |
}); |
toRemove.forEach((key) => breakpoints.remove(key)); |
// Add all new breakpoints. |
- breakpoints.addAll(newBptMap); |
+ newBpts.forEach((bpt) => (breakpoints[bpt.number] = bpt)); |
} |
void _addBreakpoint(Breakpoint bpt) { |
@@ -1068,13 +1019,17 @@ class Isolate extends ServiceObjectOwner with Coverage { |
} |
void _onEvent(ServiceEvent event) { |
- assert(event.eventType != ServiceEvent.kIsolateStart && |
- event.eventType != ServiceEvent.kIsolateExit); |
switch(event.eventType) { |
+ case ServiceEvent.kIsolateStart: |
+ case ServiceEvent.kIsolateExit: |
+ // Handled elsewhere. |
+ break; |
+ |
case ServiceEvent.kBreakpointAdded: |
_addBreakpoint(event.breakpoint); |
break; |
+ case ServiceEvent.kIsolateUpdate: |
case ServiceEvent.kBreakpointResolved: |
// Update occurs as side-effect of caching. |
break; |
@@ -1145,6 +1100,7 @@ class Isolate extends ServiceObjectOwner with Coverage { |
// TODO(turnidge): Handle this more gracefully. |
Logger.root.severe(result.message); |
} |
+ return result; |
}); |
} |
@@ -1154,6 +1110,7 @@ class Isolate extends ServiceObjectOwner with Coverage { |
// TODO(turnidge): Handle this more gracefully. |
Logger.root.severe(result.message); |
} |
+ return result; |
}); |
} |
@@ -1163,6 +1120,7 @@ class Isolate extends ServiceObjectOwner with Coverage { |
// TODO(turnidge): Handle this more gracefully. |
Logger.root.severe(result.message); |
} |
+ return result; |
}); |
} |
@@ -1172,6 +1130,7 @@ class Isolate extends ServiceObjectOwner with Coverage { |
// TODO(turnidge): Handle this more gracefully. |
Logger.root.severe(result.message); |
} |
+ return result; |
}); |
} |
@@ -1181,9 +1140,17 @@ class Isolate extends ServiceObjectOwner with Coverage { |
// TODO(turnidge): Handle this more gracefully. |
Logger.root.severe(result.message); |
} |
+ return result; |
}); |
} |
+ Future setName(String newName) { |
+ Map params = { |
+ 'name': newName, |
+ }; |
+ return invokeRpc('setName', params); |
+ } |
+ |
Future<ServiceMap> getStack() { |
return invokeRpc('getStack', {}).then((result) { |
if (result is DartError) { |
@@ -1426,6 +1393,7 @@ class ServiceEvent extends ServiceObject { |
/// The possible 'eventType' values. |
static const kIsolateStart = 'IsolateStart'; |
static const kIsolateExit = 'IsolateExit'; |
+ static const kIsolateUpdate = 'IsolateUpdate'; |
static const kPauseStart = 'PauseStart'; |
static const kPauseExit = 'PauseExit'; |
static const kPauseBreakpoint = 'PauseBreakpoint'; |
@@ -1455,6 +1423,7 @@ class ServiceEvent extends ServiceObject { |
void _update(ObservableMap map, bool mapIsRef) { |
_loaded = true; |
_upgradeCollection(map, owner); |
+ assert(map['isolate'] == null || owner == map['isolate']); |
eventType = map['eventType']; |
name = 'ServiceEvent $eventType'; |
vmName = name; |
@@ -1868,7 +1837,7 @@ class FunctionKind { |
case 'Stub': return kStub; |
case 'Tag': return kTag; |
} |
- print('did not understand $value'); |
+ Logger.root.severe('Unrecognized function kind: $value'); |
throw new FallThroughError(); |
} |
@@ -2494,7 +2463,7 @@ class CodeKind { |
} else if (s == 'Stub') { |
return Stub; |
} |
- print('do not understand code kind $s'); |
+ Logger.root.severe('Unrecognized code kind: $s'); |
throw new FallThroughError(); |
} |
static const Collected = const CodeKind._internal('Collected'); |