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 |
deleted file mode 100644 |
index 3c8f3b7ede9659e4026edc6dd616895f914a3578..0000000000000000000000000000000000000000 |
--- a/runtime/observatory/lib/src/service/object.dart |
+++ /dev/null |
@@ -1,2744 +0,0 @@ |
-// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
-// for details. All rights reserved. Use of this source code is governed by a |
-// BSD-style license that can be found in the LICENSE file. |
- |
-part of service; |
- |
-/// A [ServiceObject] is an object known to the VM service and is tied |
-/// to an owning [Isolate]. |
-abstract class ServiceObject extends Observable { |
- static int LexicalSortName(ServiceObject o1, ServiceObject o2) { |
- return o1.name.compareTo(o2.name); |
- } |
- |
- List removeDuplicatesAndSortLexical(List<ServiceObject> list) { |
- return list.toSet().toList()..sort(LexicalSortName); |
- } |
- |
- /// The owner of this [ServiceObject]. This can be an [Isolate], a |
- /// [VM], or null. |
- @reflectable ServiceObjectOwner get owner => _owner; |
- ServiceObjectOwner _owner; |
- |
- /// The [VM] which owns this [ServiceObject]. |
- @reflectable VM get vm => _owner.vm; |
- |
- /// The [Isolate] which owns this [ServiceObject]. May be null. |
- @reflectable Isolate get isolate => _owner.isolate; |
- |
- /// The id of this object. |
- @reflectable String get id => _id; |
- String _id; |
- |
- /// The user-level type of this object. |
- @reflectable String get type => _type; |
- String _type; |
- |
- /// The vm type of this object. |
- @reflectable String get vmType => _vmType; |
- String _vmType; |
- |
- static bool _isInstanceType(String type) { |
- switch (type) { |
- case 'BoundedType': |
- case 'Instance': |
- case 'List': |
- case 'String': |
- case 'Type': |
- case 'TypeParameter': |
- case 'TypeRef': |
- case 'bool': |
- case 'double': |
- case 'int': |
- case 'null': |
- return true; |
- default: |
- return false; |
- } |
- } |
- |
- static bool _isTypeType(String type) { |
- switch (type) { |
- case 'BoundedType': |
- case 'Type': |
- case 'TypeParameter': |
- case 'TypeRef': |
- return true; |
- default: |
- return false; |
- } |
- } |
- |
- bool get isAbstractType => _isTypeType(type); |
- bool get isBool => type == 'bool'; |
- bool get isContext => type == 'Context'; |
- bool get isDouble => type == 'double'; |
- bool get isError => type == 'Error'; |
- bool get isInstance => _isInstanceType(type); |
- bool get isInt => type == 'int'; |
- bool get isList => type == 'List'; |
- bool get isNull => type == 'null'; |
- bool get isSentinel => type == 'Sentinel'; |
- bool get isString => type == 'String'; |
- |
- // Kinds of Instance. |
- bool get isMirrorReference => vmType == 'MirrorReference'; |
- bool get isWeakProperty => vmType == 'WeakProperty'; |
- bool get isClosure => false; |
- bool get isPlainInstance { |
- return (type == 'Instance' && |
- !isMirrorReference && !isWeakProperty && !isClosure); |
- } |
- |
- /// The complete service url of this object. |
- @reflectable String get link => _owner.relativeLink(_id); |
- |
- /// Has this object been fully loaded? |
- bool get loaded => _loaded; |
- bool _loaded = false; |
- // TODO(turnidge): Make loaded observable and get rid of loading |
- // from Isolate. |
- |
- /// Is this object cacheable? That is, is it impossible for the [id] |
- /// of this object to change? |
- bool get canCache => false; |
- |
- /// Is this object immutable after it is [loaded]? |
- bool get immutable => false; |
- |
- @observable String name; |
- @observable String vmName; |
- |
- /// Creates an empty [ServiceObject]. |
- ServiceObject._empty(this._owner); |
- |
- /// Creates a [ServiceObject] initialized from [map]. |
- factory ServiceObject._fromMap(ServiceObjectOwner owner, |
- ObservableMap map) { |
- if (map == null) { |
- return null; |
- } |
- if (!_isServiceMap(map)) { |
- Logger.root.severe('Malformed service object: $map'); |
- } |
- assert(_isServiceMap(map)); |
- var type = _stripRef(map['type']); |
- var vmType = map['_vmType'] != null ? _stripRef(map['_vmType']) : type; |
- var obj = null; |
- assert(type != 'VM'); |
- switch (type) { |
- case 'Class': |
- obj = new Class._empty(owner); |
- break; |
- case 'Code': |
- obj = new Code._empty(owner); |
- break; |
- case 'Context': |
- obj = new Context._empty(owner); |
- break; |
- case 'Counter': |
- obj = new ServiceMetric._empty(owner); |
- break; |
- case 'Error': |
- obj = new DartError._empty(owner); |
- break; |
- case 'Field': |
- obj = new Field._empty(owner); |
- break; |
- case 'Function': |
- obj = new ServiceFunction._empty(owner); |
- break; |
- case 'Gauge': |
- obj = new ServiceMetric._empty(owner); |
- break; |
- case 'Isolate': |
- obj = new Isolate._empty(owner.vm); |
- break; |
- case 'Library': |
- obj = new Library._empty(owner); |
- break; |
- case 'Object': |
- switch (vmType) { |
- case 'PcDescriptors': |
- obj = new PcDescriptors._empty(owner); |
- break; |
- case 'LocalVarDescriptors': |
- obj = new LocalVarDescriptors._empty(owner); |
- break; |
- case 'TokenStream': |
- obj = new TokenStream._empty(owner); |
- break; |
- } |
- break; |
- case 'ServiceError': |
- obj = new ServiceError._empty(owner); |
- break; |
- case 'ServiceEvent': |
- obj = new ServiceEvent._empty(owner); |
- break; |
- case 'ServiceException': |
- obj = new ServiceException._empty(owner); |
- break; |
- case 'Script': |
- obj = new Script._empty(owner); |
- break; |
- case 'Socket': |
- obj = new Socket._empty(owner); |
- break; |
- default: |
- if (_isInstanceType(type) || |
- type == 'Sentinel') { // TODO(rmacnak): Separate this out. |
- obj = new Instance._empty(owner); |
- } |
- break; |
- } |
- if (obj == null) { |
- obj = new ServiceMap._empty(owner); |
- } |
- obj.update(map); |
- return obj; |
- } |
- |
- /// If [this] was created from a reference, load the full object |
- /// from the service by calling [reload]. Else, return [this]. |
- Future<ServiceObject> load() { |
- if (loaded) { |
- return new Future.value(this); |
- } |
- // Call reload which will fill in the entire object. |
- return reload(); |
- } |
- |
- Future<ServiceObject> _inProgressReload; |
- |
- /// Reload [this]. Returns a future which completes to [this] or |
- /// a [ServiceError]. |
- Future<ServiceObject> reload() { |
- if (id == '') { |
- // Errors don't have ids. |
- assert(type == 'Error'); |
- return new Future.value(this); |
- } |
- if (loaded && immutable) { |
- return new Future.value(this); |
- } |
- if (_inProgressReload == null) { |
- _inProgressReload = vm.getAsMap(link).then((ObservableMap map) { |
- var mapType = _stripRef(map['type']); |
- if (mapType != _type) { |
- // If the type changes, return a new object instead of |
- // updating the existing one. |
- // |
- // TODO(turnidge): Check for vmType changing as well? |
- assert(mapType == 'Error' || mapType == 'Sentinel'); |
- return new ServiceObject._fromMap(owner, map); |
- } |
- update(map); |
- return this; |
- }).whenComplete(() { |
- // This reload is complete. |
- _inProgressReload = null; |
- }); |
- } |
- return _inProgressReload; |
- } |
- |
- /// Update [this] using [map] as a source. [map] can be a reference. |
- void update(ObservableMap map) { |
- assert(_isServiceMap(map)); |
- |
- // Don't allow the type to change on an object update. |
- // TODO(turnidge): Make this a ServiceError? |
- var mapIsRef = _hasRef(map['type']); |
- var mapType = _stripRef(map['type']); |
- assert(_type == null || _type == mapType); |
- |
- if (_id != null && _id != map['id']) { |
- // It is only safe to change an id when the object isn't cacheable. |
- assert(!canCache); |
- } |
- _id = map['id']; |
- |
- _type = mapType; |
- |
- // When the response specifies a specific vmType, use it. |
- // Otherwise the vmType of the response is the same as the 'user' |
- // type. |
- if (map.containsKey('_vmType')) { |
- _vmType = _stripRef(map['_vmType']); |
- } else { |
- _vmType = _type; |
- } |
- |
- _update(map, mapIsRef); |
- } |
- |
- // Updates internal state from [map]. [map] can be a reference. |
- void _update(ObservableMap map, bool mapIsRef); |
- |
- String relativeLink(String id) { |
- assert(id != null); |
- return "${link}/${id}"; |
- } |
-} |
- |
-abstract class Coverage { |
- // Following getters and functions will be provided by [ServiceObject]. |
- ServiceObjectOwner get owner; |
- String get type; |
- VM get vm; |
- String relativeLink(String id); |
- |
- /// Default handler for coverage data. |
- void processCoverageData(List coverageData) { |
- coverageData.forEach((scriptCoverage) { |
- assert(scriptCoverage['script'] != null); |
- scriptCoverage['script']._processHits(scriptCoverage['hits']); |
- }); |
- } |
- |
- Future refreshCoverage() { |
- return vm.getAsMap(relativeLink('coverage')).then((ObservableMap map) { |
- var coverageOwner = (type == 'Isolate') ? this : owner; |
- var coverage = new ServiceObject._fromMap(coverageOwner, map); |
- assert(coverage.type == 'CodeCoverage'); |
- var coverageList = coverage['coverage']; |
- assert(coverageList != null); |
- processCoverageData(coverageList); |
- }); |
- } |
-} |
- |
-abstract class ServiceObjectOwner extends ServiceObject { |
- /// Creates an empty [ServiceObjectOwner]. |
- ServiceObjectOwner._empty(ServiceObjectOwner owner) : super._empty(owner); |
- |
- /// Builds a [ServiceObject] corresponding to the [id] from [map]. |
- /// The result may come from the cache. The result will not necessarily |
- /// be [loaded]. |
- ServiceObject getFromMap(ObservableMap map); |
- |
- /// Creates a link to [id] relative to [this]. |
- String relativeLink(String id); |
-} |
- |
-/// State for a VM being inspected. |
-abstract class VM extends ServiceObjectOwner { |
- @reflectable VM get vm => this; |
- @reflectable Isolate get isolate => null; |
- |
- @reflectable Iterable<Isolate> get isolates => _isolateCache.values; |
- |
- @reflectable String get link => '$id'; |
- @reflectable String relativeLink(String id) => '$id'; |
- |
- @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; |
- |
- VM() : super._empty(null) { |
- name = 'vm'; |
- vmName = 'vm'; |
- _cache['vm'] = this; |
- update(toObservable({'id':'vm', 'type':'@VM'})); |
- } |
- |
- final StreamController<ServiceException> exceptions = |
- new StreamController.broadcast(); |
- final StreamController<ServiceError> errors = |
- new StreamController.broadcast(); |
- final StreamController<ServiceEvent> events = |
- new StreamController.broadcast(); |
- |
- void postEventMessage(String eventMessage, [dynamic data]) { |
- var map; |
- try { |
- map = _parseJSON(eventMessage); |
- assert(!map.containsKey('_data')); |
- if (data != null) { |
- map['_data'] = data; |
- } |
- } catch (e, st) { |
- Logger.root.severe('Ignoring malformed event message: ${eventMessage}'); |
- return; |
- } |
- if (map['type'] != 'ServiceEvent') { |
- Logger.root.severe( |
- "Expected 'ServiceEvent' but found '${map['type']}'"); |
- 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'); |
- } else { |
- var event = new ServiceObject._fromMap(owningIsolate, map); |
- events.add(event); |
- } |
- }); |
- } |
- |
- static final RegExp _currentIsolateMatcher = new RegExp(r'isolates/\d+'); |
- static final RegExp _currentObjectMatcher = new RegExp(r'isolates/\d+/'); |
- static final String _isolatesPrefix = 'isolates/'; |
- |
- String _parseObjectId(String id) { |
- Match m = _currentObjectMatcher.matchAsPrefix(id); |
- if (m == null) { |
- return null; |
- } |
- return m.input.substring(m.end); |
- } |
- |
- String _parseIsolateId(String id) { |
- Match m = _currentIsolateMatcher.matchAsPrefix(id); |
- if (m == null) { |
- return ''; |
- } |
- return id.substring(0, m.end); |
- } |
- |
- Map<String,ServiceObject> _cache = new Map<String,ServiceObject>(); |
- Map<String,Isolate> _isolateCache = new Map<String,Isolate>(); |
- |
- ServiceObject getFromMap(ObservableMap map) { |
- throw new UnimplementedError(); |
- } |
- |
- Future<ServiceObject> _getIsolate(String isolateId) { |
- if (isolateId == '') { |
- return new Future.value(null); |
- } |
- Isolate isolate = _isolateCache[isolateId]; |
- if (isolate != null) { |
- return new Future.value(isolate); |
- } |
- // The isolate is not in the cache. Reload the vm and see if the |
- // requested isolate is found. |
- return reload().then((result) { |
- if (result is! VM) { |
- return null; |
- } |
- assert(result == this); |
- return _isolateCache[isolateId]; |
- }); |
- } |
- |
- Future<ServiceObject> get(String id) { |
- assert(id.startsWith('/') == false); |
- // Isolates are handled specially, since they can cache sub-objects. |
- if (id.startsWith(_isolatesPrefix)) { |
- String isolateId = _parseIsolateId(id); |
- String objectId = _parseObjectId(id); |
- return _getIsolate(isolateId).then((isolate) { |
- if (isolate == null) { |
- // The isolate does not exist. Return the VM object instead. |
- // |
- // TODO(turnidge): Generate a service error? |
- return this; |
- } |
- if (objectId == null) { |
- return isolate.reload(); |
- } else { |
- return isolate.get(objectId); |
- } |
- }); |
- } |
- |
- var obj = _cache[id]; |
- if (obj != null) { |
- return obj.reload(); |
- } |
- |
- // Cache miss. Get the object from the vm directly. |
- return getAsMap(id).then((ObservableMap map) { |
- var obj = new ServiceObject._fromMap(this, map); |
- if (obj.canCache) { |
- _cache.putIfAbsent(id, () => obj); |
- } |
- return obj; |
- }); |
- } |
- |
- dynamic _reviver(dynamic key, dynamic value) { |
- return value; |
- } |
- |
- ObservableMap _parseJSON(String response) { |
- var map; |
- try { |
- var decoder = new JsonDecoder(_reviver); |
- map = decoder.convert(response); |
- } catch (e, st) { |
- return null; |
- } |
- return toObservable(map); |
- } |
- |
- Future<ObservableMap> _processMap(ObservableMap map) { |
- // Verify that the top level response is a service map. |
- if (!_isServiceMap(map)) { |
- return new Future.error( |
- new ServiceObject._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 ServiceObject._fromMap(this, map)); |
- } else if (map['type'] == 'ServiceException') { |
- return new Future.error(new ServiceObject._fromMap(this, map)); |
- } |
- // map is now guaranteed to be a non-error/exception ServiceObject. |
- return new Future.value(map); |
- } |
- |
- Future<ObservableMap> _decodeError(e) { |
- return new Future.error(new ServiceObject._fromMap(this, toObservable({ |
- 'type': 'ServiceException', |
- 'id': '', |
- 'kind': 'DecodeException', |
- 'response': |
- 'This is likely a result of a known V8 bug. Although the ' |
- 'the bug has been fixed the fix may not be in your Chrome' |
- ' version. For more information see dartbug.com/18385. ' |
- 'Observatory is still functioning and you should try your' |
- ' action again.', |
- 'message': 'Could not decode JSON: $e', |
- }))); |
- } |
- |
- /// 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 any chained then() calls |
- /// will only receive a map encoding a valid ServiceObject. |
- Future<ObservableMap> getAsMap(String id) { |
- return getString(id).then((response) { |
- var map = _parseJSON(response); |
- if (Tracer.current != null) { |
- Tracer.current.trace("Received response for ${id}", map:map); |
- } |
- return _processMap(map); |
- }).catchError((error) { |
- // ServiceError, forward to VM's ServiceError stream. |
- errors.add(error); |
- return new Future.error(error); |
- }, test: (e) => e is ServiceError).catchError((exception) { |
- // ServiceException, forward to VM's ServiceException stream. |
- exceptions.add(exception); |
- return new Future.error(exception); |
- }, test: (e) => e is ServiceException); |
- } |
- |
- /// Get [id] as a [String] from the service directly. See [getAsMap]. |
- Future<String> getString(String id); |
- /// Force the VM to disconnect. |
- void disconnect(); |
- /// Completes when the VM first connects. |
- Future get onConnect; |
- /// Completes when the VM disconnects or there was an error connecting. |
- Future get onDisconnect; |
- |
- void _update(ObservableMap map, bool mapIsRef) { |
- if (mapIsRef) { |
- return; |
- } |
- _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); |
- assertsEnabled = map['assertsEnabled']; |
- pid = map['pid']; |
- typeChecksEnabled = map['typeChecksEnabled']; |
- _updateIsolates(map['isolates']); |
- } |
- |
- void _updateIsolates(List newIsolates) { |
- var oldIsolateCache = _isolateCache; |
- var newIsolateCache = new Map<String,Isolate>(); |
- for (var isolateMap in newIsolates) { |
- var isolateId = isolateMap['id']; |
- var isolate = oldIsolateCache[isolateId]; |
- if (isolate != null) { |
- newIsolateCache[isolateId] = isolate; |
- } else { |
- isolate = new ServiceObject._fromMap(this, isolateMap); |
- newIsolateCache[isolateId] = isolate; |
- Logger.root.info('New isolate \'${isolate.id}\''); |
- } |
- } |
- // Update the individual isolates asynchronously. |
- newIsolateCache.forEach((isolateId, isolate) { |
- isolate.reload(); |
- }); |
- |
- _isolateCache = newIsolateCache; |
- } |
-} |
- |
-/// Snapshot in time of tag counters. |
-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); |
- } |
- } |
-} |
- |
-class HeapSpace extends Observable { |
- @observable int used = 0; |
- @observable int capacity = 0; |
- @observable int external = 0; |
- @observable int collections = 0; |
- @observable double totalCollectionTimeInSeconds = 0.0; |
- @observable double averageCollectionPeriodInMillis = 0.0; |
- |
- void update(Map heapMap) { |
- used = heapMap['used']; |
- capacity = heapMap['capacity']; |
- external = heapMap['external']; |
- collections = heapMap['collections']; |
- totalCollectionTimeInSeconds = heapMap['time']; |
- averageCollectionPeriodInMillis = heapMap['avgCollectionPeriodMillis']; |
- } |
-} |
- |
-/// State for a running isolate. |
-class Isolate extends ServiceObjectOwner with Coverage { |
- @reflectable VM get vm => owner; |
- @reflectable Isolate get isolate => this; |
- @observable ObservableMap counters = new ObservableMap(); |
- |
- String get link => '/${_id}'; |
- |
- @observable ServiceEvent pauseEvent = null; |
- bool get _isPaused => pauseEvent != null; |
- |
- @observable bool running = false; |
- @observable bool idle = false; |
- @observable bool loading = true; |
- @observable bool ioEnabled = false; |
- |
- Map<String,ServiceObject> _cache = new Map<String,ServiceObject>(); |
- final TagProfile tagProfile = new TagProfile(20); |
- |
- Isolate._empty(ServiceObjectOwner owner) : super._empty(owner) { |
- assert(owner is VM); |
- } |
- |
- /// Creates a link to [id] relative to [this]. |
- @reflectable String relativeLink(String id) => '/${this.id}/$id'; |
- |
- static const TAG_ROOT_ID = 'code/tag-0'; |
- |
- /// Returns the Code object for the root tag. |
- Code tagRoot() { |
- // TODO(turnidge): Use get() here instead? |
- return _cache[TAG_ROOT_ID]; |
- } |
- |
- void processProfile(ServiceMap profile) { |
- assert(profile.type == 'Profile'); |
- var codeTable = new List<Code>(); |
- var codeRegions = profile['codes']; |
- for (var codeRegion in codeRegions) { |
- Code code = codeRegion['code']; |
- assert(code != null); |
- codeTable.add(code); |
- } |
- _resetProfileData(); |
- _updateProfileData(profile, codeTable); |
- var exclusiveTrie = profile['exclusive_trie']; |
- if (exclusiveTrie != null) { |
- profileTrieRoot = _processProfileTrie(exclusiveTrie, codeTable); |
- } |
- } |
- |
- void _resetProfileData() { |
- _cache.values.forEach((value) { |
- if (value is Code) { |
- Code code = value; |
- code.resetProfileData(); |
- } |
- }); |
- } |
- |
- void _updateProfileData(ServiceMap profile, List<Code> codeTable) { |
- var codeRegions = profile['codes']; |
- var sampleCount = profile['samples']; |
- for (var codeRegion in codeRegions) { |
- Code code = codeRegion['code']; |
- code.updateProfileData(codeRegion, codeTable, sampleCount); |
- } |
- } |
- |
- /// Fetches and builds the class hierarchy for this isolate. Returns the |
- /// Object class object. |
- Future<Class> getClassHierarchy() { |
- return get('classes').then(_loadClasses).then(_buildClassHierarchy); |
- } |
- |
- /// Given the class list, loads each class. |
- Future<List<Class>> _loadClasses(ServiceMap classList) { |
- assert(classList.type == 'ClassList'); |
- var futureClasses = []; |
- for (var cls in classList['members']) { |
- // Skip over non-class classes. |
- if (cls is Class) { |
- futureClasses.add(cls.load()); |
- } |
- } |
- return Future.wait(futureClasses); |
- } |
- |
- /// Builds the class hierarchy and returns the Object class. |
- Future<Class> _buildClassHierarchy(List<Class> classes) { |
- rootClasses.clear(); |
- objectClass = null; |
- for (var cls in classes) { |
- if (cls.superclass == null) { |
- rootClasses.add(cls); |
- } |
- if ((cls.vmName == 'Object') && (cls.isPatch == false)) { |
- objectClass = cls; |
- } |
- } |
- assert(objectClass != null); |
- return new Future.value(objectClass); |
- } |
- |
- ServiceObject getFromMap(ObservableMap map) { |
- if (map == null) { |
- return null; |
- } |
- String id = map['id']; |
- var obj = _cache[id]; |
- if (obj != null) { |
- // Consider calling update when map is not a reference. |
- return obj; |
- } |
- // Build the object from the map directly. |
- obj = new ServiceObject._fromMap(this, map); |
- if (obj != null && obj.canCache) { |
- _cache[id] = obj; |
- } |
- return obj; |
- } |
- |
- Future<ServiceObject> get(String id) { |
- // Do not allow null ids or empty ids. |
- assert(id != null && id != ''); |
- var obj = _cache[id]; |
- if (obj != null) { |
- return obj.reload(); |
- } |
- // Cache miss. Get the object from the vm directly. |
- return vm.getAsMap(relativeLink(id)).then((ObservableMap map) { |
- var obj = new ServiceObject._fromMap(this, map); |
- if (obj.canCache) { |
- _cache.putIfAbsent(id, () => obj); |
- } |
- return obj; |
- }); |
- } |
- |
- @observable Class objectClass; |
- @observable final rootClasses = new ObservableList<Class>(); |
- |
- @observable Library rootLib; |
- @observable ObservableList<Library> libraries = |
- new ObservableList<Library>(); |
- @observable ObservableMap topFrame; |
- |
- @observable String name; |
- @observable String vmName; |
- @observable String mainPort; |
- @observable ServiceFunction entry; |
- |
- @observable final Map<String, double> timers = |
- toObservable(new Map<String, double>()); |
- |
- final HeapSpace newSpace = new HeapSpace(); |
- final HeapSpace oldSpace = new HeapSpace(); |
- |
- @observable String fileAndLine; |
- |
- @observable DartError error; |
- |
- void updateHeapsFromMap(ObservableMap map) { |
- newSpace.update(map['new']); |
- oldSpace.update(map['old']); |
- } |
- |
- void _update(ObservableMap map, bool mapIsRef) { |
- mainPort = map['mainPort']; |
- name = map['name']; |
- vmName = map['name']; |
- if (mapIsRef) { |
- return; |
- } |
- _loaded = true; |
- loading = false; |
- |
- reloadBreakpoints(); |
- _upgradeCollection(map, isolate); |
- if (map['rootLib'] == null || |
- map['timers'] == null || |
- map['heaps'] == null) { |
- Logger.root.severe("Malformed 'Isolate' response: $map"); |
- return; |
- } |
- rootLib = map['rootLib']; |
- if (map['entry'] != null) { |
- entry = map['entry']; |
- } |
- if (map['topFrame'] != null) { |
- topFrame = map['topFrame']; |
- } else { |
- topFrame = null ; |
- } |
- |
- var countersMap = map['tagCounters']; |
- if (countersMap != null) { |
- var names = countersMap['names']; |
- var counts = countersMap['counters']; |
- assert(names.length == counts.length); |
- var sum = 0; |
- for (var i = 0; i < counts.length; i++) { |
- sum += counts[i]; |
- } |
- // TODO: Why does this not work without this? |
- counters = toObservable({}); |
- if (sum == 0) { |
- for (var i = 0; i < names.length; i++) { |
- counters[names[i]] = '0.0%'; |
- } |
- } else { |
- for (var i = 0; i < names.length; i++) { |
- counters[names[i]] = |
- (counts[i] / sum * 100.0).toStringAsFixed(2) + '%'; |
- } |
- } |
- } |
- var timerMap = {}; |
- map['timers'].forEach((timer) { |
- timerMap[timer['name']] = timer['time']; |
- }); |
- timers['total'] = timerMap['time_total_runtime']; |
- timers['compile'] = timerMap['time_compilation']; |
- timers['gc'] = 0.0; // TODO(turnidge): Export this from VM. |
- timers['init'] = (timerMap['time_script_loading'] + |
- timerMap['time_creating_snapshot'] + |
- timerMap['time_isolate_initialization'] + |
- timerMap['time_bootstrap']); |
- timers['dart'] = timerMap['time_dart_execution']; |
- |
- updateHeapsFromMap(map['heaps']); |
- |
- List features = map['features']; |
- if (features != null) { |
- for (var feature in features) { |
- if (feature == 'io') { |
- ioEnabled = true; |
- } |
- } |
- } |
- // Isolate status |
- pauseEvent = map['pauseEvent']; |
- running = (!_isPaused && map['topFrame'] != null); |
- idle = (!_isPaused && map['topFrame'] == null); |
- error = map['error']; |
- |
- libraries.clear(); |
- libraries.addAll(map['libraries']); |
- libraries.sort(ServiceObject.LexicalSortName); |
- } |
- |
- 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; |
- // The profile trie is serialized as a list of integers. Each node |
- // is recreated by consuming some portion of the list. The format is as |
- // follows: |
- // [0] index into codeTable of code object. |
- // [1] tick count (number of times this stack frame occured). |
- // [2] child node count |
- // Reading the trie is done by recursively reading the tree depth-first |
- // pre-order. |
- CodeTrieNode _processProfileTrie(List<int> data, List<Code> codeTable) { |
- // Setup state shared across calls to _readTrieNode. |
- _trieDataCursor = 0; |
- _trieData = data; |
- if (_trieData == null) { |
- return null; |
- } |
- if (_trieData.length < 3) { |
- // Not enough integers for 1 node. |
- return null; |
- } |
- // Read the tree, returns the root node. |
- return _readTrieNode(codeTable); |
- } |
- int _trieDataCursor; |
- List<int> _trieData; |
- CodeTrieNode _readTrieNode(List<Code> codeTable) { |
- // Read index into code table. |
- var index = _trieData[_trieDataCursor++]; |
- // Lookup code object. |
- var code = codeTable[index]; |
- // Frame counter. |
- var count = _trieData[_trieDataCursor++]; |
- // Create node. |
- var node = new CodeTrieNode(code, count); |
- // Number of children. |
- var children = _trieData[_trieDataCursor++]; |
- // Recursively read child nodes. |
- for (var i = 0; i < children; i++) { |
- var child = _readTrieNode(codeTable); |
- node.children.add(child); |
- node.summedChildCount += child.count; |
- } |
- return node; |
- } |
- |
- ServiceMap breakpoints; |
- |
- void _removeBreakpoint(ServiceMap bpt) { |
- var script = bpt['location']['script']; |
- var tokenPos = bpt['location']['tokenPos']; |
- assert(tokenPos != null); |
- if (script.loaded) { |
- var line = script.tokenToLine(tokenPos); |
- assert(line != null); |
- assert(script.lines[line - 1].bpt == bpt); |
- script.lines[line - 1].bpt = null; |
- } |
- } |
- |
- void _addBreakpoint(ServiceMap bpt) { |
- var script = bpt['location']['script']; |
- var tokenPos = bpt['location']['tokenPos']; |
- assert(tokenPos != null); |
- if (script.loaded) { |
- var line = script.tokenToLine(tokenPos); |
- assert(line != null); |
- assert(script.lines[line - 1].bpt == null); |
- script.lines[line - 1].bpt = bpt; |
- } else { |
- // Load the script and then plop in the breakpoint. |
- script.load().then((_) { |
- _addBreakpoint(bpt); |
- }); |
- } |
- } |
- |
- void _updateBreakpoints(ServiceMap newBreakpoints) { |
- // Remove all of the old breakpoints from the Script lines. |
- if (breakpoints != null) { |
- for (var bpt in breakpoints['breakpoints']) { |
- _removeBreakpoint(bpt); |
- } |
- } |
- // Add all of the new breakpoints to the Script lines. |
- for (var bpt in newBreakpoints['breakpoints']) { |
- _addBreakpoint(bpt); |
- } |
- breakpoints = newBreakpoints; |
- } |
- |
- Future<ServiceObject> _inProgressReloadBpts; |
- |
- Future reloadBreakpoints() { |
- // TODO(turnidge): Can reusing the Future here ever cause us to |
- // get stale breakpoints? |
- if (_inProgressReloadBpts == null) { |
- _inProgressReloadBpts = |
- get('debug/breakpoints').then((newBpts) { |
- _updateBreakpoints(newBpts); |
- }).whenComplete(() { |
- _inProgressReloadBpts = null; |
- }); |
- } |
- return _inProgressReloadBpts; |
- } |
- |
- Future<ServiceObject> setBreakpoint(Script script, int line) { |
- return get(script.id + "/setBreakpoint?line=${line}").then((result) { |
- if (result is DartError) { |
- // Unable to set a breakpoint at desired line. |
- script.lines[line - 1].possibleBpt = false; |
- } |
- return reloadBreakpoints(); |
- }); |
- } |
- |
- Future clearBreakpoint(ServiceMap bpt) { |
- return get('${bpt.id}/clear').then((result) { |
- if (result is DartError) { |
- // TODO(turnidge): Handle this more gracefully. |
- Logger.root.severe(result.message); |
- } |
- if (pauseEvent != null && |
- pauseEvent.breakpoint != null && |
- (pauseEvent.breakpoint['id'] == bpt['id'])) { |
- return isolate.reload(); |
- } else { |
- return reloadBreakpoints(); |
- } |
- }); |
- } |
- |
- Future pause() { |
- return get("debug/pause").then((result) { |
- if (result is DartError) { |
- // TODO(turnidge): Handle this more gracefully. |
- Logger.root.severe(result.message); |
- } |
- return isolate.reload(); |
- }); |
- } |
- |
- Future resume() { |
- return get("debug/resume").then((result) { |
- if (result is DartError) { |
- // TODO(turnidge): Handle this more gracefully. |
- Logger.root.severe(result.message); |
- } |
- return isolate.reload(); |
- }); |
- } |
- |
- Future stepInto() { |
- return get("debug/resume?step=into").then((result) { |
- if (result is DartError) { |
- // TODO(turnidge): Handle this more gracefully. |
- Logger.root.severe(result.message); |
- } |
- return isolate.reload(); |
- }); |
- } |
- |
- Future stepOver() { |
- return get("debug/resume?step=over").then((result) { |
- if (result is DartError) { |
- // TODO(turnidge): Handle this more gracefully. |
- Logger.root.severe(result.message); |
- } |
- return isolate.reload(); |
- }); |
- } |
- |
- Future stepOut() { |
- return get("debug/resume?step=out").then((result) { |
- if (result is DartError) { |
- // TODO(turnidge): Handle this more gracefully. |
- Logger.root.severe(result.message); |
- } |
- return isolate.reload(); |
- }); |
- } |
- |
- final ObservableMap<String, ServiceMetric> dartMetrics = |
- new ObservableMap<String, ServiceMetric>(); |
- |
- final ObservableMap<String, ServiceMetric> vmMetrics = |
- new ObservableMap<String, ServiceMetric>(); |
- |
- Future<ObservableMap<String, ServiceMetric>> _refreshMetrics( |
- String id, |
- ObservableMap<String, ServiceMetric> metricsMap) { |
- return get(id).then((result) { |
- if (result is DartError) { |
- // TODO(turnidge): Handle this more gracefully. |
- Logger.root.severe(result.message); |
- return null; |
- } |
- // Clear metrics map. |
- metricsMap.clear(); |
- // Repopulate metrics map. |
- var members = result['members']; |
- for (var metric in members) { |
- metricsMap[metric.id] = metric; |
- } |
- return metricsMap; |
- }); |
- } |
- |
- Future<ObservableMap<String, ServiceMetric>> refreshDartMetrics() { |
- return _refreshMetrics('metrics', dartMetrics); |
- } |
- |
- Future<ObservableMap<String, ServiceMetric>> refreshVMMetrics() { |
- return _refreshMetrics('metrics/vm', vmMetrics); |
- } |
- |
- Future refreshMetrics() { |
- return refreshDartMetrics().then((_) => refreshVMMetrics()); |
- } |
- |
- String toString() => "Isolate($_id)"; |
-} |
- |
-/// A [ServiceObject] which implements [ObservableMap]. |
-class ServiceMap extends ServiceObject implements ObservableMap { |
- final ObservableMap _map = new ObservableMap(); |
- static String objectIdRingPrefix = 'objects/'; |
- |
- bool get canCache { |
- return (_type == 'Class' || |
- _type == 'Function' || |
- _type == 'Field') && |
- !_id.startsWith(objectIdRingPrefix); |
- } |
- bool get immutable => false; |
- |
- ServiceMap._empty(ServiceObjectOwner owner) : super._empty(owner); |
- |
- void _upgradeValues() { |
- assert(owner != null); |
- _upgradeCollection(_map, owner); |
- } |
- |
- void _update(ObservableMap map, bool mapIsRef) { |
- _loaded = !mapIsRef; |
- |
- // TODO(turnidge): Currently _map.clear() prevents us from |
- // upgrading an already upgraded submap. Is clearing really the |
- // right thing to do here? |
- _map.clear(); |
- _map.addAll(map); |
- |
- name = _map['name']; |
- vmName = (_map.containsKey('vmName') ? _map['vmName'] : name); |
- _upgradeValues(); |
- } |
- |
- // Forward Map interface calls. |
- void addAll(Map other) => _map.addAll(other); |
- void clear() => _map.clear(); |
- bool containsValue(v) => _map.containsValue(v); |
- bool containsKey(k) => _map.containsKey(k); |
- void forEach(Function f) => _map.forEach(f); |
- putIfAbsent(key, Function ifAbsent) => _map.putIfAbsent(key, ifAbsent); |
- void remove(key) => _map.remove(key); |
- operator [](k) => _map[k]; |
- operator []=(k, v) => _map[k] = v; |
- bool get isEmpty => _map.isEmpty; |
- bool get isNotEmpty => _map.isNotEmpty; |
- Iterable get keys => _map.keys; |
- Iterable get values => _map.values; |
- int get length => _map.length; |
- |
- // Forward ChangeNotifier interface calls. |
- bool deliverChanges() => _map.deliverChanges(); |
- void notifyChange(ChangeRecord record) => _map.notifyChange(record); |
- notifyPropertyChange(Symbol field, Object oldValue, Object newValue) => |
- _map.notifyPropertyChange(field, oldValue, newValue); |
- void observed() => _map.observed(); |
- void unobserved() => _map.unobserved(); |
- Stream<List<ChangeRecord>> get changes => _map.changes; |
- bool get hasObservers => _map.hasObservers; |
- |
- String toString() => "ServiceMap($_map)"; |
-} |
- |
-/// A [DartError] is peered to a Dart Error object. |
-class DartError extends ServiceObject { |
- DartError._empty(ServiceObject owner) : super._empty(owner); |
- |
- @observable String kind; |
- @observable String message; |
- @observable Instance exception; |
- @observable Instance stacktrace; |
- |
- void _update(ObservableMap map, bool mapIsRef) { |
- kind = map['kind']; |
- message = map['message']; |
- exception = new ServiceObject._fromMap(owner, map['exception']); |
- stacktrace = new ServiceObject._fromMap(owner, map['stacktrace']); |
- name = 'DartError $kind'; |
- vmName = name; |
- } |
- |
- String toString() => 'DartError($message)'; |
-} |
- |
-/// 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._empty(ServiceObjectOwner owner) : super._empty(owner); |
- |
- @observable String kind; |
- @observable String message; |
- |
- void _update(ObservableMap map, bool mapIsRef) { |
- _loaded = true; |
- kind = map['kind']; |
- message = map['message']; |
- name = 'ServiceError $kind'; |
- vmName = name; |
- } |
- |
- String toString() => 'ServiceError($message)'; |
-} |
- |
-/// 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._empty(ServiceObject owner) : super._empty(owner); |
- |
- @observable String kind; |
- @observable String message; |
- @observable dynamic response; |
- |
- void _update(ObservableMap map, bool mapIsRef) { |
- kind = map['kind']; |
- message = map['message']; |
- response = map['response']; |
- name = 'ServiceException $kind'; |
- vmName = name; |
- } |
- |
- String toString() => 'ServiceException($message)'; |
-} |
- |
-/// A [ServiceEvent] is an asynchronous event notification from the vm. |
-class ServiceEvent extends ServiceObject { |
- ServiceEvent._empty(ServiceObjectOwner owner) : super._empty(owner); |
- |
- ServiceEvent.vmDisconencted() : super._empty(null) { |
- eventType = 'VMDisconnected'; |
- } |
- |
- @observable String eventType; |
- @observable ServiceMap breakpoint; |
- @observable ServiceMap exception; |
- @observable ByteData data; |
- @observable int count; |
- |
- void _update(ObservableMap map, bool mapIsRef) { |
- _loaded = true; |
- _upgradeCollection(map, owner); |
- eventType = map['eventType']; |
- name = 'ServiceEvent $eventType'; |
- vmName = name; |
- if (map['breakpoint'] != null) { |
- breakpoint = map['breakpoint']; |
- } |
- if (map['exception'] != null) { |
- exception = map['exception']; |
- } |
- if (map['_data'] != null) { |
- data = map['_data']; |
- } |
- if (map['count'] != null) { |
- count = map['count']; |
- } |
- } |
- |
- String toString() { |
- return 'ServiceEvent of type $eventType with ' |
- '${data == null ? 0 : data.lengthInBytes} bytes of binary data'; |
- } |
-} |
- |
-class Library extends ServiceObject with Coverage { |
- @observable String url; |
- @reflectable final imports = new ObservableList<Library>(); |
- @reflectable final scripts = new ObservableList<Script>(); |
- @reflectable final classes = new ObservableList<Class>(); |
- @reflectable final variables = new ObservableList<Field>(); |
- @reflectable final functions = new ObservableList<ServiceFunction>(); |
- |
- bool get canCache => true; |
- bool get immutable => false; |
- |
- Library._empty(ServiceObjectOwner owner) : super._empty(owner); |
- |
- void _update(ObservableMap map, bool mapIsRef) { |
- url = map['url']; |
- var shortUrl = url; |
- if (url.startsWith('file://') || |
- url.startsWith('http://')) { |
- shortUrl = url.substring(url.lastIndexOf('/') + 1); |
- } |
- name = map['name']; |
- if (name.isEmpty) { |
- // When there is no name for a library, use the shortUrl. |
- name = shortUrl; |
- } |
- vmName = (map.containsKey('vmName') ? map['vmName'] : name); |
- if (mapIsRef) { |
- return; |
- } |
- _loaded = true; |
- _upgradeCollection(map, isolate); |
- imports.clear(); |
- imports.addAll(removeDuplicatesAndSortLexical(map['imports'])); |
- scripts.clear(); |
- scripts.addAll(removeDuplicatesAndSortLexical(map['scripts'])); |
- classes.clear(); |
- classes.addAll(map['classes']); |
- classes.sort(ServiceObject.LexicalSortName); |
- variables.clear(); |
- variables.addAll(map['variables']); |
- variables.sort(ServiceObject.LexicalSortName); |
- functions.clear(); |
- functions.addAll(map['functions']); |
- functions.sort(ServiceObject.LexicalSortName); |
- } |
- |
- String toString() => "Library($url)"; |
-} |
- |
-class AllocationCount extends Observable { |
- @observable int instances = 0; |
- @observable int bytes = 0; |
- |
- void reset() { |
- instances = 0; |
- bytes = 0; |
- } |
- |
- bool get empty => (instances == 0) && (bytes == 0); |
-} |
- |
-class Allocations { |
- // Indexes into VM provided array. (see vm/class_table.h). |
- static const ALLOCATED_BEFORE_GC = 0; |
- static const ALLOCATED_BEFORE_GC_SIZE = 1; |
- static const LIVE_AFTER_GC = 2; |
- static const LIVE_AFTER_GC_SIZE = 3; |
- static const ALLOCATED_SINCE_GC = 4; |
- static const ALLOCATED_SINCE_GC_SIZE = 5; |
- static const ACCUMULATED = 6; |
- static const ACCUMULATED_SIZE = 7; |
- |
- final AllocationCount accumulated = new AllocationCount(); |
- final AllocationCount current = new AllocationCount(); |
- |
- void update(List stats) { |
- accumulated.instances = stats[ACCUMULATED]; |
- accumulated.bytes = stats[ACCUMULATED_SIZE]; |
- current.instances = stats[LIVE_AFTER_GC] + stats[ALLOCATED_SINCE_GC]; |
- current.bytes = stats[LIVE_AFTER_GC_SIZE] + stats[ALLOCATED_SINCE_GC_SIZE]; |
- } |
- |
- bool get empty => accumulated.empty && current.empty; |
-} |
- |
-class Class extends ServiceObject with Coverage { |
- @observable Library library; |
- @observable Script script; |
- |
- @observable bool isAbstract; |
- @observable bool isConst; |
- @observable bool isFinalized; |
- @observable bool isPatch; |
- @observable bool isImplemented; |
- |
- @observable int tokenPos; |
- @observable int endTokenPos; |
- |
- @observable ServiceMap error; |
- @observable int vmCid; |
- |
- final Allocations newSpace = new Allocations(); |
- final Allocations oldSpace = new Allocations(); |
- final AllocationCount promotedByLastNewGC = new AllocationCount(); |
- |
- bool get hasNoAllocations => newSpace.empty && oldSpace.empty; |
- |
- @reflectable final fields = new ObservableList<Field>(); |
- @reflectable final functions = new ObservableList<ServiceFunction>(); |
- |
- @observable Class superclass; |
- @reflectable final interfaces = new ObservableList<Class>(); |
- @reflectable final subclasses = new ObservableList<Class>(); |
- |
- bool get canCache => true; |
- bool get immutable => false; |
- |
- Class._empty(ServiceObjectOwner owner) : super._empty(owner); |
- |
- void _update(ObservableMap map, bool mapIsRef) { |
- name = map['name']; |
- vmName = (map.containsKey('vmName') ? map['vmName'] : name); |
- var idPrefix = "classes/"; |
- assert(id.startsWith(idPrefix)); |
- vmCid = int.parse(id.substring(idPrefix.length)); |
- |
- if (mapIsRef) { |
- return; |
- } |
- |
- // We are fully loaded. |
- _loaded = true; |
- |
- // Extract full properties. |
- _upgradeCollection(map, isolate); |
- |
- // Some builtin classes aren't associated with a library. |
- if (map['library'] is Library) { |
- library = map['library']; |
- } else { |
- library = null; |
- } |
- |
- script = map['script']; |
- |
- isAbstract = map['abstract']; |
- isConst = map['const']; |
- isFinalized = map['finalized']; |
- isPatch = map['patch']; |
- isImplemented = map['implemented']; |
- |
- tokenPos = map['tokenPos']; |
- endTokenPos = map['endTokenPos']; |
- |
- subclasses.clear(); |
- subclasses.addAll(map['subclasses']); |
- subclasses.sort(ServiceObject.LexicalSortName); |
- |
- fields.clear(); |
- fields.addAll(map['fields']); |
- fields.sort(ServiceObject.LexicalSortName); |
- |
- functions.clear(); |
- functions.addAll(map['functions']); |
- functions.sort(ServiceObject.LexicalSortName); |
- |
- superclass = map['super']; |
- // Work-around Object not tracking its subclasses in the VM. |
- if (superclass != null && superclass.name == "Object") { |
- superclass._addSubclass(this); |
- } |
- error = map['error']; |
- |
- var allocationStats = map['allocationStats']; |
- if (allocationStats != null) { |
- newSpace.update(allocationStats['new']); |
- oldSpace.update(allocationStats['old']); |
- promotedByLastNewGC.instances = allocationStats['promotedInstances']; |
- promotedByLastNewGC.bytes = allocationStats['promotedBytes']; |
- } |
- } |
- |
- void _addSubclass(Class subclass) { |
- if (subclasses.contains(subclass)) { |
- return; |
- } |
- subclasses.add(subclass); |
- subclasses.sort(ServiceObject.LexicalSortName); |
- } |
- |
- Future<ServiceObject> get(String command) { |
- return isolate.get(id + "/$command"); |
- } |
- |
- String toString() => 'Class($vmName)'; |
-} |
- |
-class Instance extends ServiceObject { |
- @observable Class clazz; |
- @observable int size; |
- @observable String valueAsString; // If primitive. |
- @observable bool valueAsStringIsTruncated; |
- @observable ServiceFunction closureFunc; // If a closure. |
- @observable Context closureCtxt; // If a closure. |
- @observable String name; // If a Type. |
- @observable int length; // If a List. |
- |
- @observable var typeClass; |
- @observable var fields; |
- @observable var nativeFields; |
- @observable var elements; |
- @observable var userName; |
- @observable var referent; // If a MirrorReference. |
- @observable Instance key; // If a WeakProperty. |
- @observable Instance value; // If a WeakProperty. |
- |
- bool get isClosure => closureFunc != null; |
- |
- Instance._empty(ServiceObjectOwner owner) : super._empty(owner); |
- |
- void _update(ObservableMap map, bool mapIsRef) { |
- // Extract full properties. |
- _upgradeCollection(map, isolate); |
- |
- clazz = map['class']; |
- size = map['size']; |
- valueAsString = map['valueAsString']; |
- // Coerce absence to false. |
- valueAsStringIsTruncated = map['valueAsStringIsTruncated'] == true; |
- closureFunc = map['closureFunc']; |
- closureCtxt = map['closureCtxt']; |
- name = map['name']; |
- length = map['length']; |
- |
- if (mapIsRef) { |
- return; |
- } |
- |
- nativeFields = map['nativeFields']; |
- fields = map['fields']; |
- elements = map['elements']; |
- typeClass = map['type_class']; |
- userName = map['user_name']; |
- referent = map['referent']; |
- key = map['key']; |
- value = map['value']; |
- |
- // We are fully loaded. |
- _loaded = true; |
- } |
- |
- String get shortName => valueAsString != null ? valueAsString : 'a ${clazz.name}'; |
- |
- String toString() => 'Instance($shortName)'; |
-} |
- |
- |
-class Context extends ServiceObject { |
- @observable Class clazz; |
- @observable int size; |
- |
- @observable var parentContext; |
- @observable int length; |
- @observable var variables; |
- |
- Context._empty(ServiceObjectOwner owner) : super._empty(owner); |
- |
- void _update(ObservableMap map, bool mapIsRef) { |
- // Extract full properties. |
- _upgradeCollection(map, isolate); |
- |
- size = map['size']; |
- length = map['length']; |
- parentContext = map['parent']; |
- |
- if (mapIsRef) { |
- return; |
- } |
- |
- clazz = map['class']; |
- variables = map['variables']; |
- |
- // We are fully loaded. |
- _loaded = true; |
- } |
- |
- String toString() => 'Context($length)'; |
-} |
- |
- |
-// TODO(koda): Sync this with VM. |
-class FunctionKind { |
- final String _strValue; |
- FunctionKind._internal(this._strValue); |
- toString() => _strValue; |
- bool isFake() => [kCollected, kNative, kTag, kReused].contains(this); |
- |
- static FunctionKind fromJSON(String value) { |
- switch(value) { |
- case 'kRegularFunction': return kRegularFunction; |
- case 'kClosureFunction': return kClosureFunction; |
- case 'kGetterFunction': return kGetterFunction; |
- case 'kSetterFunction': return kSetterFunction; |
- case 'kConstructor': return kConstructor; |
- case 'kImplicitGetter': return kImplicitGetterFunction; |
- case 'kImplicitSetter': return kImplicitSetterFunction; |
- case 'kStaticInitializer': return kStaticInitializer; |
- case 'kMethodExtractor': return kMethodExtractor; |
- case 'kNoSuchMethodDispatcher': return kNoSuchMethodDispatcher; |
- case 'kInvokeFieldDispatcher': return kInvokeFieldDispatcher; |
- case 'Collected': return kCollected; |
- case 'Native': return kNative; |
- case 'Tag': return kTag; |
- case 'Reused': return kReused; |
- } |
- return kUNKNOWN; |
- } |
- |
- static FunctionKind kRegularFunction = new FunctionKind._internal('function'); |
- static FunctionKind kClosureFunction = new FunctionKind._internal('closure function'); |
- static FunctionKind kGetterFunction = new FunctionKind._internal('getter function'); |
- static FunctionKind kSetterFunction = new FunctionKind._internal('setter function'); |
- static FunctionKind kConstructor = new FunctionKind._internal('constructor'); |
- static FunctionKind kImplicitGetterFunction = new FunctionKind._internal('implicit getter function'); |
- static FunctionKind kImplicitSetterFunction = new FunctionKind._internal('implicit setter function'); |
- static FunctionKind kStaticInitializer = new FunctionKind._internal('static initializer'); |
- static FunctionKind kMethodExtractor = new FunctionKind._internal('method extractor'); |
- static FunctionKind kNoSuchMethodDispatcher = new FunctionKind._internal('noSuchMethod dispatcher'); |
- static FunctionKind kInvokeFieldDispatcher = new FunctionKind._internal('invoke field dispatcher'); |
- static FunctionKind kCollected = new FunctionKind._internal('Collected'); |
- static FunctionKind kNative = new FunctionKind._internal('Native'); |
- static FunctionKind kTag = new FunctionKind._internal('Tag'); |
- static FunctionKind kReused = new FunctionKind._internal('Reused'); |
- static FunctionKind kUNKNOWN = new FunctionKind._internal('UNKNOWN'); |
-} |
- |
-class ServiceFunction extends ServiceObject with Coverage { |
- @observable Class owningClass; |
- @observable Library owningLibrary; |
- @observable bool isStatic; |
- @observable bool isConst; |
- @observable ServiceFunction parent; |
- @observable Script script; |
- @observable int tokenPos; |
- @observable int endTokenPos; |
- @observable Code code; |
- @observable Code unoptimizedCode; |
- @observable bool isOptimizable; |
- @observable bool isInlinable; |
- @observable FunctionKind kind; |
- @observable int deoptimizations; |
- @observable String qualifiedName; |
- @observable int usageCounter; |
- @observable bool isDart; |
- |
- ServiceFunction._empty(ServiceObject owner) : super._empty(owner); |
- |
- void _update(ObservableMap map, bool mapIsRef) { |
- name = map['name']; |
- vmName = (map.containsKey('vmName') ? map['vmName'] : name); |
- |
- _upgradeCollection(map, isolate); |
- |
- owningClass = map.containsKey('owningClass') ? map['owningClass'] : null; |
- owningLibrary = map.containsKey('owningLibrary') ? map['owningLibrary'] : null; |
- kind = FunctionKind.fromJSON(map['kind']); |
- isDart = !kind.isFake(); |
- |
- if (mapIsRef) { return; } |
- |
- isStatic = map['static']; |
- isConst = map['const']; |
- parent = map['parent']; |
- script = map['script']; |
- tokenPos = map['tokenPos']; |
- endTokenPos = map['endTokenPos']; |
- code = _convertNull(map['code']); |
- unoptimizedCode = _convertNull(map['unoptimizedCode']); |
- isOptimizable = map['optimizable']; |
- isInlinable = map['inlinable']; |
- deoptimizations = map['deoptimizations']; |
- usageCounter = map['usageCounter']; |
- |
- if (parent == null) { |
- qualifiedName = (owningClass != null) ? |
- "${owningClass.name}.${name}" : |
- name; |
- } else { |
- qualifiedName = "${parent.qualifiedName}.${name}"; |
- } |
- |
- } |
-} |
- |
- |
-class Field extends ServiceObject { |
- @observable var /* Library or Class */ owner; |
- @observable Instance declaredType; |
- @observable bool isStatic; |
- @observable bool isFinal; |
- @observable bool isConst; |
- @observable Instance value; |
- @observable String name; |
- @observable String vmName; |
- |
- @observable bool guardNullable; |
- @observable String guardClass; |
- @observable String guardLength; |
- @observable Script script; |
- @observable int tokenPos; |
- |
- Field._empty(ServiceObjectOwner owner) : super._empty(owner); |
- |
- void _update(ObservableMap map, bool mapIsRef) { |
- // Extract full properties. |
- _upgradeCollection(map, isolate); |
- |
- name = map['name']; |
- vmName = (map.containsKey('vmName') ? map['vmName'] : name); |
- owner = map['owner']; |
- declaredType = map['declaredType']; |
- isStatic = map['static']; |
- isFinal = map['final']; |
- isConst = map['const']; |
- value = map['value']; |
- |
- if (mapIsRef) { |
- return; |
- } |
- |
- guardNullable = map['guardNullable']; |
- guardClass = map['guardClass']; |
- guardLength = map['guardLength']; |
- script = map['script']; |
- tokenPos = map['tokenPos']; |
- |
- _loaded = true; |
- } |
- |
- String toString() => 'Field(${owner.name}.$name)'; |
-} |
- |
- |
-class ScriptLine extends Observable { |
- final Script script; |
- final int line; |
- final String text; |
- @observable int hits; |
- @observable ServiceMap bpt; |
- @observable bool possibleBpt = true; |
- |
- static bool _isTrivialToken(String token) { |
- if (token == 'else') { |
- return true; |
- } |
- for (var c in token.split('')) { |
- switch (c) { |
- case '{': |
- case '}': |
- case '(': |
- case ')': |
- case ';': |
- break; |
- default: |
- return false; |
- } |
- } |
- return true; |
- } |
- |
- static bool _isTrivialLine(String text) { |
- var wsTokens = text.split(new RegExp(r"(\s)+")); |
- for (var wsToken in wsTokens) { |
- var tokens = wsToken.split(new RegExp(r"(\b)")); |
- for (var token in tokens) { |
- if (!_isTrivialToken(token)) { |
- return false; |
- } |
- } |
- } |
- return true; |
- } |
- |
- ScriptLine(this.script, this.line, this.text) { |
- possibleBpt = !_isTrivialLine(text); |
- |
- // TODO(turnidge): This is not so efficient. Consider improving. |
- for (var bpt in this.script.isolate.breakpoints['breakpoints']) { |
- var bptScript = bpt['location']['script']; |
- var bptTokenPos = bpt['location']['tokenPos']; |
- if (bptScript == this.script && |
- bptScript.tokenToLine(bptTokenPos) == line) { |
- this.bpt = bpt; |
- } |
- } |
- } |
-} |
- |
-class Script extends ServiceObject with Coverage { |
- final lines = new ObservableList<ScriptLine>(); |
- final _hits = new Map<int, int>(); |
- @observable String kind; |
- @observable int firstTokenPos; |
- @observable int lastTokenPos; |
- @observable Library owningLibrary; |
- bool get canCache => true; |
- bool get immutable => true; |
- |
- String _shortUrl; |
- String _url; |
- |
- Script._empty(ServiceObjectOwner owner) : super._empty(owner); |
- |
- ScriptLine getLine(int line) { |
- assert(line >= 1); |
- return lines[line - 1]; |
- } |
- |
- /// This function maps a token position to a line number. |
- int tokenToLine(int token) => _tokenToLine[token]; |
- Map _tokenToLine = {}; |
- |
- /// This function maps a token position to a column number. |
- int tokenToCol(int token) => _tokenToCol[token]; |
- Map _tokenToCol = {}; |
- |
- void _update(ObservableMap map, bool mapIsRef) { |
- _upgradeCollection(map, isolate); |
- kind = map['kind']; |
- _url = map['name']; |
- _shortUrl = _url.substring(_url.lastIndexOf('/') + 1); |
- name = _shortUrl; |
- vmName = _url; |
- if (mapIsRef) { |
- return; |
- } |
- _processSource(map['source']); |
- _parseTokenPosTable(map['tokenPosTable']); |
- owningLibrary = map['owningLibrary']; |
- } |
- |
- void _parseTokenPosTable(List<List<int>> table) { |
- if (table == null) { |
- return; |
- } |
- _tokenToLine.clear(); |
- _tokenToCol.clear(); |
- firstTokenPos = null; |
- lastTokenPos = null; |
- var lineSet = new Set(); |
- |
- for (var line in table) { |
- // Each entry begins with a line number... |
- var lineNumber = line[0]; |
- lineSet.add(lineNumber); |
- for (var pos = 1; pos < line.length; pos += 2) { |
- // ...and is followed by (token offset, col number) pairs. |
- var tokenOffset = line[pos]; |
- var colNumber = line[pos+1]; |
- if (firstTokenPos == null) { |
- // Mark first token position. |
- firstTokenPos = tokenOffset; |
- lastTokenPos = tokenOffset; |
- } else { |
- // Keep track of max and min token positions. |
- firstTokenPos = (firstTokenPos <= tokenOffset) ? |
- firstTokenPos : tokenOffset; |
- lastTokenPos = (lastTokenPos >= tokenOffset) ? |
- lastTokenPos : tokenOffset; |
- } |
- _tokenToLine[tokenOffset] = lineNumber; |
- _tokenToCol[tokenOffset] = colNumber; |
- } |
- } |
- |
- for (var line in lines) { |
- // Remove possible breakpoints on lines with no tokens. |
- if (!lineSet.contains(line.line)) { |
- line.possibleBpt = false; |
- } |
- } |
- } |
- |
- void _processHits(List scriptHits) { |
- // Update hits table. |
- for (var i = 0; i < scriptHits.length; i += 2) { |
- var line = scriptHits[i]; |
- var hit = scriptHits[i + 1]; // hit status. |
- assert(line >= 1); // Lines start at 1. |
- var oldHits = _hits[line]; |
- if (oldHits != null) { |
- hit += oldHits; |
- } |
- _hits[line] = hit; |
- } |
- _applyHitsToLines(); |
- } |
- |
- void _processSource(String source) { |
- // Preemptyively mark that this is not loaded. |
- _loaded = false; |
- if (source == null) { |
- return; |
- } |
- var sourceLines = source.split('\n'); |
- if (sourceLines.length == 0) { |
- return; |
- } |
- // We have the source to the script. This is now loaded. |
- _loaded = true; |
- lines.clear(); |
- Logger.root.info('Adding ${sourceLines.length} source lines for ${_url}'); |
- for (var i = 0; i < sourceLines.length; i++) { |
- lines.add(new ScriptLine(this, i + 1, sourceLines[i])); |
- } |
- _applyHitsToLines(); |
- } |
- |
- void _applyHitsToLines() { |
- for (var line in lines) { |
- var hits = _hits[line.line]; |
- line.hits = hits; |
- } |
- } |
-} |
- |
-class CodeTick { |
- final int address; |
- final int exclusiveTicks; |
- final int inclusiveTicks; |
- CodeTick(this.address, this.exclusiveTicks, this.inclusiveTicks); |
-} |
- |
-class PcDescriptor extends Observable { |
- final int address; |
- @reflectable final int deoptId; |
- @reflectable final int tokenPos; |
- @reflectable final int tryIndex; |
- @reflectable final String kind; |
- @observable Script script; |
- @observable String formattedLine; |
- PcDescriptor(this.address, this.deoptId, this.tokenPos, this.tryIndex, |
- this.kind); |
- |
- @reflectable String formattedDeoptId() { |
- if (deoptId == -1) { |
- return 'N/A'; |
- } |
- return deoptId.toString(); |
- } |
- |
- @reflectable String formattedTokenPos() { |
- if (tokenPos == -1) { |
- return ''; |
- } |
- return tokenPos.toString(); |
- } |
- |
- void processScript(Script script) { |
- this.script = null; |
- if (tokenPos == -1) { |
- return; |
- } |
- var line = script.tokenToLine(tokenPos); |
- if (line == null) { |
- return; |
- } |
- this.script = script; |
- var scriptLine = script.getLine(line); |
- formattedLine = scriptLine.text; |
- } |
-} |
- |
-class PcDescriptors extends ServiceObject { |
- @observable Class clazz; |
- @observable int size; |
- bool get canCache => false; |
- bool get immutable => true; |
- @reflectable final List<PcDescriptor> descriptors = |
- new ObservableList<PcDescriptor>(); |
- |
- PcDescriptors._empty(ServiceObjectOwner owner) : super._empty(owner) { |
- print('created PcDescriptors.'); |
- } |
- |
- void _update(ObservableMap m, bool mapIsRef) { |
- if (mapIsRef) { |
- return; |
- } |
- _upgradeCollection(m, isolate); |
- clazz = m['class']; |
- size = m['size']; |
- descriptors.clear(); |
- for (var descriptor in m['members']) { |
- var address = int.parse(descriptor['pc'], radix:16); |
- var deoptId = descriptor['deoptId']; |
- var tokenPos = descriptor['tokenPos']; |
- var tryIndex = descriptor['tryIndex']; |
- var kind = descriptor['kind'].trim(); |
- descriptors.add( |
- new PcDescriptor(address, deoptId, tokenPos, tryIndex, kind)); |
- } |
- } |
-} |
- |
-class LocalVarDescriptor extends Observable { |
- @reflectable final String name; |
- @reflectable final int index; |
- @reflectable final int beginPos; |
- @reflectable final int endPos; |
- @reflectable final int scopeId; |
- @reflectable final String kind; |
- |
- LocalVarDescriptor(this.name, this.index, this.beginPos, this.endPos, |
- this.scopeId, this.kind); |
-} |
- |
-class LocalVarDescriptors extends ServiceObject { |
- @observable Class clazz; |
- @observable int size; |
- bool get canCache => false; |
- bool get immutable => true; |
- @reflectable final List<LocalVarDescriptor> descriptors = |
- new ObservableList<LocalVarDescriptor>(); |
- LocalVarDescriptors._empty(ServiceObjectOwner owner) : super._empty(owner); |
- |
- void _update(ObservableMap m, bool mapIsRef) { |
- if (mapIsRef) { |
- return; |
- } |
- _upgradeCollection(m, isolate); |
- clazz = m['class']; |
- size = m['size']; |
- descriptors.clear(); |
- for (var descriptor in m['members']) { |
- var name = descriptor['name']; |
- var index = descriptor['index']; |
- var beginPos = descriptor['beginPos']; |
- var endPos = descriptor['endPos']; |
- var scopeId = descriptor['scopeId']; |
- var kind = descriptor['kind'].trim(); |
- descriptors.add( |
- new LocalVarDescriptor(name, index, beginPos, endPos, scopeId, kind)); |
- } |
- } |
-} |
- |
-class TokenStream extends ServiceObject { |
- @observable Class clazz; |
- @observable int size; |
- bool get canCache => false; |
- bool get immutable => true; |
- |
- @observable String privateKey; |
- |
- TokenStream._empty(ServiceObjectOwner owner) : super._empty(owner); |
- |
- void _update(ObservableMap m, bool mapIsRef) { |
- if (mapIsRef) { |
- return; |
- } |
- _upgradeCollection(m, isolate); |
- clazz = m['class']; |
- size = m['size']; |
- privateKey = m['privateKey']; |
- } |
-} |
- |
-class CodeInstruction extends Observable { |
- @observable final int address; |
- @observable final String machine; |
- @observable final String human; |
- @observable CodeInstruction jumpTarget; |
- @reflectable List<PcDescriptor> descriptors = |
- new ObservableList<PcDescriptor>(); |
- |
- static String formatPercent(num a, num total) { |
- var percent = 100.0 * (a / total); |
- return '${percent.toStringAsFixed(2)}%'; |
- } |
- |
- CodeInstruction(this.address, this.machine, this.human); |
- |
- @reflectable bool get isComment => address == 0; |
- @reflectable bool get hasDescriptors => descriptors.length > 0; |
- |
- @reflectable String formattedAddress() { |
- if (address == 0) { |
- return ''; |
- } |
- return '0x${address.toRadixString(16)}'; |
- } |
- |
- @reflectable String formattedInclusive(Code code) { |
- if (code == null) { |
- return ''; |
- } |
- var tick = code.addressTicks[address]; |
- if (tick == null) { |
- return ''; |
- } |
- // Don't show inclusive ticks if they are the same as exclusive ticks. |
- if (tick.inclusiveTicks == tick.exclusiveTicks) { |
- return ''; |
- } |
- var pcent = formatPercent(tick.inclusiveTicks, code.totalSamplesInProfile); |
- return '$pcent (${tick.inclusiveTicks})'; |
- } |
- |
- @reflectable String formattedExclusive(Code code) { |
- if (code == null) { |
- return ''; |
- } |
- var tick = code.addressTicks[address]; |
- if (tick == null) { |
- return ''; |
- } |
- var pcent = formatPercent(tick.exclusiveTicks, code.totalSamplesInProfile); |
- return '$pcent (${tick.exclusiveTicks})'; |
- } |
- |
- bool _isJumpInstruction() { |
- return human.startsWith('j'); |
- } |
- |
- int _getJumpAddress() { |
- assert(_isJumpInstruction()); |
- var chunks = human.split(' '); |
- if (chunks.length != 2) { |
- // We expect jump instructions to be of the form 'j.. address'. |
- return 0; |
- } |
- var address = chunks[1]; |
- if (address.startsWith('0x')) { |
- // Chop off the 0x. |
- address = address.substring(2); |
- } |
- try { |
- return int.parse(address, radix:16); |
- } catch (_) { |
- return 0; |
- } |
- } |
- |
- void _resolveJumpTarget(List<CodeInstruction> instructions) { |
- if (!_isJumpInstruction()) { |
- return; |
- } |
- int address = _getJumpAddress(); |
- if (address == 0) { |
- // Could not determine jump address. |
- Logger.root.severe('Could not determine jump address for $human'); |
- return; |
- } |
- for (var i = 0; i < instructions.length; i++) { |
- var instruction = instructions[i]; |
- if (instruction.address == address) { |
- jumpTarget = instruction; |
- return; |
- } |
- } |
- Logger.root.severe( |
- 'Could not find instruction at ${address.toRadixString(16)}'); |
- } |
-} |
- |
-class CodeKind { |
- final _value; |
- const CodeKind._internal(this._value); |
- String toString() => '$_value'; |
- |
- static CodeKind fromString(String s) { |
- if (s == 'Native') { |
- return Native; |
- } else if (s == 'Dart') { |
- return Dart; |
- } else if (s == 'Collected') { |
- return Collected; |
- } else if (s == 'Reused') { |
- return Reused; |
- } else if (s == 'Tag') { |
- return Tag; |
- } |
- Logger.root.warning('Unknown code kind $s'); |
- throw new FallThroughError(); |
- } |
- static const Native = const CodeKind._internal('Native'); |
- static const Dart = const CodeKind._internal('Dart'); |
- static const Collected = const CodeKind._internal('Collected'); |
- static const Reused = const CodeKind._internal('Reused'); |
- static const Tag = const CodeKind._internal('Tag'); |
-} |
- |
-class CodeCallCount { |
- final Code code; |
- final int count; |
- CodeCallCount(this.code, this.count); |
-} |
- |
-class CodeTrieNode { |
- final Code code; |
- final int count; |
- final children = new List<CodeTrieNode>(); |
- int summedChildCount = 0; |
- CodeTrieNode(this.code, this.count); |
-} |
- |
-class Code extends ServiceObject { |
- @observable CodeKind kind; |
- @observable int totalSamplesInProfile = 0; |
- @reflectable int exclusiveTicks = 0; |
- @reflectable int inclusiveTicks = 0; |
- @reflectable int startAddress = 0; |
- @reflectable int endAddress = 0; |
- @reflectable final callers = new List<CodeCallCount>(); |
- @reflectable final callees = new List<CodeCallCount>(); |
- @reflectable final instructions = new ObservableList<CodeInstruction>(); |
- @reflectable final addressTicks = new ObservableMap<int, CodeTick>(); |
- @observable String formattedInclusiveTicks = ''; |
- @observable String formattedExclusiveTicks = ''; |
- @observable Instance objectPool; |
- @observable ServiceFunction function; |
- @observable Script script; |
- @observable bool isOptimized = false; |
- |
- bool get canCache => true; |
- bool get immutable => true; |
- |
- Code._empty(ServiceObjectOwner owner) : super._empty(owner); |
- |
- // Reset all data associated with a profile. |
- void resetProfileData() { |
- totalSamplesInProfile = 0; |
- exclusiveTicks = 0; |
- inclusiveTicks = 0; |
- formattedInclusiveTicks = ''; |
- formattedExclusiveTicks = ''; |
- callers.clear(); |
- callees.clear(); |
- addressTicks.clear(); |
- } |
- |
- void _updateDescriptors(Script script) { |
- this.script = script; |
- for (var instruction in instructions) { |
- for (var descriptor in instruction.descriptors) { |
- descriptor.processScript(script); |
- } |
- } |
- } |
- |
- void loadScript() { |
- if (script != null) { |
- // Already done. |
- return; |
- } |
- if (kind != CodeKind.Dart){ |
- return; |
- } |
- if (function == null) { |
- return; |
- } |
- if (function.script == null) { |
- // Attempt to load the function. |
- function.load().then((func) { |
- var script = function.script; |
- if (script == null) { |
- // Function doesn't have an associated script. |
- return; |
- } |
- // Load the script and then update descriptors. |
- script.load().then(_updateDescriptors); |
- }); |
- return; |
- } |
- // Load the script and then update descriptors. |
- function.script.load().then(_updateDescriptors); |
- } |
- |
- /// Reload [this]. Returns a future which completes to [this] or |
- /// a [ServiceError]. |
- Future<ServiceObject> reload() { |
- assert(kind != null); |
- if (kind == CodeKind.Dart) { |
- // We only reload Dart code. |
- return super.reload(); |
- } |
- return new Future.value(this); |
- } |
- |
- void _resolveCalls(List<CodeCallCount> calls, List data, List<Code> codes) { |
- // Assert that this has been cleared. |
- assert(calls.length == 0); |
- // Resolve. |
- for (var i = 0; i < data.length; i += 2) { |
- var index = int.parse(data[i]); |
- var count = int.parse(data[i + 1]); |
- assert(index >= 0); |
- assert(index < codes.length); |
- calls.add(new CodeCallCount(codes[index], count)); |
- } |
- // Sort to descending count order. |
- calls.sort((a, b) => b.count - a.count); |
- } |
- |
- |
- static String formatPercent(num a, num total) { |
- var percent = 100.0 * (a / total); |
- return '${percent.toStringAsFixed(2)}%'; |
- } |
- |
- void updateProfileData(Map profileData, |
- List<Code> codeTable, |
- int sampleCount) { |
- // Assert we have a CodeRegion entry. |
- assert(profileData['type'] == 'CodeRegion'); |
- // Assert we are handed profile data for this code object. |
- assert(profileData['code'] == this); |
- totalSamplesInProfile = sampleCount; |
- inclusiveTicks = int.parse(profileData['inclusive_ticks']); |
- exclusiveTicks = int.parse(profileData['exclusive_ticks']); |
- _resolveCalls(callers, profileData['callers'], codeTable); |
- _resolveCalls(callees, profileData['callees'], codeTable); |
- var ticks = profileData['ticks']; |
- if (ticks != null) { |
- _processTicks(ticks); |
- } |
- formattedInclusiveTicks = |
- '${formatPercent(inclusiveTicks, totalSamplesInProfile)} ' |
- '($inclusiveTicks)'; |
- formattedExclusiveTicks = |
- '${formatPercent(exclusiveTicks, totalSamplesInProfile)} ' |
- '($exclusiveTicks)'; |
- } |
- |
- void _update(ObservableMap m, bool mapIsRef) { |
- name = m['name']; |
- vmName = (m.containsKey('vmName') ? m['vmName'] : name); |
- isOptimized = m['optimized'] != null ? m['optimized'] : false; |
- kind = CodeKind.fromString(m['kind']); |
- startAddress = int.parse(m['start'], radix:16); |
- endAddress = int.parse(m['end'], radix:16); |
- function = isolate.getFromMap(m['function']); |
- objectPool = isolate.getFromMap(m['objectPool']); |
- var disassembly = m['disassembly']; |
- if (disassembly != null) { |
- _processDisassembly(disassembly); |
- } |
- var descriptors = m['descriptors']; |
- if (descriptors != null) { |
- descriptors = descriptors['members']; |
- _processDescriptors(descriptors); |
- } |
- // We are loaded if we have instructions or are not Dart code. |
- _loaded = (instructions.length != 0) || (kind != CodeKind.Dart); |
- hasDisassembly = (instructions.length != 0) && (kind == CodeKind.Dart); |
- } |
- |
- @observable bool hasDisassembly = false; |
- |
- void _processDisassembly(List<String> disassembly){ |
- assert(disassembly != null); |
- instructions.clear(); |
- assert((disassembly.length % 3) == 0); |
- for (var i = 0; i < disassembly.length; i += 3) { |
- var address = 0; // Assume code comment. |
- var machine = disassembly[i + 1]; |
- var human = disassembly[i + 2]; |
- if (disassembly[i] != '') { |
- // Not a code comment, extract address. |
- address = int.parse(disassembly[i]); |
- } |
- var instruction = new CodeInstruction(address, machine, human); |
- instructions.add(instruction); |
- } |
- for (var instruction in instructions) { |
- instruction._resolveJumpTarget(instructions); |
- } |
- } |
- |
- void _processDescriptor(Map d) { |
- var address = int.parse(d['pc'], radix:16); |
- var deoptId = d['deoptId']; |
- var tokenPos = d['tokenPos']; |
- var tryIndex = d['tryIndex']; |
- var kind = d['kind'].trim(); |
- for (var instruction in instructions) { |
- if (instruction.address == address) { |
- instruction.descriptors.add(new PcDescriptor(address, |
- deoptId, |
- tokenPos, |
- tryIndex, |
- kind)); |
- return; |
- } |
- } |
- Logger.root.warning( |
- 'Could not find instruction with pc descriptor address: $address'); |
- } |
- |
- void _processDescriptors(List<Map> descriptors) { |
- for (Map descriptor in descriptors) { |
- _processDescriptor(descriptor); |
- } |
- } |
- |
- void _processTicks(List<String> profileTicks) { |
- assert(profileTicks != null); |
- assert((profileTicks.length % 3) == 0); |
- for (var i = 0; i < profileTicks.length; i += 3) { |
- var address = int.parse(profileTicks[i], radix:16); |
- var exclusive = int.parse(profileTicks[i + 1]); |
- var inclusive = int.parse(profileTicks[i + 2]); |
- var tick = new CodeTick(address, exclusive, inclusive); |
- addressTicks[address] = tick; |
- } |
- } |
- |
- /// Returns true if [address] is contained inside [this]. |
- bool contains(int address) { |
- return (address >= startAddress) && (address < endAddress); |
- } |
- |
- /// Sum all caller counts. |
- int sumCallersCount() => _sumCallCount(callers); |
- /// Specific caller count. |
- int callersCount(Code code) => _callCount(callers, code); |
- /// Sum of callees count. |
- int sumCalleesCount() => _sumCallCount(callees); |
- /// Specific callee count. |
- int calleesCount(Code code) => _callCount(callees, code); |
- |
- int _sumCallCount(List<CodeCallCount> calls) { |
- var sum = 0; |
- for (CodeCallCount caller in calls) { |
- sum += caller.count; |
- } |
- return sum; |
- } |
- |
- int _callCount(List<CodeCallCount> calls, Code code) { |
- for (CodeCallCount caller in calls) { |
- if (caller.code == code) { |
- return caller.count; |
- } |
- } |
- return 0; |
- } |
- |
- @reflectable bool get isDartCode => kind == CodeKind.Dart; |
-} |
- |
- |
-class SocketKind { |
- final _value; |
- const SocketKind._internal(this._value); |
- String toString() => '$_value'; |
- |
- static SocketKind fromString(String s) { |
- if (s == 'Listening') { |
- return Listening; |
- } else if (s == 'Normal') { |
- return Normal; |
- } else if (s == 'Pipe') { |
- return Pipe; |
- } else if (s == 'Internal') { |
- return Internal; |
- } |
- Logger.root.warning('Unknown socket kind $s'); |
- throw new FallThroughError(); |
- } |
- static const Listening = const SocketKind._internal('Listening'); |
- static const Normal = const SocketKind._internal('Normal'); |
- static const Pipe = const SocketKind._internal('Pipe'); |
- static const Internal = const SocketKind._internal('Internal'); |
-} |
- |
-/// A snapshot of statistics associated with a [Socket]. |
-class SocketStats { |
- @reflectable final int bytesRead; |
- @reflectable final int bytesWritten; |
- @reflectable final int readCalls; |
- @reflectable final int writeCalls; |
- @reflectable final int available; |
- |
- SocketStats(this.bytesRead, this.bytesWritten, |
- this.readCalls, this.writeCalls, |
- this.available); |
-} |
- |
-/// A peer to a Socket in dart:io. Sockets can represent network sockets or |
-/// OS pipes. Each socket is owned by another ServceObject, for example, |
-/// a process or an HTTP server. |
-class Socket extends ServiceObject { |
- Socket._empty(ServiceObjectOwner owner) : super._empty(owner); |
- |
- bool get canCache => true; |
- |
- ServiceObject socketOwner; |
- |
- @reflectable bool get isPipe => (kind == SocketKind.Pipe); |
- |
- @observable SocketStats latest; |
- @observable SocketStats previous; |
- |
- @observable SocketKind kind; |
- |
- @observable String protocol = ''; |
- |
- @observable bool readClosed = false; |
- @observable bool writeClosed = false; |
- @observable bool closing = false; |
- |
- /// Listening for connections. |
- @observable bool listening = false; |
- |
- @observable int fd; |
- |
- @observable String localAddress; |
- @observable int localPort; |
- @observable String remoteAddress; |
- @observable int remotePort; |
- |
- // Updates internal state from [map]. [map] can be a reference. |
- void _update(ObservableMap map, bool mapIsRef) { |
- name = map['name']; |
- vmName = map['name']; |
- |
- kind = SocketKind.fromString(map['kind']); |
- |
- if (mapIsRef) { |
- return; |
- } |
- |
- _loaded = true; |
- |
- _upgradeCollection(map, isolate); |
- |
- readClosed = map['readClosed']; |
- writeClosed = map['writeClosed']; |
- closing = map['closing']; |
- listening = map['listening']; |
- |
- protocol = map['protocol']; |
- |
- localAddress = map['localAddress']; |
- localPort = map['localPort']; |
- remoteAddress = map['remoteAddress']; |
- remotePort = map['remotePort']; |
- |
- fd = map['fd']; |
- socketOwner = map['owner']; |
- } |
-} |
- |
-class MetricSample { |
- final double value; |
- final DateTime time; |
- MetricSample(this.value) : time = new DateTime.now(); |
-} |
- |
-class ServiceMetric extends ServiceObject { |
- ServiceMetric._empty(ServiceObjectOwner owner) : super._empty(owner) { |
- } |
- |
- bool get canCache => true; |
- bool get immutable => false; |
- |
- @observable bool recording = false; |
- MetricPoller poller; |
- |
- final ObservableList<MetricSample> samples = |
- new ObservableList<MetricSample>(); |
- int _sampleBufferSize = 100; |
- int get sampleBufferSize => _sampleBufferSize; |
- set sampleBufferSize(int size) { |
- _sampleBufferSize = size; |
- _removeOld(); |
- } |
- |
- void addSample(MetricSample sample) { |
- samples.add(sample); |
- _removeOld(); |
- } |
- |
- void _removeOld() { |
- // TODO(johnmccutchan): If this becomes hot, consider using a circular |
- // buffer. |
- if (samples.length > _sampleBufferSize) { |
- int count = samples.length - _sampleBufferSize; |
- samples.removeRange(0, count); |
- } |
- } |
- |
- @observable String description; |
- @observable double value = 0.0; |
- // Only a guage has a non-null min and max. |
- @observable double min; |
- @observable double max; |
- |
- bool get isGauge => (min != null) && (max != null); |
- |
- void _update(ObservableMap map, bool mapIsRef) { |
- name = map['name']; |
- description = map['description']; |
- vmName = map['name']; |
- value = map['value']; |
- min = map['min']; |
- max = map['max']; |
- } |
- |
- String toString() => "ServiceMetric($_id)"; |
-} |
- |
-class MetricPoller { |
- // Metrics to be polled. |
- final List<ServiceMetric> metrics = new List<ServiceMetric>(); |
- final Duration pollPeriod; |
- Timer _pollTimer; |
- |
- MetricPoller(int milliseconds) : |
- pollPeriod = new Duration(milliseconds: milliseconds) { |
- start(); |
- } |
- |
- void start() { |
- _pollTimer = new Timer.periodic(pollPeriod, _onPoll); |
- } |
- |
- void cancel() { |
- if (_pollTimer != null) { |
- _pollTimer.cancel(); |
- } |
- _pollTimer = null; |
- } |
- |
- void _onPoll(_) { |
- // Reload metrics and add a sample to each. |
- for (var metric in metrics) { |
- metric.reload().then((m) { |
- m.addSample(new MetricSample(m.value)); |
- }); |
- } |
- } |
-} |
- |
-// Convert any ServiceMaps representing a null instance into an actual null. |
-_convertNull(obj) { |
- if (obj.isNull) { |
- return null; |
- } |
- return obj; |
-} |
- |
-// Returns true if [map] is a service map. i.e. it has the following keys: |
-// 'id' and a 'type'. |
-bool _isServiceMap(ObservableMap m) { |
- return (m != null) && (m['id'] != null) && (m['type'] != null); |
-} |
- |
-bool _hasRef(String type) => type.startsWith('@'); |
-String _stripRef(String type) => (_hasRef(type) ? type.substring(1) : type); |
- |
-/// Recursively upgrades all [ServiceObject]s inside [collection] which must |
-/// be an [ObservableMap] or an [ObservableList]. Upgraded elements will be |
-/// associated with [vm] and [isolate]. |
-void _upgradeCollection(collection, ServiceObjectOwner owner) { |
- if (collection is ServiceMap) { |
- return; |
- } |
- if (collection is ObservableMap) { |
- _upgradeObservableMap(collection, owner); |
- } else if (collection is ObservableList) { |
- _upgradeObservableList(collection, owner); |
- } |
-} |
- |
-void _upgradeObservableMap(ObservableMap map, ServiceObjectOwner owner) { |
- map.forEach((k, v) { |
- if ((v is ObservableMap) && _isServiceMap(v)) { |
- map[k] = owner.getFromMap(v); |
- } else if (v is ObservableList) { |
- _upgradeObservableList(v, owner); |
- } else if (v is ObservableMap) { |
- _upgradeObservableMap(v, owner); |
- } |
- }); |
-} |
- |
-void _upgradeObservableList(ObservableList list, ServiceObjectOwner owner) { |
- for (var i = 0; i < list.length; i++) { |
- var v = list[i]; |
- if ((v is ObservableMap) && _isServiceMap(v)) { |
- list[i] = owner.getFromMap(v); |
- } else if (v is ObservableList) { |
- _upgradeObservableList(v, owner); |
- } else if (v is ObservableMap) { |
- _upgradeObservableMap(v, owner); |
- } |
- } |
-} |