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

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