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 /// State for a running isolate. | 7 /// State for a running isolate. |
8 class Isolate extends ServiceObject { | 8 class Isolate extends ServiceObject { |
9 final VM vm; | 9 final VM vm; |
10 String get link => _id; | 10 String get link => _id; |
11 String get hashLink => '#/$_id'; | 11 String get hashLink => '#/$_id'; |
12 | 12 |
13 ScriptCache _scripts; | 13 ScriptCache _scripts; |
14 /// Script cache. | 14 /// Script cache. |
15 ScriptCache get scripts => _scripts; | 15 ScriptCache get scripts => _scripts; |
16 CodeCache _codes; | 16 CodeCache _codes; |
17 /// Code cache. | 17 /// Code cache. |
18 CodeCache get codes => _codes; | 18 CodeCache get codes => _codes; |
19 /// Class cache. | 19 /// Class cache. |
20 ClassCache _classes; | 20 ClassCache _classes; |
21 ClassCache get classes => _classes; | 21 ClassCache get classes => _classes; |
22 /// Function cache. | |
23 FunctionCache _functions; | |
24 FunctionCache get functions => _functions; | |
22 | 25 |
23 void _initOnce() { | 26 void _initOnce() { |
24 // Only called once. | 27 // Only called once. |
25 assert(_isolate == null); | 28 assert(_isolate == null); |
26 _isolate = this; | 29 _isolate = this; |
27 _scripts = new ScriptCache(this); | 30 _scripts = new ScriptCache(this); |
28 _codes = new CodeCache(this); | 31 _codes = new CodeCache(this); |
29 _classes = new ClassCache(this); | 32 _classes = new ClassCache(this); |
33 _functions = new FunctionCache(this); | |
30 } | 34 } |
31 | 35 |
32 Isolate.fromId(this.vm, String id) : super(null, id, '@Isolate') { | 36 Isolate.fromId(this.vm, String id) : super(null, id, '@Isolate') { |
33 _initOnce(); | 37 _initOnce(); |
34 } | 38 } |
35 | 39 |
36 Isolate.fromMap(this.vm, Map map) : super.fromMap(null, map) { | 40 Isolate.fromMap(this.vm, Map map) : super.fromMap(null, map) { |
37 _initOnce(); | 41 _initOnce(); |
38 } | 42 } |
39 | 43 |
40 /// Creates a link to [id] relative to [this]. | 44 /// Creates a link to [id] relative to [this]. |
41 @reflectable String relativeLink(String id) => '${this.id}/$id'; | 45 @reflectable String relativeLink(String id) => '${this.id}/$id'; |
42 /// Creates a relative link to [id] with a '#/' prefix. | 46 /// Creates a relative link to [id] with a '#/' prefix. |
43 @reflectable String relativeHashLink(String id) => '#/${relativeLink(id)}'; | 47 @reflectable String relativeHashLink(String id) => '#/${relativeLink(id)}'; |
44 | 48 |
45 Future<ScriptCache> refreshCoverage() { | 49 Future<ScriptCache> refreshCoverage() { |
46 return get('coverage').then(_scripts._processCoverage); | 50 return get('coverage').then(_scripts._processCoverage); |
47 } | 51 } |
48 | 52 |
49 void processProfile(ServiceMap profile) { | 53 void processProfile(ServiceMap profile) { |
50 assert(profile.serviceType == 'Profile'); | 54 assert(profile.serviceType == 'Profile'); |
51 var codeTable = new List<Code>(); | 55 var codeTable = new List<Code>(); |
52 var profileCodes = profile['codes']; | 56 var codeRegions = profile['codes']; |
53 for (var profileCode in profileCodes) { | 57 for (var codeRegion in codeRegions) { |
54 Code code = profileCode['code']; | 58 Code code = codeRegion['code']; |
59 assert(code != null); | |
55 codeTable.add(code); | 60 codeTable.add(code); |
56 } | 61 } |
57 _codes._resetProfileData(); | 62 _codes._resetProfileData(); |
58 _codes._updateProfileData(profile, codeTable); | 63 _codes._updateProfileData(profile, codeTable); |
59 } | 64 } |
60 | 65 |
61 /// Requests [serviceId] from [this]. Completes to a [ServiceObject]. | 66 /// Requests [serviceId] from [this]. Completes to a [ServiceObject]. |
62 /// Can return pre-existing, cached, [ServiceObject]s. | 67 /// Can return pre-existing, cached, [ServiceObject]s. |
63 Future<ServiceObject> get(String serviceId) { | 68 Future<ServiceObject> get(String serviceId) { |
64 if (_scripts.cachesId(serviceId)) { | 69 if (_scripts.cachesId(serviceId)) { |
65 return _scripts.get(serviceId); | 70 return _scripts.get(serviceId); |
66 } | 71 } |
67 if (_codes.cachesId(serviceId)) { | 72 if (_codes.cachesId(serviceId)) { |
68 return _codes.get(serviceId); | 73 return _codes.get(serviceId); |
69 } | 74 } |
70 if (_classes.cachesId(serviceId)) { | 75 if (_classes.cachesId(serviceId)) { |
71 return _classes.get(serviceId); | 76 return _classes.get(serviceId); |
72 } | 77 } |
78 if (_functions.cachesId(serviceId)) { | |
79 return _functions.get(serviceId); | |
80 } | |
73 return vm.getAsMap(relativeLink(serviceId)).then((ObservableMap m) { | 81 return vm.getAsMap(relativeLink(serviceId)).then((ObservableMap m) { |
74 return _upgradeToServiceObject(vm, this, m); | 82 return _upgradeToServiceObject(vm, this, m); |
75 }); | 83 }); |
76 } | 84 } |
77 | 85 |
78 @observable ServiceMap rootLib; | 86 @observable ServiceMap rootLib; |
79 @observable ObservableMap topFrame; | 87 @observable ObservableMap topFrame; |
80 | 88 |
81 @observable String name; | 89 @observable String name; |
82 @observable String vmName; | 90 @observable String vmName; |
(...skipping 313 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
396 } | 404 } |
397 | 405 |
398 @reflectable String formattedInclusive(Code code) { | 406 @reflectable String formattedInclusive(Code code) { |
399 if (code == null) { | 407 if (code == null) { |
400 return ''; | 408 return ''; |
401 } | 409 } |
402 var tick = code.addressTicks[address]; | 410 var tick = code.addressTicks[address]; |
403 if (tick == null) { | 411 if (tick == null) { |
404 return ''; | 412 return ''; |
405 } | 413 } |
414 // Don't show inclusive ticks if they are the same as exclusive ticks. | |
415 if (tick.inclusiveTicks == tick.exclusiveTicks) { | |
416 return ''; | |
417 } | |
406 var pcent = formatPercent(tick.inclusiveTicks, code.totalSamplesInProfile); | 418 var pcent = formatPercent(tick.inclusiveTicks, code.totalSamplesInProfile); |
407 return '$pcent (${tick.inclusiveTicks})'; | 419 return '$pcent (${tick.inclusiveTicks})'; |
408 } | 420 } |
409 | 421 |
410 @reflectable String formattedExclusive(Code code) { | 422 @reflectable String formattedExclusive(Code code) { |
411 if (code == null) { | 423 if (code == null) { |
412 return ''; | 424 return ''; |
413 } | 425 } |
414 var tick = code.addressTicks[address]; | 426 var tick = code.addressTicks[address]; |
415 if (tick == null) { | 427 if (tick == null) { |
416 return ''; | 428 return ''; |
417 } | 429 } |
418 var pcent = formatPercent(tick.exclusiveTicks, code.totalSamplesInProfile); | 430 var pcent = formatPercent(tick.exclusiveTicks, code.totalSamplesInProfile); |
419 return '$pcent (${tick.exclusiveTicks})'; | 431 return '$pcent (${tick.exclusiveTicks})'; |
420 } | 432 } |
421 } | 433 } |
422 | 434 |
423 class CodeKind { | 435 class CodeKind { |
424 final _value; | 436 final _value; |
425 const CodeKind._internal(this._value); | 437 const CodeKind._internal(this._value); |
426 String toString() => 'CodeKind.$_value'; | 438 String toString() => '$_value'; |
427 | 439 |
428 static CodeKind fromString(String s) { | 440 static CodeKind fromString(String s) { |
429 if (s == 'Native') { | 441 if (s == 'Native') { |
430 return Native; | 442 return Native; |
431 } else if (s == 'Dart') { | 443 } else if (s == 'Dart') { |
432 return Dart; | 444 return Dart; |
433 } else if (s == 'Collected') { | 445 } else if (s == 'Collected') { |
434 return Collected; | 446 return Collected; |
447 } else if (s == 'Reused') { | |
448 return Reused; | |
turnidge
2014/03/13 18:09:09
What does Reused mean? The same exact code at the
Cutch
2014/03/13 20:52:26
The address is used by a newer piece of code.
| |
435 } | 449 } |
450 Logger.root.warning('Unknown code kind $s'); | |
436 throw new FallThroughError(); | 451 throw new FallThroughError(); |
437 } | 452 } |
438 static const Native = const CodeKind._internal('Native'); | 453 static const Native = const CodeKind._internal('Native'); |
439 static const Dart = const CodeKind._internal('Dart'); | 454 static const Dart = const CodeKind._internal('Dart'); |
440 static const Collected = const CodeKind._internal('Collected'); | 455 static const Collected = const CodeKind._internal('Collected'); |
456 static const Reused = const CodeKind._internal('Reused'); | |
441 } | 457 } |
442 | 458 |
443 class CodeCallCount { | 459 class CodeCallCount { |
444 final Code code; | 460 final Code code; |
445 final int count; | 461 final int count; |
446 CodeCallCount(this.code, this.count); | 462 CodeCallCount(this.code, this.count); |
447 } | 463 } |
448 | 464 |
449 class Code extends ServiceObject { | 465 class Code extends ServiceObject { |
450 @observable CodeKind kind; | 466 @observable CodeKind kind; |
451 @observable int totalSamplesInProfile = 0; | 467 @observable int totalSamplesInProfile = 0; |
452 @reflectable int exclusiveTicks = 0; | 468 @reflectable int exclusiveTicks = 0; |
453 @reflectable int inclusiveTicks = 0; | 469 @reflectable int inclusiveTicks = 0; |
454 @reflectable int startAddress = 0; | 470 @reflectable int startAddress = 0; |
455 @reflectable int endAddress = 0; | 471 @reflectable int endAddress = 0; |
456 @reflectable final callers = new List<CodeCallCount>(); | 472 @reflectable final callers = new List<CodeCallCount>(); |
457 @reflectable final callees = new List<CodeCallCount>(); | 473 @reflectable final callees = new List<CodeCallCount>(); |
458 @reflectable final instructions = new ObservableList<CodeInstruction>(); | 474 @reflectable final instructions = new ObservableList<CodeInstruction>(); |
459 @reflectable final addressTicks = new ObservableMap<int, CodeTick>(); | 475 @reflectable final addressTicks = new ObservableMap<int, CodeTick>(); |
476 @observable String formattedInclusiveTicks = ''; | |
477 @observable String formattedExclusiveTicks = ''; | |
460 | 478 |
461 @observable ServiceMap function; | 479 @observable ServiceMap function; |
462 String name; | 480 String name; |
463 String vmName; | 481 String vmName; |
464 | 482 |
465 Code.fromMap(Isolate isolate, Map map) : super.fromMap(isolate, map); | 483 Code.fromMap(Isolate isolate, Map map) : super.fromMap(isolate, map); |
466 | 484 |
467 // Reset all data associated with a profile. | 485 // Reset all data associated with a profile. |
468 void resetProfileData() { | 486 void resetProfileData() { |
469 totalSamplesInProfile = 0; | 487 totalSamplesInProfile = 0; |
470 exclusiveTicks = 0; | 488 exclusiveTicks = 0; |
471 inclusiveTicks = 0; | 489 inclusiveTicks = 0; |
490 formattedInclusiveTicks = ''; | |
491 formattedExclusiveTicks = ''; | |
472 callers.clear(); | 492 callers.clear(); |
473 callees.clear(); | 493 callees.clear(); |
474 addressTicks.clear(); | 494 addressTicks.clear(); |
475 } | 495 } |
476 | 496 |
497 /// Reload [this]. Returns a future which completes to [this] or | |
498 /// a [ServiceError]. | |
499 Future<ServiceObject> reload() { | |
500 assert(kind != null); | |
501 if (kind == CodeKind.Dart) { | |
502 // We only reload Dart code. | |
503 return super.reload(); | |
504 } | |
505 return new Future.value(this); | |
506 } | |
507 | |
477 void _resolveCalls(List<CodeCallCount> calls, List data, List<Code> codes) { | 508 void _resolveCalls(List<CodeCallCount> calls, List data, List<Code> codes) { |
478 // Assert that this has been cleared. | 509 // Assert that this has been cleared. |
479 assert(calls.length == 0); | 510 assert(calls.length == 0); |
480 // Resolve. | 511 // Resolve. |
481 for (var i = 0; i < data.length; i += 2) { | 512 for (var i = 0; i < data.length; i += 2) { |
482 var index = int.parse(data[i]); | 513 var index = int.parse(data[i]); |
483 var count = int.parse(data[i + 1]); | 514 var count = int.parse(data[i + 1]); |
484 assert(index >= 0); | 515 assert(index >= 0); |
485 assert(index < codes.length); | 516 assert(index < codes.length); |
486 calls.add(new CodeCallCount(codes[index], count)); | 517 calls.add(new CodeCallCount(codes[index], count)); |
487 } | 518 } |
488 // Sort to descending count order. | 519 // Sort to descending count order. |
489 calls.sort((a, b) => b.count - a.count); | 520 calls.sort((a, b) => b.count - a.count); |
490 } | 521 } |
491 | 522 |
492 | 523 |
524 static String formatPercent(num a, num total) { | |
525 var percent = 100.0 * (a / total); | |
526 return '${percent.toStringAsFixed(2)}%'; | |
527 } | |
528 | |
493 void updateProfileData(Map profileData, | 529 void updateProfileData(Map profileData, |
494 List<Code> codeTable, | 530 List<Code> codeTable, |
495 int sampleCount) { | 531 int sampleCount) { |
496 // Assert we have a ProfileCode entry. | 532 // Assert we have a CodeRegion entry. |
497 assert(profileData['type'] == 'ProfileCode'); | 533 assert(profileData['type'] == 'CodeRegion'); |
498 // Assert we are handed profile data for this code object. | 534 // Assert we are handed profile data for this code object. |
499 assert(profileData['code'] == this); | 535 assert(profileData['code'] == this); |
500 totalSamplesInProfile = sampleCount; | 536 totalSamplesInProfile = sampleCount; |
501 inclusiveTicks = int.parse(profileData['inclusive_ticks']); | 537 inclusiveTicks = int.parse(profileData['inclusive_ticks']); |
502 exclusiveTicks = int.parse(profileData['exclusive_ticks']); | 538 exclusiveTicks = int.parse(profileData['exclusive_ticks']); |
503 _resolveCalls(callers, profileData['callers'], codeTable); | 539 _resolveCalls(callers, profileData['callers'], codeTable); |
504 _resolveCalls(callees, profileData['callees'], codeTable); | 540 _resolveCalls(callees, profileData['callees'], codeTable); |
505 var ticks = profileData['ticks']; | 541 var ticks = profileData['ticks']; |
506 if (ticks != null) { | 542 if (ticks != null) { |
507 _processTicks(ticks); | 543 _processTicks(ticks); |
508 } | 544 } |
545 formattedInclusiveTicks = | |
546 '${formatPercent(inclusiveTicks, totalSamplesInProfile)} ' | |
547 '($inclusiveTicks)'; | |
548 formattedExclusiveTicks = | |
549 '${formatPercent(exclusiveTicks, totalSamplesInProfile)} ' | |
550 '($inclusiveTicks)'; | |
509 } | 551 } |
510 | 552 |
511 void _update(ObservableMap m) { | 553 void _update(ObservableMap m) { |
512 assert(ServiceObject.isServiceMap(m)); | 554 assert(ServiceObject.isServiceMap(m)); |
513 assert(m['id'] == _id); | 555 assert(m['id'] == _id); |
514 assert(ServiceObject.stripRef(m['type']) == _serviceType); | 556 assert(ServiceObject.stripRef(m['type']) == _serviceType); |
515 name = m['user_name']; | 557 name = m['user_name']; |
516 vmName = m['name']; | 558 vmName = m['name']; |
559 kind = CodeKind.fromString(m['kind']); | |
517 startAddress = int.parse(m['start'], radix:16); | 560 startAddress = int.parse(m['start'], radix:16); |
518 endAddress = int.parse(m['end'], radix:16); | 561 endAddress = int.parse(m['end'], radix:16); |
519 // Upgrade the function. | 562 // Upgrade the function. |
520 function = _upgradeToServiceObject(isolate.vm, isolate, m['function']); | 563 function = _upgradeToServiceObject(isolate.vm, isolate, m['function']); |
521 var disassembly = m['disassembly']; | 564 var disassembly = m['disassembly']; |
522 if (disassembly != null) { | 565 if (disassembly != null) { |
523 _processDisassembly(disassembly); | 566 _processDisassembly(disassembly); |
524 } | 567 } |
525 // We are a reference if we don't have instructions. | 568 // We are a reference if we don't have instructions and are Dart code. |
526 _ref = (instructions.length == 0); | 569 _ref = (instructions.length == 0) && (kind == CodeKind.Dart); |
570 hasDisassembly = (instructions.length != 0) && (kind == CodeKind.Dart); | |
527 } | 571 } |
528 | 572 |
573 @observable bool hasDisassembly = false; | |
574 | |
529 void _processDisassembly(List<String> disassembly){ | 575 void _processDisassembly(List<String> disassembly){ |
530 assert(disassembly != null); | 576 assert(disassembly != null); |
531 instructions.clear(); | 577 instructions.clear(); |
532 assert((disassembly.length % 3) == 0); | 578 assert((disassembly.length % 3) == 0); |
533 for (var i = 0; i < disassembly.length; i += 3) { | 579 for (var i = 0; i < disassembly.length; i += 3) { |
534 var address = 0; // Assume code comment. | 580 var address = 0; // Assume code comment. |
535 var machine = disassembly[i + 1]; | 581 var machine = disassembly[i + 1]; |
536 var human = disassembly[i + 2]; | 582 var human = disassembly[i + 2]; |
537 if (disassembly[i] != '') { | 583 if (disassembly[i] != '') { |
538 // Not a code comment, extract address. | 584 // Not a code comment, extract address. |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
579 | 625 |
580 int _callCount(List<CodeCallCount> calls, Code code) { | 626 int _callCount(List<CodeCallCount> calls, Code code) { |
581 for (CodeCallCount caller in calls) { | 627 for (CodeCallCount caller in calls) { |
582 if (caller.code == code) { | 628 if (caller.code == code) { |
583 return caller.count; | 629 return caller.count; |
584 } | 630 } |
585 } | 631 } |
586 return 0; | 632 return 0; |
587 } | 633 } |
588 } | 634 } |
OLD | NEW |