Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 } | |
| OLD | NEW |