Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(117)

Side by Side Diff: runtime/bin/vmservice/client/lib/src/service/object.dart

Issue 204983008: Rework how ServiceObjects are created and cached in Observatory. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: gen js / fix 1 bug Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 part of service; 5 part of service;
6 6
7 /// A [ServiceObject] is an object known to the VM service and is tied 7 /// A [ServiceObject] is an object known to the VM service and is tied
8 /// to an owning [Isolate]. 8 /// to an owning [Isolate].
9 abstract class ServiceObject extends Observable { 9 abstract class ServiceObject extends Observable {
10 /// The owner of this [ServiceObject]. This can be an [Isolate], a 10 /// The owner of this [ServiceObject]. This can be an [Isolate], a
11 /// [VM], or null. 11 /// [VM], or null.
12 @reflectable ServiceObject get owner => _owner; 12 @reflectable ServiceObjectOwner get owner => _owner;
13 ServiceObject _owner; 13 ServiceObjectOwner _owner;
14 14
15 /// The [VM] which owns this [ServiceObject]. 15 /// The [VM] which owns this [ServiceObject].
16 @reflectable VM get vm { 16 @reflectable VM get vm => _owner.vm;
17 if (owner == null) {
18 assert(this is VM);
19 return this;
20 } else if (owner is VM) {
21 return owner;
22 } else {
23 assert(owner.owner is VM);
24 return owner.owner;
25 }
26 }
27 17
28 /// The [Isolate] which owns this [ServiceObject]. May be null. 18 /// The [Isolate] which owns this [ServiceObject]. May be null.
29 @reflectable Isolate get isolate { 19 @reflectable Isolate get isolate => _owner.isolate;
30 if (owner == null) {
31 return null;
32 } else if (this is Isolate) {
33 return this;
34 } else {
35 assert(owner is Isolate);
36 return owner;
37 }
38 }
39 20
40 /// The id of this object. 21 /// The id of this object.
41 @reflectable String get id => _id; 22 @reflectable String get id => _id;
42 String _id; 23 String _id;
43 24
44 /// The service type of this object. 25 /// The service type of this object.
45 @reflectable String get serviceType => _serviceType; 26 @reflectable String get serviceType => _serviceType;
46 String _serviceType; 27 String _serviceType;
47 28
48 /// The complete service url of this object. 29 /// The complete service url of this object.
49 @reflectable String get link => isolate.relativeLink(_id); 30 @reflectable String get link => isolate.relativeLink(_id);
50 31
51 /// The complete service url of this object with a '#/' prefix. 32 /// The complete service url of this object with a '#/' prefix.
52 @reflectable String get hashLink => '#/${link}'; 33 @reflectable String get hashLink => '#/${link}';
53 set hashLink(var o) { /* silence polymer */ } 34 set hashLink(var o) { /* silence polymer */ }
54 35
55 /// Returns true if [this] has only been partially initialized via 36 /// Has this object been fully loaded?
56 /// a reference. See [load]. 37 bool get loaded => _loaded;
57 bool isRef() => _ref; 38 bool _loaded = false;
58 bool _ref; 39
40 /// Is this object cacheable? That is, is it impossible for the [id]
41 /// of this object to change?
42 bool get canCache => false;
43
44 /// Is this object immutable after it is [loaded]?
45 bool get immutable => false;
59 46
60 @observable String name; 47 @observable String name;
61 @observable String vmName; 48 @observable String vmName;
62 @observable String mainPort;
63 49
64 ServiceObject(this._owner, this._id, this._serviceType) { 50 /// Creates an empty [ServiceObject].
65 _ref = isRefType(_serviceType); 51 ServiceObject._empty(this._owner);
66 _serviceType = stripRef(_serviceType);
67 _created();
68 }
69 52
70 ServiceObject.fromMap(this._owner, ObservableMap map) { 53 /// Creates a [ServiceObject] initialized from [map].
71 assert(isServiceMap(map)); 54 factory ServiceObject._fromMap(ServiceObjectOwner owner,
72 _id = map['id']; 55 ObservableMap map) {
73 _ref = isRefType(map['type']); 56 if (!_isServiceMap(map)) {
74 _serviceType = stripRef(map['type']); 57 Logger.root.severe('Malformed service object: $map');
75 update(map); 58 }
76 _created(); 59 assert(_isServiceMap(map));
60 var type = _stripRef(map['type']);
61 var obj = null;
62 assert(type != 'VM');
63 switch (type) {
64 case 'Code':
65 obj = new Code._empty(owner);
66 break;
67 case 'Error':
68 obj = new ServiceError._empty(owner);
69 break;
70 case 'Isolate':
71 obj = new Isolate._empty(owner);
72 break;
73 case 'Script':
74 obj = new Script._empty(owner);
75 break;
76 default:
77 obj = new ServiceMap._empty(owner);
78 }
79 obj.update(map);
80 return obj;
77 } 81 }
78 82
79 /// If [this] was created from a reference, load the full object 83 /// If [this] was created from a reference, load the full object
80 /// from the service by calling [reload]. Else, return [this]. 84 /// from the service by calling [reload]. Else, return [this].
81 Future<ServiceObject> load() { 85 Future<ServiceObject> load() {
82 if (!_ref) { 86 if (loaded) {
83 // Not a reference.
84 return new Future.value(this); 87 return new Future.value(this);
85 } 88 }
86 // Call reload which will fill in the entire object. 89 // Call reload which will fill in the entire object.
87 return reload(); 90 return reload();
88 } 91 }
89 92
90 /// Reload [this]. Returns a future which completes to [this] or 93 /// Reload [this]. Returns a future which completes to [this] or
91 /// a [ServiceError]. 94 /// a [ServiceError].
92 Future<ServiceObject> reload() { 95 Future<ServiceObject> reload() {
93 if (id == '') { 96 if (id == '') {
94 // Errors don't have ids. 97 // Errors don't have ids.
95 assert(serviceType == 'Error'); 98 assert(serviceType == 'Error');
96 return new Future.value(this); 99 return new Future.value(this);
97 } 100 }
98 return vm.getAsMap(link).then(update); 101 if (loaded && immutable) {
102 return new Future.value(this);
103 }
104 return vm.getAsMap(link).then((ObservableMap map) {
105 var mapType = _stripRef(map['type']);
106 if (mapType != _serviceType) {
107 // If the type changes, return a new object instead of
108 // updating the existing one.
109 assert(mapType == 'Error' || mapType == 'Null');
110 return new ServiceObject._fromMap(owner, map);
111 }
112 update(map);
113 return this;
114 });
99 } 115 }
100 116
101 /// Update [this] using [m] as a source. [m] can be a reference. 117 /// Update [this] using [map] as a source. [map] can be a reference.
102 ServiceObject update(ObservableMap m) { 118 void update(ObservableMap map) {
103 // Assert that m is a service map. 119 assert(_isServiceMap(map));
104 assert(ServiceObject.isServiceMap(m)); 120
105 if ((m['type'] == 'Error') && (_serviceType != 'Error')) { 121 // Don't allow the type to change on an object update.
106 // Got an unexpected error. Don't update the object. 122 // TODO(turnidge): Make this a ServiceError?
107 return _upgradeToServiceObject(vm, isolate, m); 123 var mapIsRef = _hasRef(map['type']);
124 var mapType = _stripRef(map['type']);
125 assert(_serviceType == null || _serviceType == mapType);
126
127 if (_id != null && _id != map['id']) {
128 // It is only safe to change an id when the object isn't cacheable.
129 assert(!canCache);
108 } 130 }
109 // TODO(johnmccutchan): Should we allow for a ServiceObject's id 131 _id = map['id'];
110 // or type to change? 132
111 _id = m['id']; 133 _serviceType = mapType;
112 _serviceType = stripRef(m['type']); 134 _update(map, mapIsRef);
113 _update(m);
114 return this;
115 } 135 }
116 136
117 // update internal state from [map]. [map] can be a reference. 137 // Updates internal state from [map]. [map] can be a reference.
118 void _update(ObservableMap map); 138 void _update(ObservableMap map, bool mapIsRef);
139 }
119 140
120 void _created() { 141 abstract class ServiceObjectOwner extends ServiceObject {
121 var refNotice = _ref ? ' Created from reference.' : ''; 142 /// Creates an empty [ServiceObjectOwner].
122 Logger.root.info('Created ServiceObject for \'${_id}\' with type ' 143 ServiceObjectOwner._empty(ServiceObjectOwner owner) : super._empty(owner);
123 '\'${_serviceType}\'.' + refNotice);
124 }
125 144
126 // ------------------------------------------------------ 145 /// Builds a [ServiceObject] corresponding to the [id] from [map].
127 146 /// The result may come from the cache. The result will not necessarily
128 /// Returns true if [map] is a service map. i.e. it has the following keys: 147 /// be [loaded].
129 /// 'id' and a 'type'. 148 ServiceObject getFromMap(ObservableMap map);
130 static bool isServiceMap(ObservableMap m) {
131 return (m != null) && (m['id'] != null) && (m['type'] != null);
132 }
133
134 /// Returns true if [type] is a reference type. i.e. it begins with an
135 /// '@' character.
136 static bool isRefType(String type) {
137 return type.startsWith('@');
138 }
139
140 /// Returns the unreffed version of [type].
141 static String stripRef(String type) {
142 if (!isRefType(type)) {
143 return type;
144 }
145 // Strip off the '@' character.
146 return type.substring(1);
147 }
148 } 149 }
149 150
150 /// State for a VM being inspected. 151 /// State for a VM being inspected.
151 abstract class VM extends ServiceObject { 152 abstract class VM extends ServiceObjectOwner {
152 @reflectable IsolateList _isolates; 153 @reflectable VM get vm => this;
153 @reflectable IsolateList get isolates => _isolates; 154 @reflectable Isolate get isolate => null;
154 155
155 @observable List<Isolate> allIsolates = toObservable([]); 156 @reflectable Iterable<Isolate> get isolates => _isolateCache.values;
156 157
157 @reflectable String get link => "$id"; 158 @reflectable String get link => '$id';
158 159
159 @observable String version = 'unknown'; 160 @observable String version = 'unknown';
160 @observable String architecture = 'unknown'; 161 @observable String architecture = 'unknown';
161 @observable double uptime = 0.0; 162 @observable double uptime = 0.0;
162 163
163 void _initOnce() { 164 VM() : super._empty(null) {
164 assert(_isolates == null); 165 name = 'vm';
165 _isolates = new IsolateList(this); 166 vmName = 'vm';
166 name = "vm"; 167 _cache['vm'] = this;
167 vmName = "vm"; 168 update(toObservable({'id':'vm', 'type':'@VM'}));
168 }
169
170 VM() : super(null, "vm", "VM") {
171 _initOnce();
172 } 169 }
173 170
174 static final RegExp _currentIsolateMatcher = new RegExp(r'isolates/\d+'); 171 static final RegExp _currentIsolateMatcher = new RegExp(r'isolates/\d+');
175 static final RegExp _currentObjectMatcher = new RegExp(r'isolates/\d+(/|$)'); 172 static final RegExp _currentObjectMatcher = new RegExp(r'isolates/\d+(/|$)');
173 static final String _isolatesPrefix = 'isolates/';
176 174
177 String _parseObjectId(String id) { 175 String _parseObjectId(String id) {
178 Match m = _currentObjectMatcher.matchAsPrefix(id); 176 Match m = _currentObjectMatcher.matchAsPrefix(id);
179 if (m == null) { 177 if (m == null) {
180 return null; 178 return null;
181 } 179 }
182 return m.input.substring(m.end); 180 return m.input.substring(m.end);
183 } 181 }
184 182
185 String _parseIsolateId(String id) { 183 String _parseIsolateId(String id) {
186 Match m = _currentIsolateMatcher.matchAsPrefix(id); 184 Match m = _currentIsolateMatcher.matchAsPrefix(id);
187 if (m == null) { 185 if (m == null) {
188 return ''; 186 return '';
189 } 187 }
190 return id.substring(0, m.end); 188 return id.substring(0, m.end);
191 } 189 }
192 190
193 Future<ServiceObject> getDirect(String id) { 191 Map<String,ServiceObject> _cache = new Map<String,ServiceObject>();
194 return vm.getAsMap(id).then((ObservableMap m) { 192 Map<String,Isolate> _isolateCache = new Map<String,Isolate>();
195 return _upgradeToServiceObject(vm, null, m); 193
196 }); 194 ServiceObject getFromMap(ObservableMap map) {
195 throw new UnimplementedError();
196 }
197
198 Future<ServiceObject> _getIsolate(String isolateId) {
199 if (isolateId == '') {
200 return new Future.value(null);
201 }
202 Isolate isolate = _isolateCache[isolateId];
203 if (isolate != null) {
204 return new Future.value(isolate);
205 }
206 // The isolate is not in the cache. Reload the vm and see if the
207 // requested isolate is found.
208 return reload().then((result) {
209 if (result is! VM) {
210 return null;
211 }
212 assert(result == this);
213 return _isolateCache[isolateId];
214 });
197 } 215 }
198 216
199 Future<ServiceObject> get(String id) { 217 Future<ServiceObject> get(String id) {
200 if (id.startsWith('isolates/')) { 218 // Isolates are handled specially, since they can cache sub-objects.
219 if (id.startsWith(_isolatesPrefix)) {
201 String isolateId = _parseIsolateId(id); 220 String isolateId = _parseIsolateId(id);
202 if (isolateId == '') { 221 String objectId = _parseObjectId(id);
203 return reload(); 222 return _getIsolate(isolateId).then((isolate) {
204 } else { 223 if (isolate == null) {
205 Isolate isolate = _isolates.getIsolate(isolateId); 224 // The isolate does not exist. Return the VM object instead.
206 if (isolate == null) { 225 //
207 // TODO(turnidge): Isolate not found error. 226 // TODO(turnidge): Generate a service error?
208 return reload(); 227 return this;
209 } else { 228 }
210 String objectId = _parseObjectId(id);
211 if (objectId == null) { 229 if (objectId == null) {
212 return isolate.reload(); 230 return isolate.reload();
213 } else { 231 } else {
214 return isolate.get(objectId); 232 return isolate.get(objectId);
215 } 233 }
234 });
235 }
236
237 var obj = _cache[id];
238 if (obj != null) {
239 return obj.reload();
240 }
241 // Cache miss. Get the object from the vm directly.
242 return getAsMap(id).then((ObservableMap map) {
243 var obj = new ServiceObject._fromMap(this, map);
244 if (obj.canCache) {
245 _cache.putIfAbsent(id, () => obj);
216 } 246 }
217 } 247 return obj;
218 } else if (id == 'vm') { 248 });
219 return reload();
220 } else {
221 return getDirect(id);
222 }
223 } 249 }
224 250
225 /// Gets [id] as an [ObservableMap] from the service directly. 251 /// Gets [id] as an [ObservableMap] from the service directly.
226 Future<ObservableMap> getAsMap(String id) { 252 Future<ObservableMap> getAsMap(String id) {
227 return getString(id).then((response) { 253 return getString(id).then((response) {
228 try { 254 try {
229 var map = JSON.decode(response); 255 var map = JSON.decode(response);
230 Logger.root.info('Decoded $id');
231 Logger.root.info('Response $response');
232 return toObservable(map); 256 return toObservable(map);
233 } catch (e, st) { 257 } catch (e, st) {
234 return toObservable({ 258 return toObservable({
235 'type': 'Error', 259 'type': 'Error',
236 'id': '', 260 'id': '',
237 'kind': 'DecodeError', 261 'kind': 'DecodeError',
238 'message': '$e', 262 'message': '$e',
239 }); 263 });
240 } 264 }
241 }).catchError((error) { 265 }).catchError((error) {
242 return toObservable({ 266 return toObservable({
243 'type': 'Error', 267 'type': 'Error',
244 'id': '', 268 'id': '',
245 'kind': 'LastResort', 269 'kind': 'LastResort',
246 'message': '$error' 270 'message': '$error'
247 }); 271 });
248 }); 272 });
249 } 273 }
250 274
251 /// Get [id] as a [String] from the service directly. See [getAsMap]. 275 /// Get [id] as a [String] from the service directly. See [getAsMap].
252 Future<String> getString(String id); 276 Future<String> getString(String id);
253 277
254 void _update(ObservableMap map) { 278 void _update(ObservableMap map, bool mapIsRef) {
255 _ref = false; 279 if (mapIsRef) {
280 return;
281 }
282 _loaded = true;
256 version = map['version']; 283 version = map['version'];
257 architecture = map['architecture']; 284 architecture = map['architecture'];
258 uptime = map['uptime']; 285 uptime = map['uptime'];
259 _isolates.updateIsolates(map['isolates']); 286 _updateIsolates(map['isolates']);
260 allIsolates.clear(); 287 }
261 allIsolates.addAll(_isolates.isolates.values); 288
289 void _updateIsolates(List newIsolates) {
290 var oldIsolateCache = _isolateCache;
291 var newIsolateCache = new Map<String,Isolate>();
292 for (var isolateMap in newIsolates) {
293 var isolateId = isolateMap['id'];
294 var isolate = oldIsolateCache[isolateId];
295 if (isolate != null) {
296 newIsolateCache[isolateId] = isolate;
297 } else {
298 isolate = new ServiceObject._fromMap(this, isolateMap);
299 newIsolateCache[isolateId] = isolate;
300 Logger.root.info('New isolate \'${isolate.id}\'');
301 }
302 }
303 // Update the individual isolates asynchronously.
304 newIsolateCache.forEach((isolateId, isolate) {
305 isolate.reload();
306 });
307
308 _isolateCache = newIsolateCache;
262 } 309 }
263 } 310 }
264 311
265 /// State for a running isolate. 312 /// State for a running isolate.
266 class Isolate extends ServiceObject { 313 class Isolate extends ServiceObjectOwner {
314 @reflectable VM get vm => owner;
315 @reflectable Isolate get isolate => this;
316
267 String get link => _id; 317 String get link => _id;
268 String get hashLink => '#/$_id'; 318 String get hashLink => '#/$_id';
269 319
270 @observable bool pausedOnStart = false; 320 @observable bool pausedOnStart = false;
271 @observable bool pausedOnExit = false; 321 @observable bool pausedOnExit = false;
272 @observable bool running = false; 322 @observable bool running = false;
273 @observable bool idle = false; 323 @observable bool idle = false;
274 324
275 ScriptCache _scripts; 325 Map<String,ServiceObject> _cache = new Map<String,ServiceObject>();
276 /// Script cache.
277 ScriptCache get scripts => _scripts;
278 CodeCache _codes;
279 /// Code cache.
280 CodeCache get codes => _codes;
281 /// Class cache.
282 ClassCache _classes;
283 ClassCache get classes => _classes;
284 /// Function cache.
285 FunctionCache _functions;
286 FunctionCache get functions => _functions;
287 326
288 void _initOnce() { 327 Isolate._empty(ServiceObjectOwner owner) : super._empty(owner);
289 // Only called once.
290 assert(_scripts == null);
291 _scripts = new ScriptCache(this);
292 _codes = new CodeCache(this);
293 _classes = new ClassCache(this);
294 _functions = new FunctionCache(this);
295 }
296
297 Isolate.fromId(VM vm, String id) : super(vm, id, '@Isolate') {
298 _initOnce();
299 }
300
301 Isolate.fromMap(VM vm, Map map) : super.fromMap(vm, map) {
302 _initOnce();
303 }
304 328
305 /// Creates a link to [id] relative to [this]. 329 /// Creates a link to [id] relative to [this].
306 @reflectable String relativeLink(String id) => '${this.id}/$id'; 330 @reflectable String relativeLink(String id) => '${this.id}/$id';
307 /// Creates a relative link to [id] with a '#/' prefix. 331 /// Creates a relative link to [id] with a '#/' prefix.
308 @reflectable String relativeHashLink(String id) => '#/${relativeLink(id)}'; 332 @reflectable String relativeHashLink(String id) => '#/${relativeLink(id)}';
309 333
310 Future<ScriptCache> refreshCoverage() { 334 static const TAG_ROOT_ID = 'code/tag-0';
311 return get('coverage').then(_scripts._processCoverage); 335
336 /// Returns the Code object for the root tag.
337 Code tagRoot() {
338 // TODO(turnidge): Use get() here instead?
339 return _cache[TAG_ROOT_ID];
312 } 340 }
313 341
314 void processProfile(ServiceMap profile) { 342 void processProfile(ServiceMap profile) {
315 assert(profile.serviceType == 'Profile'); 343 assert(profile.serviceType == 'Profile');
316 var codeTable = new List<Code>(); 344 var codeTable = new List<Code>();
317 var codeRegions = profile['codes']; 345 var codeRegions = profile['codes'];
318 for (var codeRegion in codeRegions) { 346 for (var codeRegion in codeRegions) {
319 Code code = codeRegion['code']; 347 Code code = codeRegion['code'];
320 assert(code != null); 348 assert(code != null);
321 codeTable.add(code); 349 codeTable.add(code);
322 } 350 }
323 _codes._resetProfileData(); 351 _resetProfileData();
324 _codes._updateProfileData(profile, codeTable); 352 _updateProfileData(profile, codeTable);
325 var exclusiveTrie = profile['exclusive_trie']; 353 var exclusiveTrie = profile['exclusive_trie'];
326 if (exclusiveTrie != null) { 354 if (exclusiveTrie != null) {
327 profileTrieRoot = _processProfileTrie(exclusiveTrie, codeTable); 355 profileTrieRoot = _processProfileTrie(exclusiveTrie, codeTable);
328 } 356 }
329 } 357 }
330 358
331 Future<ServiceObject> getDirect(String serviceId) { 359 void _resetProfileData() {
332 return vm.getAsMap(relativeLink(serviceId)).then((ObservableMap m) { 360 _cache.values.forEach((value) {
333 return _upgradeToServiceObject(vm, this, m); 361 if (value is Code) {
362 Code code = value;
363 code.resetProfileData();
364 }
365 });
366 }
367
368 void _updateProfileData(ServiceMap profile, List<Code> codeTable) {
369 var codeRegions = profile['codes'];
370 var sampleCount = profile['samples'];
371 for (var codeRegion in codeRegions) {
372 Code code = codeRegion['code'];
373 code.updateProfileData(codeRegion, codeTable, sampleCount);
374 }
375 }
376
377 Future refreshCoverage() {
378 return get('coverage').then(_processCoverage);
379 }
380
381 void _processCoverage(ServiceMap coverage) {
382 assert(coverage.serviceType == 'CodeCoverage');
383 var coverageList = coverage['coverage'];
384 assert(coverageList != null);
385 coverageList.forEach((scriptCoverage) {
386 _processScriptCoverage(scriptCoverage);
334 }); 387 });
335 } 388 }
336 389
337 /// Requests [serviceId] from [this]. Completes to a [ServiceObject]. 390 void _processScriptCoverage(ObservableMap scriptCoverage) {
338 /// Can return pre-existing, cached, [ServiceObject]s. 391 // Because the coverage data was upgraded into a ServiceObject,
339 Future<ServiceObject> get(String serviceId) { 392 // the script can be directly accessed.
340 if (serviceId == '') { 393 Script script = scriptCoverage['script'];
341 return reload(); 394 script._processHits(scriptCoverage['hits']);
395 }
396
397 ServiceObject getFromMap(ObservableMap map) {
398 if (map == null) {
399 return null;
342 } 400 }
343 if (_scripts.cachesId(serviceId)) { 401 String id = map['id'];
344 return _scripts.get(serviceId); 402 var obj = _cache[id];
403 if (obj != null) {
404 return obj;
345 } 405 }
346 if (_codes.cachesId(serviceId)) { 406 // Build the object from the map directly.
347 return _codes.get(serviceId); 407 obj = new ServiceObject._fromMap(this, map);
408 if (obj.canCache) {
409 _cache[id] = obj;
348 } 410 }
349 if (_classes.cachesId(serviceId)) { 411 return obj;
350 return _classes.get(serviceId); 412 }
413
414 Future<ServiceObject> get(String id) {
415 var obj = _cache[id];
416 if (obj != null) {
417 return obj.reload();
351 } 418 }
352 if (_functions.cachesId(serviceId)) { 419 // Cache miss. Get the object from the vm directly.
353 return _functions.get(serviceId); 420 return vm.getAsMap(relativeLink(id)).then((ObservableMap map) {
354 } 421 var obj = new ServiceObject._fromMap(this, map);
355 return getDirect(serviceId); 422 if (obj.canCache) {
423 _cache.putIfAbsent(id, () => obj);
424 }
425 return obj;
426 });
356 } 427 }
357 428
358 @observable ServiceMap rootLib; 429 @observable ServiceMap rootLib;
359 @observable ObservableMap topFrame; 430 @observable ObservableMap topFrame;
360 431
361 @observable String name; 432 @observable String name;
362 @observable String vmName; 433 @observable String vmName;
434 @observable String mainPort;
363 @observable Map entry; 435 @observable Map entry;
364 436
365 @observable final Map<String, double> timers = 437 @observable final Map<String, double> timers =
366 toObservable(new Map<String, double>()); 438 toObservable(new Map<String, double>());
367 439
368 @observable int newHeapUsed = 0; 440 @observable int newHeapUsed = 0;
369 @observable int oldHeapUsed = 0; 441 @observable int oldHeapUsed = 0;
370 @observable int newHeapCapacity = 0; 442 @observable int newHeapCapacity = 0;
371 @observable int oldHeapCapacity = 0; 443 @observable int oldHeapCapacity = 0;
372 444
373 @observable String fileAndLine; 445 @observable String fileAndLine;
374 446
375 void _update(ObservableMap map) { 447 void _update(ObservableMap map, bool mapIsRef) {
376 upgradeCollection(map, vm, this);
377 mainPort = map['mainPort']; 448 mainPort = map['mainPort'];
378 name = map['name']; 449 name = map['name'];
379 if (ServiceObject.isRefType(map['type'])) { 450 vmName = map['name'];
451 if (mapIsRef) {
380 return; 452 return;
381 } 453 }
382 _ref = false; 454 _loaded = true;
455 _upgradeCollection(map, isolate);
383 if (map['rootLib'] == null || 456 if (map['rootLib'] == null ||
384 map['timers'] == null || 457 map['timers'] == null ||
385 map['heap'] == null) { 458 map['heap'] == null) {
386 Logger.root.severe("Malformed 'Isolate' response: $map"); 459 Logger.root.severe("Malformed 'Isolate' response: $map");
387 return; 460 return;
388 } 461 }
389 rootLib = map['rootLib']; 462 rootLib = map['rootLib'];
390 vmName = map['name'];
391 if (map['entry'] != null) { 463 if (map['entry'] != null) {
392 entry = map['entry']; 464 entry = map['entry'];
393 } 465 }
394 if (map['topFrame'] != null) { 466 if (map['topFrame'] != null) {
395 topFrame = map['topFrame']; 467 topFrame = map['topFrame'];
396 } else { 468 } else {
397 topFrame = null ; 469 topFrame = null ;
398 } 470 }
399 471
400 var timerMap = {}; 472 var timerMap = {};
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
461 // Recursively read child nodes. 533 // Recursively read child nodes.
462 for (var i = 0; i < children; i++) { 534 for (var i = 0; i < children; i++) {
463 var child = _readTrieNode(codeTable); 535 var child = _readTrieNode(codeTable);
464 node.children.add(child); 536 node.children.add(child);
465 node.summedChildCount += child.count; 537 node.summedChildCount += child.count;
466 } 538 }
467 return node; 539 return node;
468 } 540 }
469 } 541 }
470 542
471 // TODO(johnmccutchan): Make this into an IsolateCache.
472 class IsolateList {
473 final VM _vm;
474 final isolates = new ObservableMap<String, Isolate>();
475
476 IsolateList(this._vm);
477
478 void updateIsolates(List<Map> members) {
479 // Find dead isolates.
480 var deadIsolates = [];
481 isolates.forEach((k, v) {
482 if (!_foundIsolateInMembers(k, members)) {
483 deadIsolates.add(k);
484 }
485 });
486 // Remove them.
487 deadIsolates.forEach((id) {
488 isolates.remove(id);
489 Logger.root.info('Isolate \'$id\' has gone away.');
490 });
491
492 // Add new isolates.
493 members.forEach((map) {
494 var id = map['id'];
495 var isolate = isolates[id];
496 if (isolate == null) {
497 isolate = new Isolate.fromMap(_vm, map);
498 Logger.root.info('Created ServiceObject for \'${isolate.id}\' with '
499 'type \'${isolate.serviceType}\'');
500 isolates[id] = isolate;
501 }
502 });
503
504 // After updating the isolate list, refresh each isolate.
505 _refreshIsolates();
506 }
507
508 void _refreshIsolates() {
509 // This is technically asynchronous but we don't need to wait for
510 // the result.
511 isolates.forEach((k, Isolate isolate) {
512 isolate.reload();
513 });
514 }
515
516 Isolate getIsolate(String id) {
517 assert(id.startsWith('isolates/'));
518 var isolate = isolates[id];
519 if (isolate != null) {
520 return isolate;
521 }
522 isolate = new Isolate.fromId(_vm, id);
523 isolates[id] = isolate;
524 isolate.load();
525 return isolate;
526 }
527
528 Isolate getIsolateFromMap(ObservableMap m) {
529 assert(ServiceObject.isServiceMap(m));
530 String id = m['id'];
531 assert(id.startsWith('isolates/'));
532 var isolate = isolates[id];
533 if (isolate != null) {
534 isolate.update(m);
535 return isolate;
536 }
537 isolate = new Isolate.fromMap(_vm, m);
538 isolates[id] = isolate;
539 isolate.load();
540 return isolate;
541 }
542
543 static bool _foundIsolateInMembers(String id, List<Map> members) {
544 return members.any((E) => E['id'] == id);
545 }
546 }
547
548
549 /// A [ServiceObject] which implements [ObservableMap]. 543 /// A [ServiceObject] which implements [ObservableMap].
550 class ServiceMap extends ServiceObject implements ObservableMap { 544 class ServiceMap extends ServiceObject implements ObservableMap {
551 final ObservableMap _map = new ObservableMap(); 545 final ObservableMap _map = new ObservableMap();
552 ServiceMap(Isolate isolate, String id, String serviceType) : 546
553 super(isolate, id, serviceType) { 547 bool get canCache {
548 return (_serviceType == 'Class' ||
549 _serviceType == 'Function' ||
550 _serviceType == 'Library');
554 } 551 }
552 bool get immutable => canCache;
555 553
556 ServiceMap.fromMap(Isolate isolate, ObservableMap m) : 554 ServiceMap._empty(ServiceObjectOwner owner) : super._empty(owner);
557 super.fromMap(isolate, m);
558 555
559 String toString() => _map.toString(); 556 String toString() => _map.toString();
560 557
561 void _upgradeValues() { 558 void _upgradeValues() {
562 assert(isolate != null); 559 assert(owner != null);
563 upgradeCollection(_map, vm, isolate); 560 _upgradeCollection(_map, owner);
564 } 561 }
565 562
566 void _update(ObservableMap m) { 563 void _update(ObservableMap map, bool mapIsRef) {
564 _loaded = !mapIsRef;
565
566 // TODO(turnidge): Currently _map.clear() prevents us from
567 // upgrading an already upgraded submap. Is clearing really the
568 // right thing to do here?
567 _map.clear(); 569 _map.clear();
568 _map.addAll(m); 570 _map.addAll(map);
571
569 name = _map['user_name']; 572 name = _map['user_name'];
570 vmName = _map['name']; 573 vmName = _map['name'];
571 _upgradeValues(); 574 _upgradeValues();
572 } 575 }
573 576
574 // Forward Map interface calls. 577 // Forward Map interface calls.
575 void addAll(Map other) => _map.addAll(other); 578 void addAll(Map other) => _map.addAll(other);
576 void clear() => _map.clear(); 579 void clear() => _map.clear();
577 bool containsValue(v) => _map.containsValue(v); 580 bool containsValue(v) => _map.containsValue(v);
578 bool containsKey(k) => _map.containsKey(k); 581 bool containsKey(k) => _map.containsKey(k);
(...skipping 13 matching lines...) Expand all
592 void notifyChange(ChangeRecord record) => _map.notifyChange(record); 595 void notifyChange(ChangeRecord record) => _map.notifyChange(record);
593 notifyPropertyChange(Symbol field, Object oldValue, Object newValue) => 596 notifyPropertyChange(Symbol field, Object oldValue, Object newValue) =>
594 _map.notifyPropertyChange(field, oldValue, newValue); 597 _map.notifyPropertyChange(field, oldValue, newValue);
595 void observed() => _map.observed(); 598 void observed() => _map.observed();
596 void unobserved() => _map.unobserved(); 599 void unobserved() => _map.unobserved();
597 Stream<List<ChangeRecord>> get changes => _map.changes; 600 Stream<List<ChangeRecord>> get changes => _map.changes;
598 bool get hasObservers => _map.hasObservers; 601 bool get hasObservers => _map.hasObservers;
599 } 602 }
600 603
601 class ServiceError extends ServiceObject { 604 class ServiceError extends ServiceObject {
602 ServiceError.fromMap(ServiceObject owner, Map m) : super.fromMap(owner, m); 605 ServiceError._empty(ServiceObjectOwner owner) : super._empty(owner);
603 606
604 @observable String kind; 607 @observable String kind;
605 @observable String message; 608 @observable String message;
606 609
607 void _update(ObservableMap map) { 610 void _update(ObservableMap map, bool mapIsRef) {
611 _loaded = true;
608 kind = map['kind']; 612 kind = map['kind'];
609 message = map['message']; 613 message = map['message'];
610 name = 'ServiceError $kind'; 614 name = 'ServiceError $kind';
611 vmName = name; 615 vmName = name;
612 } 616 }
613 617
614 // TODO: stackTrace? 618 // TODO: stackTrace?
615 } 619 }
616 620
617 class ScriptLine { 621 class ScriptLine {
618 @reflectable final int line; 622 @reflectable final int line;
619 @reflectable final String text; 623 @reflectable final String text;
620 ScriptLine(this.line, this.text); 624 ScriptLine(this.line, this.text);
621 } 625 }
622 626
623 class Script extends ServiceObject { 627 class Script extends ServiceObject {
624 @reflectable final lines = new ObservableList<ScriptLine>(); 628 @reflectable final lines = new ObservableList<ScriptLine>();
625 @reflectable final hits = new ObservableMap<int, int>(); 629 @reflectable final hits = new ObservableMap<int, int>();
626 @observable ServiceObject library; 630 @observable ServiceObject library;
627 @observable String kind; 631 @observable String kind;
628 632
633 bool get canCache => true;
634 bool get immutable => true;
635
629 String _shortUrl; 636 String _shortUrl;
630 String _url; 637 String _url;
631 638
632 Script.fromMap(Isolate isolate, Map m) : super.fromMap(isolate, m); 639 Script._empty(ServiceObjectOwner owner) : super._empty(owner);
633 640
634 void _update(ObservableMap m) { 641 void _update(ObservableMap map, bool mapIsRef) {
635 // Assert that m is a service map. 642 kind = map['kind'];
636 assert(ServiceObject.isServiceMap(m)); 643 _url = map['name'];
637 if ((m['type'] == 'Error') && (m['kind'] == 'NotFoundError')) {
638 // TODO(johnmccutchan): Find out why dart:core/identical.dart can't
639 // be found but shows up in coverage. i.e. a function has reference
640 // to script that no library does.
641 Logger.root.info(m['message']);
642 return;
643 }
644 // Assert that the id hasn't changed.
645 assert(m['id'] == _id);
646 // Assert that the type hasn't changed.
647 assert(ServiceObject.stripRef(m['type']) == _serviceType);
648 _url = m['name'];
649 _shortUrl = _url.substring(_url.lastIndexOf('/') + 1); 644 _shortUrl = _url.substring(_url.lastIndexOf('/') + 1);
650 name = _shortUrl; 645 name = _shortUrl;
651 vmName = _url; 646 vmName = _url;
652 kind = m['kind']; 647 _processSource(map['source']);
653 _processSource(m['source']);
654 } 648 }
655 649
656 void _processHits(List scriptHits) { 650 void _processHits(List scriptHits) {
657 if (_ref) { 651 if (!_loaded) {
658 // Eagerly grab script source. 652 // Eagerly grab script source.
659 load(); 653 load();
660 } 654 }
661 // Update hits table. 655 // Update hits table.
662 for (var i = 0; i < scriptHits.length; i += 2) { 656 for (var i = 0; i < scriptHits.length; i += 2) {
663 var line = scriptHits[i]; 657 var line = scriptHits[i];
664 var hit = scriptHits[i + 1]; // hit status. 658 var hit = scriptHits[i + 1]; // hit status.
665 assert(line >= 1); // Lines start at 1. 659 assert(line >= 1); // Lines start at 1.
666 hits[line] = hit; 660 hits[line] = hit;
667 } 661 }
668 } 662 }
669 663
670 void _processSource(String source) { 664 void _processSource(String source) {
671 // Preemptyively mark that this is a reference. 665 // Preemptyively mark that this is not loaded.
672 _ref = true; 666 _loaded = false;
673 if (source == null) { 667 if (source == null) {
674 return; 668 return;
675 } 669 }
676 var sourceLines = source.split('\n'); 670 var sourceLines = source.split('\n');
677 if (sourceLines.length == 0) { 671 if (sourceLines.length == 0) {
678 return; 672 return;
679 } 673 }
680 // We have the source to the script. This is no longer a reference. 674 // We have the source to the script. This is now loaded.
681 _ref = false; 675 _loaded = true;
682 lines.clear(); 676 lines.clear();
683 Logger.root.info('Adding ${sourceLines.length} source lines for ${_url}'); 677 Logger.root.info('Adding ${sourceLines.length} source lines for ${_url}');
684 for (var i = 0; i < sourceLines.length; i++) { 678 for (var i = 0; i < sourceLines.length; i++) {
685 lines.add(new ScriptLine(i + 1, sourceLines[i])); 679 lines.add(new ScriptLine(i + 1, sourceLines[i]));
686 } 680 }
687 } 681 }
688 682
689 683
690 } 684 }
691 685
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after
797 @reflectable final callees = new List<CodeCallCount>(); 791 @reflectable final callees = new List<CodeCallCount>();
798 @reflectable final instructions = new ObservableList<CodeInstruction>(); 792 @reflectable final instructions = new ObservableList<CodeInstruction>();
799 @reflectable final addressTicks = new ObservableMap<int, CodeTick>(); 793 @reflectable final addressTicks = new ObservableMap<int, CodeTick>();
800 @observable String formattedInclusiveTicks = ''; 794 @observable String formattedInclusiveTicks = '';
801 @observable String formattedExclusiveTicks = ''; 795 @observable String formattedExclusiveTicks = '';
802 @observable ServiceMap objectPool; 796 @observable ServiceMap objectPool;
803 @observable ServiceMap function; 797 @observable ServiceMap function;
804 String name; 798 String name;
805 String vmName; 799 String vmName;
806 800
807 Code.fromMap(Isolate isolate, Map map) : super.fromMap(isolate, map); 801 bool get canCache => true;
802 bool get immutable => true;
803
804 Code._empty(ServiceObjectOwner owner) : super._empty(owner);
808 805
809 // Reset all data associated with a profile. 806 // Reset all data associated with a profile.
810 void resetProfileData() { 807 void resetProfileData() {
811 totalSamplesInProfile = 0; 808 totalSamplesInProfile = 0;
812 exclusiveTicks = 0; 809 exclusiveTicks = 0;
813 inclusiveTicks = 0; 810 inclusiveTicks = 0;
814 formattedInclusiveTicks = ''; 811 formattedInclusiveTicks = '';
815 formattedExclusiveTicks = ''; 812 formattedExclusiveTicks = '';
816 callers.clear(); 813 callers.clear();
817 callees.clear(); 814 callees.clear();
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
867 _processTicks(ticks); 864 _processTicks(ticks);
868 } 865 }
869 formattedInclusiveTicks = 866 formattedInclusiveTicks =
870 '${formatPercent(inclusiveTicks, totalSamplesInProfile)} ' 867 '${formatPercent(inclusiveTicks, totalSamplesInProfile)} '
871 '($inclusiveTicks)'; 868 '($inclusiveTicks)';
872 formattedExclusiveTicks = 869 formattedExclusiveTicks =
873 '${formatPercent(exclusiveTicks, totalSamplesInProfile)} ' 870 '${formatPercent(exclusiveTicks, totalSamplesInProfile)} '
874 '($exclusiveTicks)'; 871 '($exclusiveTicks)';
875 } 872 }
876 873
877 void _update(ObservableMap m) { 874 void _update(ObservableMap m, bool mapIsRef) {
878 assert(ServiceObject.isServiceMap(m));
879 assert(m['id'] == _id);
880 assert(ServiceObject.stripRef(m['type']) == _serviceType);
881 name = m['user_name']; 875 name = m['user_name'];
882 vmName = m['name']; 876 vmName = m['name'];
883 kind = CodeKind.fromString(m['kind']); 877 kind = CodeKind.fromString(m['kind']);
884 startAddress = int.parse(m['start'], radix:16); 878 startAddress = int.parse(m['start'], radix:16);
885 endAddress = int.parse(m['end'], radix:16); 879 endAddress = int.parse(m['end'], radix:16);
886 function = _upgradeToServiceObject(vm, isolate, m['function']); 880 function = isolate.getFromMap(m['function']);
887 objectPool = _upgradeToServiceObject(vm, isolate, m['object_pool']); 881 objectPool = isolate.getFromMap(m['object_pool']);
888 var disassembly = m['disassembly']; 882 var disassembly = m['disassembly'];
889 if (disassembly != null) { 883 if (disassembly != null) {
890 _processDisassembly(disassembly); 884 _processDisassembly(disassembly);
891 } 885 }
892 // We are a reference if we don't have instructions and are Dart code. 886 // We are loaded if we have instructions or are not Dart code.
893 _ref = (instructions.length == 0) && (kind == CodeKind.Dart); 887 _loaded = (instructions.length != 0) || (kind != CodeKind.Dart);
894 hasDisassembly = (instructions.length != 0) && (kind == CodeKind.Dart); 888 hasDisassembly = (instructions.length != 0) && (kind == CodeKind.Dart);
895 } 889 }
896 890
897 @observable bool hasDisassembly = false; 891 @observable bool hasDisassembly = false;
898 892
899 void _processDisassembly(List<String> disassembly){ 893 void _processDisassembly(List<String> disassembly){
900 assert(disassembly != null); 894 assert(disassembly != null);
901 instructions.clear(); 895 instructions.clear();
902 assert((disassembly.length % 3) == 0); 896 assert((disassembly.length % 3) == 0);
903 for (var i = 0; i < disassembly.length; i += 3) { 897 for (var i = 0; i < disassembly.length; i += 3) {
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
949 943
950 int _callCount(List<CodeCallCount> calls, Code code) { 944 int _callCount(List<CodeCallCount> calls, Code code) {
951 for (CodeCallCount caller in calls) { 945 for (CodeCallCount caller in calls) {
952 if (caller.code == code) { 946 if (caller.code == code) {
953 return caller.count; 947 return caller.count;
954 } 948 }
955 } 949 }
956 return 0; 950 return 0;
957 } 951 }
958 } 952 }
953
954 // Returns true if [map] is a service map. i.e. it has the following keys:
955 // 'id' and a 'type'.
956 bool _isServiceMap(ObservableMap m) {
957 return (m != null) && (m['id'] != null) && (m['type'] != null);
958 }
959
960 bool _hasRef(String type) => type.startsWith('@');
961 String _stripRef(String type) => (_hasRef(type) ? type.substring(1) : type);
962
963 /// Recursively upgrades all [ServiceObject]s inside [collection] which must
964 /// be an [ObservableMap] or an [ObservableList]. Upgraded elements will be
965 /// associated with [vm] and [isolate].
966 void _upgradeCollection(collection, ServiceObjectOwner owner) {
967 if (collection is ServiceMap) {
968 return;
969 }
970 if (collection is ObservableMap) {
971 _upgradeObservableMap(collection, owner);
972 } else if (collection is ObservableList) {
973 _upgradeObservableList(collection, owner);
974 }
975 }
976
977 void _upgradeObservableMap(ObservableMap map, ServiceObjectOwner owner) {
978 map.forEach((k, v) {
979 if ((v is ObservableMap) && _isServiceMap(v)) {
980 map[k] = owner.getFromMap(v);
981 } else if (v is ObservableList) {
982 _upgradeObservableList(v, owner);
983 } else if (v is ObservableMap) {
984 _upgradeObservableMap(v, owner);
985 }
986 });
987 }
988
989 void _upgradeObservableList(ObservableList list, ServiceObjectOwner owner) {
990 for (var i = 0; i < list.length; i++) {
991 var v = list[i];
992 if ((v is ObservableMap) && _isServiceMap(v)) {
993 list[i] = owner.getFromMap(v);
994 } else if (v is ObservableList) {
995 _upgradeObservableList(v, owner);
996 } else if (v is ObservableMap) {
997 _upgradeObservableMap(v, owner);
998 }
999 }
1000 }
OLDNEW
« no previous file with comments | « runtime/bin/vmservice/client/lib/src/service/cache.dart ('k') | runtime/bin/vmservice/client/lib/src/service/service.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698