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

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

Powered by Google App Engine
This is Rietveld 408576698