Chromium Code Reviews| Index: runtime/bin/vmservice/client/lib/src/service/object.dart |
| diff --git a/runtime/bin/vmservice/client/lib/src/service/object.dart b/runtime/bin/vmservice/client/lib/src/service/object.dart |
| index 0a2129f692a9521410be796be8fdf5f8492db8c6..bda1de23f920699bf441ac9c01e99db5bb6afe06 100644 |
| --- a/runtime/bin/vmservice/client/lib/src/service/object.dart |
| +++ b/runtime/bin/vmservice/client/lib/src/service/object.dart |
| @@ -102,10 +102,6 @@ abstract class ServiceObject extends Observable { |
| ServiceObject update(ObservableMap m) { |
| // Assert that m is a service map. |
| assert(ServiceObject.isServiceMap(m)); |
| - if ((m['type'] == 'Error') && (_serviceType != 'Error')) { |
| - // Got an unexpected error. Don't update the object. |
| - return _upgradeToServiceObject(vm, isolate, m); |
| - } |
| // TODO(johnmccutchan): Should we allow for a ServiceObject's id |
| // or type to change? |
| _id = m['id']; |
| @@ -118,9 +114,6 @@ abstract class ServiceObject extends Observable { |
| void _update(ObservableMap map); |
| void _created() { |
| - var refNotice = _ref ? ' Created from reference.' : ''; |
| - Logger.root.info('Created ServiceObject for \'${_id}\' with type ' |
| - '\'${_serviceType}\'.' + refNotice); |
| } |
|
turnidge
2014/03/24 20:22:13
Maybe just drop _created() entirely.
Cutch
2014/03/25 14:39:06
Done.
|
| // ------------------------------------------------------ |
| @@ -171,6 +164,11 @@ abstract class VM extends ServiceObject { |
| _initOnce(); |
| } |
| + final StreamController<ServiceException> exceptions = |
| + new StreamController.broadcast(); |
| + final StreamController<ServiceError> errors = |
| + new StreamController.broadcast(); |
| + |
| static final RegExp _currentIsolateMatcher = new RegExp(r'isolates/\d+'); |
| static final RegExp _currentObjectMatcher = new RegExp(r'isolates/\d+(/|$)'); |
| @@ -222,30 +220,53 @@ abstract class VM extends ServiceObject { |
| } |
| } |
| - /// Gets [id] as an [ObservableMap] from the service directly. |
| + /// Gets [id] as an [ObservableMap] from the service directly. If |
| + /// an error occurs, the future is completed as an error with a |
| + /// ServiceError or ServiceException. Therefore the chained then() |
| + /// execution path will always receive a map encoding a ServiceObject. |
| Future<ObservableMap> getAsMap(String id) { |
| return getString(id).then((response) { |
| try { |
| - var map = JSON.decode(response); |
| - Logger.root.info('Decoded $id'); |
| - Logger.root.info('Response $response'); |
| - return toObservable(map); |
| + var map = toObservable(JSON.decode(response)); |
| + if (!ServiceObject.isServiceMap(map)) { |
| + return new Future.error( |
| + new ServiceException.fromMap(this, toObservable({ |
| + 'type': 'ServiceException', |
| + 'id': '', |
| + 'kind': 'FormatException', |
| + 'response': map, |
| + 'message': 'Top level service responses must be service maps.', |
| + }))); |
| + } |
| + // Preemptively capture ServiceError and ServiceExceptions. |
| + if (map['type'] == 'ServiceError') { |
| + return new Future.error(new ServiceError.fromMap(this, map)); |
| + } else if (map['type'] == 'ServiceException') { |
| + return new Future.error(new ServiceException.fromMap(this, map)); |
| + } |
| + // map is now guaranteed to be a non-error/exception ServiceObject. |
| + return map; |
| } catch (e, st) { |
| - return toObservable({ |
| - 'type': 'Error', |
| + print(e); |
| + print(st); |
| + return new Future.error( |
| + new ServiceException.fromMap(this, toObservable({ |
| + 'type': 'ServiceException', |
| 'id': '', |
| - 'kind': 'DecodeError', |
| - 'message': '$e', |
| - }); |
| + 'kind': 'DecodeException', |
| + 'response': response, |
| + 'message': 'Could not decode JSON: $e', |
| + }))); |
| } |
| }).catchError((error) { |
| - return toObservable({ |
| - 'type': 'Error', |
| - 'id': '', |
| - 'kind': 'LastResort', |
| - 'message': '$error' |
| - }); |
| - }); |
| + // ServiceError. |
| + errors.add(error); |
| + return new Future.error(error); |
| + }, test: (e) => e is ServiceError).catchError((exception) { |
|
turnidge
2014/03/24 20:22:13
I hadn't seen the "test" stuff before. Wacky!
|
| + // ServiceException. |
| + exceptions.add(exception); |
| + return new Future.error(exception); |
| + }, test: (e) => e is ServiceException); |
| } |
| /// Get [id] as a [String] from the service directly. See [getAsMap]. |
| @@ -262,8 +283,92 @@ abstract class VM extends ServiceObject { |
| } |
| } |
| +class TagProfileSnapshot { |
| + final double seconds; |
| + final List<int> counters; |
| + int get sum => _sum; |
| + int _sum = 0; |
| + TagProfileSnapshot(this.seconds, int countersLength) |
| + : counters = new List<int>(countersLength); |
| + |
| + /// Set [counters] and update [sum]. |
| + void set(List<int> counters) { |
| + this.counters.setAll(0, counters); |
| + for (var i = 0; i < this.counters.length; i++) { |
| + _sum += this.counters[i]; |
| + } |
| + } |
| + |
| + /// Set [counters] with the delta from [counters] to [old_counters] |
| + /// and update [sum]. |
| + void delta(List<int> counters, List<int> old_counters) { |
| + for (var i = 0; i < this.counters.length; i++) { |
| + this.counters[i] = counters[i] - old_counters[i]; |
| + _sum += this.counters[i]; |
| + } |
| + } |
| + |
| + /// Update [counters] with new maximum values seen in [counters]. |
| + void max(List<int> counters) { |
| + for (var i = 0; i < counters.length; i++) { |
| + var c = counters[i]; |
| + this.counters[i] = this.counters[i] > c ? this.counters[i] : c; |
| + } |
| + } |
| + |
| + /// Zero [counters]. |
| + void zero() { |
| + for (var i = 0; i < counters.length; i++) { |
| + counters[i] = 0; |
| + } |
| + } |
| +} |
| + |
| +class TagProfile { |
| + final List<String> names = new List<String>(); |
| + final List<TagProfileSnapshot> snapshots = new List<TagProfileSnapshot>(); |
| + double get updatedAtSeconds => _seconds; |
| + double _seconds; |
| + TagProfileSnapshot _maxSnapshot; |
| + int _historySize; |
| + int _countersLength = 0; |
| + |
| + TagProfile(this._historySize); |
| + |
| + void _processTagProfile(double seconds, ObservableMap tagProfile) { |
| + _seconds = seconds; |
| + var counters = tagProfile['counters']; |
| + if (names.length == 0) { |
| + // Initialization. |
| + names.addAll(tagProfile['names']); |
| + _countersLength = tagProfile['counters'].length; |
| + for (var i = 0; i < _historySize; i++) { |
| + var snapshot = new TagProfileSnapshot(0.0, _countersLength); |
| + snapshot.zero(); |
| + snapshots.add(snapshot); |
| + } |
| + // The counters monotonically grow, keep track of the maximum value. |
| + _maxSnapshot = new TagProfileSnapshot(0.0, _countersLength); |
| + _maxSnapshot.set(counters); |
| + return; |
| + } |
| + var snapshot = new TagProfileSnapshot(seconds, _countersLength); |
| + // We snapshot the delta from the current counters to the maximum counter |
| + // values. |
| + snapshot.delta(counters, _maxSnapshot.counters); |
| + _maxSnapshot.max(counters); |
| + snapshots.add(snapshot); |
| + // Only keep _historySize snapshots. |
| + if (snapshots.length > _historySize) { |
| + snapshots.removeAt(0); |
| + } |
| + } |
| +} |
|
turnidge
2014/03/24 20:22:13
What do you think of us moving the profilng helper
Cutch
2014/03/25 14:39:06
sgtm.
|
| + |
| /// State for a running isolate. |
| class Isolate extends ServiceObject { |
| + final TagProfile tagProfile = new TagProfile(20); |
| + |
| String get link => _id; |
| String get hashLink => '#/$_id'; |
| @@ -372,6 +477,8 @@ class Isolate extends ServiceObject { |
| @observable String fileAndLine; |
| + @observable DartError stickyError; |
| + |
| void _update(ObservableMap map) { |
| upgradeCollection(map, vm, this); |
| mainPort = map['mainPort']; |
| @@ -420,6 +527,15 @@ class Isolate extends ServiceObject { |
| pausedOnExit = map['pausedOnExit']; |
| running = map['topFrame'] != null; |
| idle = !pausedOnStart && !pausedOnExit && !running; |
| + stickyError = map['stickyError']; |
| + } |
| + |
| + Future<TagProfile> updateTagProfile() { |
| + return vm.getAsMap(relativeLink('profile/tag')).then((ObservableMap m) { |
| + var seconds = new DateTime.now().millisecondsSinceEpoch / 1000.0; |
| + tagProfile._processTagProfile(seconds, m); |
| + return tagProfile; |
| + }); |
| } |
| @reflectable CodeTrieNode profileTrieRoot; |
| @@ -474,7 +590,7 @@ class IsolateList { |
| final isolates = new ObservableMap<String, Isolate>(); |
| IsolateList(this._vm); |
| - |
| + |
| void updateIsolates(List<Map> members) { |
| // Find dead isolates. |
| var deadIsolates = []; |
| @@ -545,7 +661,6 @@ class IsolateList { |
| } |
| } |
| - |
| /// A [ServiceObject] which implements [ObservableMap]. |
| class ServiceMap extends ServiceObject implements ObservableMap { |
| final ObservableMap _map = new ObservableMap(); |
| @@ -598,8 +713,42 @@ class ServiceMap extends ServiceObject implements ObservableMap { |
| bool get hasObservers => _map.hasObservers; |
| } |
| +/// A [DartError] is peered to a Dart Error object. |
| +class DartError extends ServiceObject { |
| + DartError.fromMap(ServiceObject owner, Map m) : super.fromMap(owner, m); |
| + |
| + @observable String kind; |
| + @observable String message; |
| + @observable ServiceMap exception; |
| + @observable ServiceMap stacktrace; |
| + |
| + void _update(ObservableMap map) { |
| + kind = map['kind']; |
| + message = map['message']; |
| + if (map['exception'] != null) { |
| + exception = new ServiceMap.fromMap(owner, map['exception']); |
| + } else { |
| + exception = null; |
| + } |
| + if (map['stacktrace'] != null) { |
| + stacktrace = new ServiceMap.fromMap(owner, map['stacktrace']); |
| + } else { |
| + stacktrace = null; |
| + } |
| + name = 'DartError $kind'; |
| + vmName = name; |
| + } |
| + |
| + // TODO: stackTrace? |
|
turnidge
2014/03/24 20:22:13
Delete this TODO?
Cutch
2014/03/25 14:39:06
Done.
|
| +} |
| + |
| +/// A [ServiceError] is an error that was triggered in the service |
| +/// server or client. Errors are prorammer mistakes that could have |
| +/// been prevented, for example, requesting a non-existant path over the |
| +/// service. |
| class ServiceError extends ServiceObject { |
| - ServiceError.fromMap(ServiceObject owner, Map m) : super.fromMap(owner, m); |
| + ServiceError.fromMap(ServiceObject owner, Map m) |
| + : super.fromMap(owner, m); |
| @observable String kind; |
| @observable String message; |
| @@ -610,8 +759,26 @@ class ServiceError extends ServiceObject { |
| name = 'ServiceError $kind'; |
| vmName = name; |
| } |
| +} |
| - // TODO: stackTrace? |
| +/// A [ServiceException] is an exception that was triggered in the service |
| +/// server or client. Exceptions are events that should be handled, |
| +/// for example, an isolate went away or the connection to the VM was lost. |
| +class ServiceException extends ServiceObject { |
| + ServiceException.fromMap(ServiceObject owner, Map m) |
| + : super.fromMap(owner, m); |
| + |
| + @observable String kind; |
| + @observable String message; |
| + @observable dynamic response; |
| + |
| + void _update(ObservableMap map) { |
| + kind = map['kind']; |
| + message = map['message']; |
| + response = map['response']; |
| + name = 'ServiceException $kind'; |
| + vmName = name; |
| + } |
| } |
| class ScriptLine { |
| @@ -884,7 +1051,11 @@ class Code extends ServiceObject { |
| startAddress = int.parse(m['start'], radix:16); |
| endAddress = int.parse(m['end'], radix:16); |
| function = _upgradeToServiceObject(vm, isolate, m['function']); |
| - objectPool = _upgradeToServiceObject(vm, isolate, m['object_pool']); |
| + if (m['object_pool'] != null) { |
| + objectPool = _upgradeToServiceObject(vm, isolate, m['object_pool']); |
| + } else { |
| + objectPool = null; |
| + } |
| var disassembly = m['disassembly']; |
| if (disassembly != null) { |
| _processDisassembly(disassembly); |