OLD | NEW |
---|---|
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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 /// This library defines runtime operations on objects used by the code | 5 /// This library defines runtime operations on objects used by the code |
6 /// generator. | 6 /// generator. |
7 part of dart._runtime; | 7 part of dart._runtime; |
8 | 8 |
9 class InvocationImpl extends Invocation { | 9 class InvocationImpl extends Invocation { |
10 final Symbol memberName; | 10 final Symbol memberName; |
(...skipping 346 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
357 (resolvedField) => _callMethod(obj, resolvedField, typeArgs, args, method)); | 357 (resolvedField) => _callMethod(obj, resolvedField, typeArgs, args, method)); |
358 | 358 |
359 dsendRepl(obj, method, @rest args) => _callMethodRepl(obj, method, null, args); | 359 dsendRepl(obj, method, @rest args) => _callMethodRepl(obj, method, null, args); |
360 | 360 |
361 dgsendRepl(obj, typeArgs, method, @rest args) => | 361 dgsendRepl(obj, typeArgs, method, @rest args) => |
362 _callMethodRepl(obj, method, typeArgs, args); | 362 _callMethodRepl(obj, method, typeArgs, args); |
363 | 363 |
364 class _MethodStats { | 364 class _MethodStats { |
365 final String typeName; | 365 final String typeName; |
366 final String frame; | 366 final String frame; |
367 int count; | 367 double count; |
368 | 368 |
369 _MethodStats(this.typeName, this.frame) { | 369 _MethodStats(this.typeName, this.frame) { |
370 count = 0; | 370 count = 0.0; |
371 } | 371 } |
372 } | 372 } |
373 | 373 |
374 class _CallMethodRecord { | |
375 var jsError; | |
376 var type; | |
377 | |
378 _CallMethodRecord(this.jsError, this.type); | |
379 } | |
380 | |
374 Map<String, _MethodStats> _callMethodStats = new Map(); | 381 Map<String, _MethodStats> _callMethodStats = new Map(); |
375 | 382 |
383 /// Size for the random sample of dynamic calls. | |
vsm
2017/04/12 21:40:46
this is perhaps big enough to move out to a separa
Jacob
2017/04/12 23:42:09
Agreed. I was feeling bad about this polluting ope
| |
384 int _callRecordSampleSize = 5000; | |
385 | |
386 /// If the number of dynamic calls exceeds [_callRecordSampleSize] this list | |
387 /// will represent a random sample of the dynamic calls made. | |
388 List<_CallMethodRecord> _callMethodRecords = new List(); | |
389 | |
390 /// If the number of dynamic calls exceeds [_callRecordSampleSize] this value | |
391 /// will be greater than [_callMethodRecords.length]. | |
392 int _totalCallRecords = 0; | |
393 | |
394 /// Minimum number of samples to consider a profile entry relevant. | |
395 /// This could be set a lot higher. We set this value so users are not | |
396 /// confused into thinking that a dynamic call that occurred once but was | |
397 /// randomly included in the sample is relevant. | |
398 num _minCount = 2; | |
399 | |
400 /// Cache mapping from raw stack frames to source mapped stack frames to | |
401 /// speedup lookup of source map frames when running the profiler. | |
402 /// The number of source map entries looked up makes caching more important | |
403 /// in this case than for typical source map use cases. | |
404 Map<String, String> _frameMappingCache = new Map(); | |
405 | |
376 List<List<Object>> getDynamicStats() { | 406 List<List<Object>> getDynamicStats() { |
377 List<List<Object>> ret = []; | 407 List<List<Object>> ret = []; |
408 // Process the accumulated method stats. This may be quite slow as processing | |
409 // stack traces is expensive. If there are performance blockers, we should | |
410 // switch to a sampling approach that caps the number of _callMethodRecords | |
411 // and uses random sampling to decide whether to add each additional record | |
412 // to the sample. Main change required is that we need to still show the total | |
413 // raw number of dynamic calls so that the magnitude of the dynamic call | |
414 // performance hit is clear to users. | |
378 | 415 |
416 if (_callMethodRecords.length > 0) { | |
417 // Ratio between total record count and sampled records count. | |
418 var recordRatio = _totalCallRecords / _callMethodRecords.length; | |
419 for (var record in _callMethodRecords) { | |
420 var stackStr = JS('String', '#.stack', record.jsError); | |
421 // Truncate the stacktrace as we are only interested in the top 3-4 frames . | |
vsm
2017/04/12 21:40:47
nit: line len
Jacob
2017/04/12 23:42:10
Done. Fixed comment.
| |
422 var frames = stackStr.split('\n'); | |
423 var src = ''; | |
424 for (int i = 2; i < frames.length; ++i) { | |
425 var frame = frames[i]; | |
426 var mappedFrame = _frameMappingCache.putIfAbsent(frame, () { | |
427 return stackTraceMapper('\n${frame}'); | |
428 }); | |
429 if (!mappedFrame.contains( | |
430 'dart:_internal/js_runtime/lib/ddc_runtime/operations.dart')) { | |
vsm
2017/04/12 21:40:47
this is a weird uri - are we replacing some prefix
Jacob
2017/04/12 23:42:10
Yeah these are wrong. I need to use libraries.dart
| |
431 src = mappedFrame; | |
432 break; | |
433 } | |
434 } | |
435 | |
436 var actualTypeName = typeName(record.type); | |
437 _callMethodStats | |
438 .putIfAbsent("$actualTypeName <$src>", | |
439 () => new _MethodStats(actualTypeName, src)) | |
440 .count += recordRatio; | |
441 } | |
442 | |
443 for (var k in _callMethodStats.keys.toList()) { | |
444 var stats = _callMethodStats[k]; | |
445 // filter out all calls that did not occur at least _minCount times in the | |
446 // random sample. | |
447 var threshold = _minCount * recordRatio; | |
448 if (stats.count + 0.001 < threshold) { | |
vsm
2017/04/12 21:40:46
why the 0.001?
Jacob
2017/04/12 23:42:09
epsilon to avoid rounding issues. done.
| |
449 _callMethodStats.remove(k); | |
450 } | |
451 } | |
452 } | |
453 _callMethodRecords.clear(); | |
454 _totalCallRecords = 0; | |
379 var keys = _callMethodStats.keys.toList(); | 455 var keys = _callMethodStats.keys.toList(); |
380 | 456 |
381 keys.sort( | 457 keys.sort( |
382 (a, b) => _callMethodStats[b].count.compareTo(_callMethodStats[a].count)); | 458 (a, b) => _callMethodStats[b].count.compareTo(_callMethodStats[a].count)); |
383 for (var key in keys) { | 459 for (var key in keys) { |
384 var stats = _callMethodStats[key]; | 460 var stats = _callMethodStats[key]; |
385 ret.add([stats.typeName, stats.frame, stats.count]); | 461 ret.add([stats.typeName, stats.frame, stats.count.round()]); |
386 } | 462 } |
387 | 463 |
388 return ret; | 464 return ret; |
389 } | 465 } |
390 | 466 |
391 clearDynamicStats() { | 467 clearDynamicStats() { |
392 _callMethodStats.clear(); | 468 _callMethodStats.clear(); |
469 _callMethodRecords.clear(); | |
393 } | 470 } |
394 | 471 |
395 bool trackProfile = JS('bool', 'dart.global.trackDdcProfile'); | 472 bool trackProfile = JS('bool', 'dart.global.trackDdcProfile'); |
396 | 473 |
397 _trackCall(obj) { | 474 _trackCall(obj) { |
398 if (JS('bool', '!#', trackProfile)) return; | 475 if (JS('bool', '!#', trackProfile)) return; |
399 | 476 int index = -1; |
400 var actual = getReifiedType(obj); | 477 _totalCallRecords++; |
401 String stackStr = JS('String', "new Error().stack"); | 478 if (_callMethodRecords.length == _callRecordSampleSize) { |
402 var stack = stackStr.split('\n at '); | 479 // Unfortunately we can't use the excellent Random.nextInt method defined |
403 var src = ''; | 480 // by Dart from within this library. |
404 for (int i = 2; i < stack.length; ++i) { | 481 index = JS('int', 'Math.floor(Math.random() * #)', _totalCallRecords); |
405 var frame = stack[i]; | 482 if (index >= _callMethodRecords.length) return; // don't sample |
406 if (!frame.contains('dart_sdk.js')) { | |
407 src = frame; | |
408 break; | |
409 } | |
410 } | 483 } |
411 | 484 var record = |
412 var actualTypeName = typeName(actual); | 485 new _CallMethodRecord(JS('', 'new Error()'), getReifiedType(obj)); |
413 _callMethodStats | 486 if (index == -1) { |
414 .putIfAbsent( | 487 _callMethodRecords.add(record); |
415 "$actualTypeName <$src>", () => new _MethodStats(actualTypeName, src)) | 488 } else { |
416 .count++; | 489 _callMethodRecords[index] = record; |
490 } | |
vsm
2017/04/12 21:40:47
Interesting sampling strategy! Looks like entries
Jacob
2017/04/12 23:42:09
Last one doesn't win because we randomly choose to
vsm
2017/04/13 18:17:49
Clever! And makes sense to me.
Worth a comment
| |
417 } | 491 } |
418 | 492 |
419 /// Shared code for dsend, dindex, and dsetindex. | 493 /// Shared code for dsend, dindex, and dsetindex. |
420 _callMethod(obj, name, typeArgs, args, displayName) { | 494 _callMethod(obj, name, typeArgs, args, displayName) { |
421 var symbol = _canonicalMember(obj, name); | 495 var symbol = _canonicalMember(obj, name); |
422 if (symbol == null) { | 496 if (symbol == null) { |
423 return noSuchMethod( | 497 return noSuchMethod( |
424 obj, new InvocationImpl(displayName, args, isMethod: true)); | 498 obj, new InvocationImpl(displayName, args, isMethod: true)); |
425 } | 499 } |
426 var f = obj != null ? JS('', '#[#]', obj, symbol) : null; | 500 var f = obj != null ? JS('', '#[#]', obj, symbol) : null; |
(...skipping 556 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
983 name = '+' + name; | 1057 name = '+' + name; |
984 } | 1058 } |
985 return name; | 1059 return name; |
986 } | 1060 } |
987 | 1061 |
988 /// Emulates the implicit "loadLibrary" function provided by a deferred library. | 1062 /// Emulates the implicit "loadLibrary" function provided by a deferred library. |
989 /// | 1063 /// |
990 /// Libraries are not actually deferred in DDC, so this just returns a future | 1064 /// Libraries are not actually deferred in DDC, so this just returns a future |
991 /// that completes immediately. | 1065 /// that completes immediately. |
992 Future loadLibrary() => new Future.value(); | 1066 Future loadLibrary() => new Future.value(); |
OLD | NEW |