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

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

Issue 192443004: Complete the switch to ServiceObject (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: 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
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 part of service;
6
7 /// State for a running isolate.
8 class Isolate extends ServiceObject {
9 final VM vm;
10 String get link => _id;
11 String get hashLink => '#/$_id';
12
13 ScriptCache _scripts;
turnidge 2014/03/10 21:03:28 consider blank lines between decls.
14 /// Script cache.
15 ScriptCache get scripts => _scripts;
16 CodeCache _codes;
17 /// Code cache.
18 CodeCache get codes => _codes;
19 /// Class cache.
20 ClassCache _classes;
21 ClassCache get classes => _classes;
22
23 void _initOnce() {
24 // Only called once.
25 assert(_isolate == null);
26 _isolate = this;
27 _scripts = new ScriptCache(this);
28 _codes = new CodeCache(this);
29 _classes = new ClassCache(this);
30 }
31
32 Isolate.fromId(this.vm, String id) : super(null, id, '@Isolate') {
33 _initOnce();
34 }
35
36 Isolate.fromMap(this.vm, Map map) : super.fromMap(null, map) {
37 _initOnce();
38 }
39
40 /// Creates a link to [id] relative to [this].
41 @reflectable String relativeLink(String id) => '${this.id}/$id';
42 /// Creates a relative link to [id] with a '#/' prefix.
43 @reflectable String relativeHashLink(String id) => '#/${relativeLink(id)}';
44
45 Future<ScriptCache> refreshCoverage() {
46 return fetch('coverage').then(_scripts._processCoverage);
47 }
48
49 void processProfile(ServiceMap profile) {
50 assert(profile.serviceType == 'Profile');
51 var codeTable = new List<Code>();
52 var profileCodes = profile['codes'];
53 for (var profileCode in profileCodes) {
54 Code code = profileCode['code'];
55 codeTable.add(code);
56 }
57 _codes._resetProfileData();
58 _codes._updateProfileData(profile, codeTable);
59 }
60
61 /// Requests [serviceId] from [this]. Completes to a [ServiceObject].
62 /// Can return pre-existing, cached, [ServiceObject]s.
63 Future<ServiceObject> get(String serviceId) {
64 if (_scripts.cachesId(serviceId)) {
65 return _scripts.get(serviceId);
66 }
67 if (_codes.cachesId(serviceId)) {
68 return _codes.get(serviceId);
69 }
70 if (_classes.cachesId(serviceId)) {
71 return _classes.get(serviceId);
72 }
73 return vm.get(this, relativeLink(serviceId));
74 }
75
76 /// Fetches [serviceId] from [this]. Completes to a [ServiceObject].
77 /// Always creates a new ServiceObject.
78 Future<ServiceObject> fetch(String serviceId, [Map options]) {
turnidge 2014/03/10 21:03:28 I find the distinction between the names "get" and
79 return vm.get(this, relativeLink(serviceId));
80 }
81
82
turnidge 2014/03/10 21:03:28 Remove one blank line here?
83 @observable ServiceMap rootLib;
84 @observable ServiceMap topFrame;
85
86 @observable String name;
87 @observable String vmName;
88 @observable Map entry;
89
90 @observable final Map<String, double> timers =
91 toObservable(new Map<String, double>());
92
93 @observable int newHeapUsed = 0;
94 @observable int oldHeapUsed = 0;
95
96 @observable String fileAndLine;
97
98 void _update(ObservableMap map) {
99 if (map['type'] == '@Isolate') {
100 if (isRef()) {
101 // Eagerly derefence isolates.
102 deref();
103 }
104 return;
105 }
106 _ref = false;
107 if (map['rootLib'] == null ||
108 map['timers'] == null ||
109 map['heap'] == null) {
110 Logger.root.severe("Malformed 'Isolate' response: $map");
111 return;
112 }
113 rootLib = new ServiceMap.fromMap(this, map['rootLib']);
114 vmName = map['name'];
115 if (map['entry'] != null) {
116 entry = map['entry'];
117 name = entry['name'];
118 } else {
119 // fred
120 name = 'root isolate';
121 }
122 if (map['topFrame'] != null) {
123 topFrame = new ServiceMap.fromMap(this, map['topFrame']);
124 } else {
125 topFrame = null ;
126 }
127
128 var timerMap = {};
129 map['timers'].forEach((timer) {
130 timerMap[timer['name']] = timer['time'];
131 });
132 timers['total'] = timerMap['time_total_runtime'];
133 timers['compile'] = timerMap['time_compilation'];
134 timers['gc'] = 0.0; // TODO(turnidge): Export this from VM.
135 timers['init'] = (timerMap['time_script_loading'] +
136 timerMap['time_creating_snapshot'] +
137 timerMap['time_isolate_initialization'] +
138 timerMap['time_bootstrap']);
139 timers['dart'] = timerMap['time_dart_execution'];
140
141 newHeapUsed = map['heap']['usedNew'];
142 oldHeapUsed = map['heap']['usedOld'];
143 }
144 }
145
146 // TODO(johnmccutchan): Make this into an IsolateCache.
147 class IsolateList extends ServiceObject {
148 final VM _vm;
149 VM get vm => _vm;
150 @observable final ObservableMap isolates = new ObservableMap();
151 IsolateList(this._vm) : super(null, 'isolates', 'IsolateList') {
152 name = 'IsolateList';
153 vmName = name;
154 }
155 IsolateList.fromMap(this._vm, Map m) : super.fromMap(null, m) {
156 name = 'IsolateList';
157 vmName = name;
158 }
159
160 Future<ServiceObject> refresh() {
161 return vm.fetchMap(id).then(update);
162 }
163
164 void _update(ObservableMap map) {
165 _updateIsolates(map['members']);
166 }
167
168 void _updateIsolates(List<Map> members) {
169 // Find dead isolates.
170 var deadIsolates = [];
171 isolates.forEach((k, v) {
172 if (!_foundIsolateInMembers(k, members)) {
173 deadIsolates.add(k);
174 }
175 });
176 // Remove them.
177 deadIsolates.forEach((id) {
178 isolates.remove(id);
179 Logger.root.info('Isolate \'$id\' has gone away.');
180 });
181
182 // Add new isolates.
183 members.forEach((map) {
184 var id = map['id'];
185 var isolate = isolates[id];
186 if (isolate == null) {
187 isolate = new Isolate.fromMap(vm, map);
188 Logger.root.info('Created ServiceObject for \'${isolate.id}\' with '
189 'type \'${isolate.serviceType}\'');
190 isolates[id] = isolate;
191 }
192 });
193
194 // After updating the isolate list, refresh each isolate.
195 _refreshIsolates();
196 }
197
198 void _refreshIsolates() {
199 // This is technically asynchronous but we don't need to wait for
200 // the result.
201 isolates.forEach((k, Isolate isolate) {
202 isolate.refresh();
203 });
204 }
205
206 Isolate getIsolate(String id) {
207 assert(id.startsWith('isolates/'));
208 var isolate = isolates[id];
209 if (isolate != null) {
210 return isolate;
211 }
212 isolate = new Isolate.fromId(vm, id);
213 isolates[id] = isolate;
214 isolate.deref();
215 return isolate;
216 }
217
218 static bool _foundIsolateInMembers(String id, List<Map> members) {
219 return members.any((E) => E['id'] == id);
220 }
221 }
222
223
224 /// A [ServiceObject] which implements [ObservableMap].
225 class ServiceMap extends ServiceObject implements ObservableMap {
226 final ObservableMap _map = new ObservableMap();
227 ServiceMap(Isolate isolate, String id, String serviceType) :
228 super(isolate, id, serviceType) {
229 }
230
231 ServiceMap.fromMap(Isolate isolate, ObservableMap m) :
232 super.fromMap(isolate, m);
233
234 bool _isUpgradeTarget(v) {
235 return (v is Map) && ServiceObject.isServiceMap(v);
236 }
237
238 String toString() => _map.toString();
239
240 ServiceObject _upgrade(Map m) {
241 var upgraded = _upgradeToServiceObject(isolate.vm, isolate, m);
242 if (upgraded is ServiceMap) {
243 // Recurse.
244 upgraded._upgradeValues();
245 }
246 return upgraded;
247 }
248
249 void _upgradeList(List l) {
250 for (var i = 0; i < l.length; i++) {
251 if (_isUpgradeTarget(l[i])) {
252 l[i] = _upgrade(l[i]);
253 } else if (l[i] is List) {
254 _upgradeList(l[i]);
255 } else if (l[i] is Map) {
256 _upgradeMap(l[i]);
257 }
258 }
259 }
260
261 void _upgradeMap(Map m) {
262 m.forEach((k, v) {
263 if (_isUpgradeTarget(v)) {
264 m[k] = _upgrade(v);
265 } else if (v is List) {
266 _upgradeList(v);
267 } else if (v is Map) {
268 _upgradeMap(v);
269 }
270 });
271 }
272
273 void _upgradeValues() {
274 assert(isolate != null);
275 _upgradeMap(_map);
276 }
277
278 void _update(ObservableMap m) {
279 _map.clear();
280 _map.addAll(m);
281 name = _map['user_name'];
282 vmName = _map['name'];
283 _upgradeValues();
284 }
285
286 // Forward Map interface calls.
287 void addAll(Map other) => _map.addAll(other);
288 void clear() => _map.clear();
289 bool containsValue(v) => _map.containsValue(v);
290 bool containsKey(k) => _map.containsKey(k);
291 void forEach(Function f) => _map.forEach(f);
292 putIfAbsent(key, Function ifAbsent) => _map.putIfAbsent(key, ifAbsent);
293 void remove(key) => _map.remove(key);
294 operator [](k) => _map[k];
295 operator []=(k, v) => _map[k] = v;
296 bool get isEmpty => _map.isEmpty;
297 bool get isNotEmpty => _map.isNotEmpty;
298 Iterable get keys => _map.keys;
299 Iterable get values => _map.values;
300 int get length => _map.length;
301
302 // Forward ChangeNotifier interface calls.
303 bool deliverChanges() => _map.deliverChanges();
304 void notifyChange(ChangeRecord record) => _map.notifyChange(record);
305 notifyPropertyChange(Symbol field, Object oldValue, Object newValue) =>
306 _map.notifyPropertyChange(field, oldValue, newValue);
307 void observed() => _map.observed();
308 void unobserved() => _map.unobserved();
309 Stream<List<ChangeRecord>> get changes => _map.changes;
310 bool get hasObservers => _map.hasObservers;
311 }
312
313 class ServiceError extends ServiceObject {
314 ServiceError.fromMap(Isolate isolate, Map m) : super.fromMap(isolate, m);
315
316 @observable String kind;
317 @observable String message;
318
319 void _update(ObservableMap map) {
320 kind = map['kind'];
321 message = map['message'];
322 name = 'ServiceError $kind';
323 vmName = name;
324 }
325
326 // TODO: stackTrace?
327 }
328
329 class ScriptLine {
330 @reflectable final int line;
331 @reflectable final String text;
332 ScriptLine(this.line, this.text);
333 }
334
335 class Script extends ServiceObject {
336 @reflectable final lines = new ObservableList<ScriptLine>();
337 @reflectable final hits = new ObservableMap<int, int>();
338 @observable ServiceObject library;
339 @observable String kind;
340
341 String _shortUrl;
342 String _url;
343
344 Script.fromMap(Isolate isolate, Map m) : super.fromMap(isolate, m);
345
346 void _update(ObservableMap m) {
347 // Assert that m is a service map.
348 assert(ServiceObject.isServiceMap(m));
349 if ((m['type'] == 'Error') && (m['kind'] == 'NotFoundError')) {
350 // TODO(johnmccutchan): Find out why dart:core/identical.dart can't
351 // be found but shows up in coverage. i.e. a function has reference
352 // to script that no library does.
353 Logger.root.info(m['message']);
354 return;
355 }
356 // Assert that the id hasn't changed.
357 assert(m['id'] == _id);
358 // Assert that the type hasn't changed.
359 assert(ServiceObject.unreffedType(m['type']) == _serviceType);
360 _url = m['name'];
361 _shortUrl = _url.substring(_url.lastIndexOf('/') + 1);
362 name = _shortUrl;
363 vmName = _url;
364 kind = m['kind'];
365 _processSource(m['source']);
366 }
367
368 void _processHits(List scriptHits) {
369 if (_ref) {
370 // Eagerly grab script source.
371 deref();
372 }
373 // Update hits table.
374 for (var i = 0; i < scriptHits.length; i += 2) {
375 var line = scriptHits[i];
376 var hit = scriptHits[i + 1]; // hit status.
377 assert(line >= 1); // Lines start at 1.
378 hits[line] = hit;
379 }
380 }
381
382 void _processSource(String source) {
383 // Preemptyively mark that this is a reference.
turnidge 2014/03/10 21:03:28 typo in comment.
384 _ref = true;
385 if (source == null) {
386 return;
387 }
388 var sourceLines = source.split('\n');
389 if (sourceLines.length == 0) {
390 return;
391 }
392 // We have the source to the script. This is no longer a reference.
393 _ref = false;
394 lines.clear();
395 Logger.root.info('Adding ${sourceLines.length} source lines for ${_url}');
396 for (var i = 0; i < sourceLines.length; i++) {
397 lines.add(new ScriptLine(i + 1, sourceLines[i]));
398 }
399 }
400
401
402 }
403
404 class CodeTick {
405 final int address;
406 final int exclusiveTicks;
407 final int inclusiveTicks;
408 CodeTick(this.address, this.exclusiveTicks, this.inclusiveTicks);
409 }
410
411
412 class CodeInstruction extends Observable {
413 @observable final int address;
414 @observable final String machine;
415 @observable final String human;
416
417 static String formatPercent(num a, num total) {
418 var percent = 100.0 * (a / total);
419 return '${percent.toStringAsFixed(2)}%';
420 }
421
422 CodeInstruction(this.address, this.machine, this.human);
423
424 @reflectable String formattedAddress() {
425 if (address == 0) {
426 return '';
427 }
428 return '0x${address.toRadixString(16)}';
429 }
430
431 @reflectable String formattedInclusive(Code code) {
432 if (code == null) {
433 return '';
434 }
435 var tick = code.addressTicks[address];
436 if (tick == null) {
437 return '';
438 }
439 var pcent = formatPercent(tick.inclusiveTicks, code.totalSamplesInProfile);
440 return '${tick.inclusiveTicks} ($pcent)';
441 }
442
443 @reflectable String formattedExclusive(Code code) {
444 if (code == null) {
445 return '';
446 }
447 var tick = code.addressTicks[address];
448 if (tick == null) {
449 return '';
450 }
451 var pcent = formatPercent(tick.exclusiveTicks, code.totalSamplesInProfile);
452 return '${tick.exclusiveTicks} ($pcent)';
453 }
454 }
455
456 class CodeKind {
457 final _value;
458 const CodeKind._internal(this._value);
459 String toString() => 'CodeKind.$_value';
460
461 static CodeKind fromString(String s) {
462 if (s == 'Native') {
463 return Native;
464 } else if (s == 'Dart') {
465 return Dart;
466 } else if (s == 'Collected') {
467 return Collected;
468 }
469 throw new FallThroughError();
470 }
471 static const Native = const CodeKind._internal('Native');
472 static const Dart = const CodeKind._internal('Dart');
473 static const Collected = const CodeKind._internal('Collected');
474 }
475
476 class CodeCallCount {
477 final Code code;
478 final int count;
479 CodeCallCount(this.code, this.count);
480 }
481
482 class Code extends ServiceObject {
483 @observable CodeKind kind;
484 @observable int totalSamplesInProfile = 0;
485 @reflectable int exclusiveTicks = 0;
486 @reflectable int inclusiveTicks = 0;
487 @reflectable int startAddress = 0;
488 @reflectable int endAddress = 0;
489 @reflectable final callers = new List<CodeCallCount>();
490 @reflectable final callees = new List<CodeCallCount>();
491 @reflectable final instructions = new ObservableList<CodeInstruction>();
492 @reflectable final addressTicks = new ObservableMap<int, CodeTick>();
493
494 @observable ServiceMap function;
495 String name;
496 String vmName;
497
498 Code.fromMap(Isolate isolate, Map map) : super.fromMap(isolate, map);
499
500 // Reset all data associated with a profile.
501 void resetProfileData() {
502 totalSamplesInProfile = 0;
503 exclusiveTicks = 0;
504 inclusiveTicks = 0;
505 callers.clear();
506 callees.clear();
507 addressTicks.clear();
508 }
509
510 void _resolveCalls(List<CodeCallCount> calls, List data, List<Code> codes) {
511 // Assert that this has been cleared.
512 assert(calls.length == 0);
513 // Resolve.
514 for (var i = 0; i < data.length; i += 2) {
515 var index = int.parse(data[i]);
516 var count = int.parse(data[i + 1]);
517 assert(index >= 0);
518 assert(index < codes.length);
519 calls.add(new CodeCallCount(codes[index], count));
520 }
521 // Sort to descending count order.
522 calls.sort((a, b) => b.count - a.count);
523 }
524
525
526 void updateProfileData(Map profileData,
527 List<Code> codeTable,
528 int sampleCount) {
529 // Assert we have a ProfileCode entry.
530 assert(profileData['type'] == 'ProfileCode');
531 // Assert we are handed profile data for this code object.
532 assert(profileData['code'] == this);
533 totalSamplesInProfile = sampleCount;
534 inclusiveTicks = int.parse(profileData['inclusive_ticks']);
535 exclusiveTicks = int.parse(profileData['exclusive_ticks']);
536 _resolveCalls(callers, profileData['callers'], codeTable);
537 _resolveCalls(callees, profileData['callees'], codeTable);
538 var ticks = profileData['ticks'];
539 if (ticks != null) {
540 _processTicks(ticks);
541 }
542 }
543
544 void _update(ObservableMap m) {
545 assert(ServiceObject.isServiceMap(m));
546 assert(m['id'] == _id);
547 assert(ServiceObject.unreffedType(m['type']) == _serviceType);
548 name = m['user_name'];
549 vmName = m['name'];
550 startAddress = int.parse(m['start'], radix:16);
551 endAddress = int.parse(m['end'], radix:16);
552 // Upgrade the function.
553 function = _upgradeToServiceObject(isolate.vm, isolate, m['function']);
554 var disassembly = m['disassembly'];
555 if (disassembly != null) {
556 _processDisassembly(disassembly);
557 }
558 // We are a reference if we don't have instructions.
559 _ref = (instructions.length == 0);
560 }
561
562 void _processDisassembly(List<String> disassembly){
563 assert(disassembly != null);
564 instructions.clear();
565 assert((disassembly.length % 3) == 0);
566 for (var i = 0; i < disassembly.length; i += 3) {
567 var address = 0; // Assume code comment.
568 var machine = disassembly[i + 1];
569 var human = disassembly[i + 2];
570 if (disassembly[i] != '') {
571 // Not a code comment, extract address.
572 address = int.parse(disassembly[i]);
573 }
574 var instruction = new CodeInstruction(address, machine, human);
575 instructions.add(instruction);
576 }
577 }
578
579 void _processTicks(List<String> profileTicks) {
580 assert(profileTicks != null);
581 assert((profileTicks.length % 3) == 0);
582 for (var i = 0; i < profileTicks.length; i += 3) {
583 var address = int.parse(profileTicks[i], radix:16);
584 var exclusive = int.parse(profileTicks[i + 1]);
585 var inclusive = int.parse(profileTicks[i + 2]);
586 var tick = new CodeTick(address, exclusive, inclusive);
587 addressTicks[address] = tick;
588 }
589 }
590
591 /// Returns true if [address] is contained inside [this].
592 bool contains(int address) {
593 return (address >= startAddress) && (address < endAddress);
594 }
595
596 /// Sum all caller counts.
597 int sumCallersCount() => _sumCallCount(callers);
598 /// Specific caller count.
599 int callersCount(Code code) => _callCount(callers, code);
600 /// Sum of callees count.
601 int sumCalleesCount() => _sumCallCount(callees);
602 /// Specific callee count.
603 int calleesCount(Code code) => _callCount(callees, code);
604
605 int _sumCallCount(List<CodeCallCount> calls) {
606 var sum = 0;
607 for (CodeCallCount caller in calls) {
608 sum += caller.count;
609 }
610 return sum;
611 }
612
613 int _callCount(List<CodeCallCount> calls, Code code) {
614 for (CodeCallCount caller in calls) {
615 if (caller.code == code) {
616 return caller.count;
617 }
618 }
619 return 0;
620 }
621 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698