| OLD | NEW |
| 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] represents a persistent object within the vm. | 7 /// A [ServiceObject] represents a persistent object within the vm. |
| 8 abstract class ServiceObject extends Observable { | 8 abstract class ServiceObject extends Observable { |
| 9 static int LexicalSortName(ServiceObject o1, ServiceObject o2) { | 9 static int LexicalSortName(ServiceObject o1, ServiceObject o2) { |
| 10 return o1.name.compareTo(o2.name); | 10 return o1.name.compareTo(o2.name); |
| (...skipping 309 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 320 /// The result may come from the cache. The result will not necessarily | 320 /// The result may come from the cache. The result will not necessarily |
| 321 /// be [loaded]. | 321 /// be [loaded]. |
| 322 ServiceObject getFromMap(ObservableMap map); | 322 ServiceObject getFromMap(ObservableMap map); |
| 323 } | 323 } |
| 324 | 324 |
| 325 /// State for a VM being inspected. | 325 /// State for a VM being inspected. |
| 326 abstract class VM extends ServiceObjectOwner { | 326 abstract class VM extends ServiceObjectOwner { |
| 327 @reflectable VM get vm => this; | 327 @reflectable VM get vm => this; |
| 328 @reflectable Isolate get isolate => null; | 328 @reflectable Isolate get isolate => null; |
| 329 | 329 |
| 330 // TODO(johnmccutchan): Ensure that isolates do not end up in _cache. |
| 331 Map<String,ServiceObject> _cache = new Map<String,ServiceObject>(); |
| 332 final ObservableMap<String,Isolate> _isolateCache = |
| 333 new ObservableMap<String,Isolate>(); |
| 334 |
| 330 @reflectable Iterable<Isolate> get isolates => _isolateCache.values; | 335 @reflectable Iterable<Isolate> get isolates => _isolateCache.values; |
| 331 | 336 |
| 332 @observable String version = 'unknown'; | 337 @observable String version = 'unknown'; |
| 333 @observable String targetCPU; | 338 @observable String targetCPU; |
| 334 @observable int architectureBits; | 339 @observable int architectureBits; |
| 335 @observable double uptime = 0.0; | 340 @observable double uptime = 0.0; |
| 336 @observable bool assertsEnabled = false; | 341 @observable bool assertsEnabled = false; |
| 337 @observable bool typeChecksEnabled = false; | 342 @observable bool typeChecksEnabled = false; |
| 338 @observable String pid = ''; | 343 @observable String pid = ''; |
| 339 @observable DateTime lastUpdate; | 344 @observable DateTime lastUpdate; |
| 340 | 345 |
| 341 VM() : super._empty(null) { | 346 VM() : super._empty(null) { |
| 342 name = 'vm'; | 347 name = 'vm'; |
| 343 vmName = 'vm'; | 348 vmName = 'vm'; |
| 344 _cache['vm'] = this; | 349 _cache['vm'] = this; |
| 345 update(toObservable({'id':'vm', 'type':'@VM'})); | 350 update(toObservable({'id':'vm', 'type':'@VM'})); |
| 346 } | 351 } |
| 347 | 352 |
| 348 final StreamController<ServiceException> exceptions = | 353 final StreamController<ServiceException> exceptions = |
| 349 new StreamController.broadcast(); | 354 new StreamController.broadcast(); |
| 350 final StreamController<ServiceError> errors = | 355 final StreamController<ServiceError> errors = |
| 351 new StreamController.broadcast(); | 356 new StreamController.broadcast(); |
| 352 final StreamController<ServiceEvent> events = | 357 final StreamController<ServiceEvent> events = |
| 353 new StreamController.broadcast(); | 358 new StreamController.broadcast(); |
| 354 | 359 |
| 355 void postEventMessage(String eventMessage, [dynamic data]) { | 360 bool _isIsolateLifecycleEvent(String eventType) { |
| 356 var map; | 361 return _isIsolateShutdownEvent(eventType) || |
| 357 try { | 362 _isIsolateCreatedEvent(eventType); |
| 358 map = _parseJSON(eventMessage); | |
| 359 assert(!map.containsKey('_data')); | |
| 360 if (data != null) { | |
| 361 map['_data'] = data; | |
| 362 } | |
| 363 } catch (e, st) { | |
| 364 Logger.root.severe('Ignoring malformed event message: ${eventMessage}'); | |
| 365 return; | |
| 366 } | |
| 367 if (map['type'] != 'ServiceEvent') { | |
| 368 Logger.root.severe( | |
| 369 "Expected 'ServiceEvent' but found '${map['type']}'"); | |
| 370 return; | |
| 371 } | |
| 372 | |
| 373 // Extract the owning isolate from the event itself. | |
| 374 String owningIsolateId = map['isolate']['id']; | |
| 375 getIsolate(owningIsolateId).then((owningIsolate) { | |
| 376 if (owningIsolate == null) { | |
| 377 // TODO(koda): Do we care about GC events in VM isolate? | |
| 378 Logger.root.severe( | |
| 379 'Ignoring event with unknown isolate id: $owningIsolateId'); | |
| 380 } else { | |
| 381 var event = new ServiceObject._fromMap(owningIsolate, map); | |
| 382 events.add(event); | |
| 383 } | |
| 384 }); | |
| 385 } | 363 } |
| 386 | 364 |
| 387 static final RegExp _currentIsolateMatcher = new RegExp(r'isolates/\d+'); | 365 bool _isIsolateShutdownEvent(String eventType) { |
| 388 static final RegExp _currentObjectMatcher = new RegExp(r'isolates/\d+/'); | 366 return (eventType == 'IsolateShutdown'); |
| 389 static final String _isolatesPrefix = 'isolates/'; | 367 } |
| 390 | 368 |
| 391 String _parseObjectId(String id) { | 369 bool _isIsolateCreatedEvent(String eventType) { |
| 392 Match m = _currentObjectMatcher.matchAsPrefix(id); | 370 return (eventType == 'IsolateCreated'); |
| 393 if (m == null) { | 371 } |
| 372 |
| 373 void postServiceEvent(String response, ByteData data) { |
| 374 var map; |
| 375 try { |
| 376 map = _parseJSON(response); |
| 377 assert(!map.containsKey('_data')); |
| 378 if (data != null) { |
| 379 map['_data'] = data; |
| 380 } |
| 381 } catch (e, st) { |
| 382 Logger.root.severe('Ignoring malformed event response: ${response}'); |
| 383 return; |
| 384 } |
| 385 if (map['type'] != 'ServiceEvent') { |
| 386 Logger.root.severe( |
| 387 "Expected 'ServiceEvent' but found '${map['type']}'"); |
| 388 return; |
| 389 } |
| 390 |
| 391 var eventType = map['eventType']; |
| 392 |
| 393 if (_isIsolateLifecycleEvent(eventType)) { |
| 394 String isolateId = map['isolate']['id']; |
| 395 var event; |
| 396 if (_isIsolateCreatedEvent(eventType)) { |
| 397 _onIsolateCreated(map['isolate']); |
| 398 // By constructing the event *after* adding the isolate to the |
| 399 // isolate cache, the call to getFromMap will use the cached Isolate. |
| 400 event = new ServiceObject._fromMap(this, map); |
| 401 } else { |
| 402 assert(_isIsolateShutdownEvent(eventType)); |
| 403 // By constructing the event *before* removing the isolate from the |
| 404 // isolate cache, the call to getFromMap will use the cached Isolate. |
| 405 event = new ServiceObject._fromMap(this, map); |
| 406 _onIsolateShutdown(isolateId); |
| 407 } |
| 408 assert(event != null); |
| 409 events.add(event); |
| 410 return; |
| 411 } |
| 412 |
| 413 // Extract the owning isolate from the event itself. |
| 414 String owningIsolateId = map['isolate']['id']; |
| 415 getIsolate(owningIsolateId).then((owningIsolate) { |
| 416 if (owningIsolate == null) { |
| 417 // TODO(koda): Do we care about GC events in VM isolate? |
| 418 Logger.root.severe('Ignoring event with unknown isolate id: ' |
| 419 '$owningIsolateId'); |
| 420 return; |
| 421 } |
| 422 var event = new ServiceObject._fromMap(owningIsolate, map); |
| 423 events.add(event); |
| 424 }); |
| 425 } |
| 426 |
| 427 Isolate _onIsolateCreated(Map isolateMap) { |
| 428 var isolateId = isolateMap['id']; |
| 429 assert(!_isolateCache.containsKey(isolateId)); |
| 430 Isolate isolate = new ServiceObject._fromMap(this, isolateMap); |
| 431 _isolateCache[isolateId] = isolate; |
| 432 notifyPropertyChange(#isolates, true, false); |
| 433 // Eagerly load the isolate. |
| 434 isolate.load().catchError((e) { |
| 435 Logger.root.info('Eagerly loading an isolate failed: $e'); |
| 436 }); |
| 437 return isolate; |
| 438 } |
| 439 |
| 440 void _onIsolateShutdown(String isolateId) { |
| 441 assert(_isolateCache.containsKey(isolateId)); |
| 442 _isolateCache.remove(isolateId); |
| 443 notifyPropertyChange(#isolates, true, false); |
| 444 } |
| 445 |
| 446 void _updateIsolatesFromList(List isolateList) { |
| 447 var shutdownIsolates = <String>[]; |
| 448 var createdIsolates = <Map>[]; |
| 449 var isolateStillExists = <String, bool>{}; |
| 450 |
| 451 // Start with the assumption that all isolates are gone. |
| 452 for (var isolateId in _isolateCache.keys) { |
| 453 isolateStillExists[isolateId] = false; |
| 454 } |
| 455 |
| 456 // Find created isolates and mark existing isolates as living. |
| 457 for (var isolateMap in isolateList) { |
| 458 var isolateId = isolateMap['id']; |
| 459 if (!_isolateCache.containsKey(isolateId)) { |
| 460 createdIsolates.add(isolateMap); |
| 461 } else { |
| 462 isolateStillExists[isolateId] = true; |
| 463 } |
| 464 } |
| 465 |
| 466 // Find shutdown isolates. |
| 467 isolateStillExists.forEach((isolateId, exists) { |
| 468 if (!exists) { |
| 469 shutdownIsolates.add(isolateId); |
| 470 } |
| 471 }); |
| 472 |
| 473 // Process shutdown. |
| 474 for (var isolateId in shutdownIsolates) { |
| 475 _onIsolateShutdown(isolateId); |
| 476 } |
| 477 |
| 478 // Process creation. |
| 479 for (var isolateMap in createdIsolates) { |
| 480 _onIsolateCreated(isolateMap); |
| 481 } |
| 482 } |
| 483 |
| 484 static final String _isolateIdPrefix = 'isolates/'; |
| 485 |
| 486 ServiceObject getFromMap(ObservableMap map) { |
| 487 if (map == null) { |
| 394 return null; | 488 return null; |
| 395 } | 489 } |
| 396 return m.input.substring(m.end); | 490 String id = map['id']; |
| 397 } | 491 if (!id.startsWith(_isolateIdPrefix)) { |
| 492 // Currently the VM only supports upgrading Isolate ServiceObjects. |
| 493 throw new UnimplementedError(); |
| 494 } |
| 398 | 495 |
| 399 String _parseIsolateId(String id) { | 496 // Check cache. |
| 400 Match m = _currentIsolateMatcher.matchAsPrefix(id); | 497 var isolate = _isolateCache[id]; |
| 401 if (m == null) { | 498 if (isolate == null) { |
| 402 return ''; | 499 // We should never see an unknown isolate here. |
| 500 throw new UnimplementedError(); |
| 403 } | 501 } |
| 404 return id.substring(0, m.end); | 502 return isolate; |
| 405 } | |
| 406 | |
| 407 Map<String,ServiceObject> _cache = new Map<String,ServiceObject>(); | |
| 408 Map<String,Isolate> _isolateCache = new Map<String,Isolate>(); | |
| 409 | |
| 410 ServiceObject getFromMap(ObservableMap map) { | |
| 411 throw new UnimplementedError(); | |
| 412 } | 503 } |
| 413 | 504 |
| 414 // Note that this function does not reload the isolate if it found | 505 // Note that this function does not reload the isolate if it found |
| 415 // in the cache. | 506 // in the cache. |
| 416 Future<ServiceObject> getIsolate(String isolateId) { | 507 Future<ServiceObject> getIsolate(String isolateId) { |
| 417 if (isolateId == '') { | 508 return new Future.value(_isolateCache[isolateId]); |
| 418 return new Future.value(null); | |
| 419 } | |
| 420 Isolate isolate = _isolateCache[isolateId]; | |
| 421 if (isolate != null) { | |
| 422 return new Future.value(isolate); | |
| 423 } | |
| 424 // The isolate is not in the cache. Reload the vm and see if the | |
| 425 // requested isolate is found. | |
| 426 // | |
| 427 // TODO(turnidge): We don't want to reload all isolates so much. | |
| 428 // Doesn't scale well. Change this to be more fine-grained. | |
| 429 return reload().then((result) { | |
| 430 if (result is! VM) { | |
| 431 return null; | |
| 432 } | |
| 433 assert(result == this); | |
| 434 return _isolateCache[isolateId]; | |
| 435 }); | |
| 436 } | 509 } |
| 437 | 510 |
| 438 dynamic _reviver(dynamic key, dynamic value) { | 511 dynamic _reviver(dynamic key, dynamic value) { |
| 439 return value; | 512 return value; |
| 440 } | 513 } |
| 441 | 514 |
| 442 ObservableMap _parseJSON(String response) { | 515 ObservableMap _parseJSON(String response) { |
| 443 var map; | 516 var map; |
| 444 try { | 517 try { |
| 445 var decoder = new JsonDecoder(_reviver); | 518 var decoder = new JsonDecoder(_reviver); |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 534 _loaded = true; | 607 _loaded = true; |
| 535 version = map['version']; | 608 version = map['version']; |
| 536 targetCPU = map['targetCPU']; | 609 targetCPU = map['targetCPU']; |
| 537 architectureBits = map['architectureBits']; | 610 architectureBits = map['architectureBits']; |
| 538 uptime = map['uptime']; | 611 uptime = map['uptime']; |
| 539 var dateInMillis = int.parse(map['date']); | 612 var dateInMillis = int.parse(map['date']); |
| 540 lastUpdate = new DateTime.fromMillisecondsSinceEpoch(dateInMillis); | 613 lastUpdate = new DateTime.fromMillisecondsSinceEpoch(dateInMillis); |
| 541 assertsEnabled = map['assertsEnabled']; | 614 assertsEnabled = map['assertsEnabled']; |
| 542 pid = map['pid']; | 615 pid = map['pid']; |
| 543 typeChecksEnabled = map['typeChecksEnabled']; | 616 typeChecksEnabled = map['typeChecksEnabled']; |
| 544 _updateIsolates(map['isolates']); | 617 _updateIsolatesFromList(map['isolates']); |
| 545 } | 618 } |
| 546 | 619 |
| 547 void _updateIsolates(List newIsolates) { | 620 // Reload all isolates. |
| 548 var oldIsolateCache = _isolateCache; | 621 Future reloadIsolates() { |
| 549 var newIsolateCache = new Map<String,Isolate>(); | 622 var reloads = []; |
| 550 for (var isolateMap in newIsolates) { | 623 for (var isolate in isolates) { |
| 551 var isolateId = isolateMap['id']; | 624 var reload = isolate.reload().catchError((e) { |
| 552 var isolate = oldIsolateCache[isolateId]; | 625 Logger.root.info('Bulk reloading of isolates failed: $e'); |
| 553 if (isolate != null) { | 626 }); |
| 554 newIsolateCache[isolateId] = isolate; | 627 reloads.add(reload); |
| 555 } else { | |
| 556 isolate = new ServiceObject._fromMap(this, isolateMap); | |
| 557 newIsolateCache[isolateId] = isolate; | |
| 558 Logger.root.info('New isolate \'${isolate.id}\''); | |
| 559 } | |
| 560 } | 628 } |
| 561 // Update the individual isolates asynchronously. | 629 return Future.wait(reloads); |
| 562 newIsolateCache.forEach((isolateId, isolate) { | |
| 563 isolate.reload(); | |
| 564 }); | |
| 565 | |
| 566 _isolateCache = newIsolateCache; | |
| 567 } | 630 } |
| 568 } | 631 } |
| 569 | 632 |
| 570 /// Snapshot in time of tag counters. | 633 /// Snapshot in time of tag counters. |
| 571 class TagProfileSnapshot { | 634 class TagProfileSnapshot { |
| 572 final double seconds; | 635 final double seconds; |
| 573 final List<int> counters; | 636 final List<int> counters; |
| 574 int get sum => _sum; | 637 int get sum => _sum; |
| 575 int _sum = 0; | 638 int _sum = 0; |
| 576 TagProfileSnapshot(this.seconds, int countersLength) | 639 TagProfileSnapshot(this.seconds, int countersLength) |
| (...skipping 2341 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2918 var v = list[i]; | 2981 var v = list[i]; |
| 2919 if ((v is ObservableMap) && _isServiceMap(v)) { | 2982 if ((v is ObservableMap) && _isServiceMap(v)) { |
| 2920 list[i] = owner.getFromMap(v); | 2983 list[i] = owner.getFromMap(v); |
| 2921 } else if (v is ObservableList) { | 2984 } else if (v is ObservableList) { |
| 2922 _upgradeObservableList(v, owner); | 2985 _upgradeObservableList(v, owner); |
| 2923 } else if (v is ObservableMap) { | 2986 } else if (v is ObservableMap) { |
| 2924 _upgradeObservableMap(v, owner); | 2987 _upgradeObservableMap(v, owner); |
| 2925 } | 2988 } |
| 2926 } | 2989 } |
| 2927 } | 2990 } |
| OLD | NEW |