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 // Randomly sampled list of call records |
| 384 List<_CallMethodRecord> _callMethodRecords = new List(); |
| 385 int totalCallRecords = 0; |
| 386 int _callRecordSampleSize = 5000; |
| 387 num _minCount = 0; |
| 388 |
| 389 /// Mapping from raw stack frames to source mapped stack frames to speedup |
| 390 /// lookup of source map frames. |
| 391 Map<String, String> _frameMappingCache = new Map(); |
| 392 |
376 List<List<Object>> getDynamicStats() { | 393 List<List<Object>> getDynamicStats() { |
377 List<List<Object>> ret = []; | 394 List<List<Object>> ret = []; |
| 395 // Process the accumulated method stats. This may be quite slow as processing |
| 396 // stack traces is expensive. If there are performance blockers, we should |
| 397 // switch to a sampling approach that caps the number of _callMethodRecords |
| 398 // and uses random sampling to decide whether to add each additional record |
| 399 // to the sample. Main change required is that we need to still show the total |
| 400 // raw number of dynamic calls so that the magnitude of the dynamic call |
| 401 // performance hit is clear to users. |
378 | 402 |
| 403 // Minimum number of samples to consider a profile entry relevant. |
| 404 // This could be set a lot higher. We set this value so users are not |
| 405 // confused into thinking that a dynamic call that occurred once but was |
| 406 // randomly included in the sample is relevant. |
| 407 if (_callMethodRecords.length > 0) { |
| 408 // Ratio between total record count and sampled records count. |
| 409 var recordRatio = totalCallRecords / _callMethodRecords.length; |
| 410 _minCount = recordRatio * 1.99; |
| 411 for (var record in _callMethodRecords) { |
| 412 var stackStr = JS('String', '#.stack', record.jsError); |
| 413 // Truncate the stacktrace as we are only interested in the top 3-4 frames
. |
| 414 var frames = stackStr.split('\n'); |
| 415 var src = ''; |
| 416 for (int i = 2; i < frames.length; ++i) { |
| 417 var frame = frames[i]; |
| 418 var mappedFrame = _frameMappingCache.putIfAbsent(frame, () { |
| 419 return stackTraceMapper('\n${frame}'); |
| 420 }); |
| 421 if (!mappedFrame.contains( |
| 422 'dart:_internal/js_runtime/lib/ddc_runtime/operations.dart')) { |
| 423 src = mappedFrame; |
| 424 break; |
| 425 } |
| 426 } |
| 427 |
| 428 var actualTypeName = typeName(record.type); |
| 429 _callMethodStats |
| 430 .putIfAbsent("$actualTypeName <$src>", |
| 431 () => new _MethodStats(actualTypeName, src)) |
| 432 .count += recordRatio; |
| 433 } |
| 434 } |
| 435 // filter out |
| 436 _callMethodRecords.clear(); |
| 437 totalCallRecords = 0; |
379 var keys = _callMethodStats.keys.toList(); | 438 var keys = _callMethodStats.keys.toList(); |
380 | 439 |
381 keys.sort( | 440 keys.sort( |
382 (a, b) => _callMethodStats[b].count.compareTo(_callMethodStats[a].count)); | 441 (a, b) => _callMethodStats[b].count.compareTo(_callMethodStats[a].count)); |
383 for (var key in keys) { | 442 for (var key in keys) { |
384 var stats = _callMethodStats[key]; | 443 var stats = _callMethodStats[key]; |
385 ret.add([stats.typeName, stats.frame, stats.count]); | 444 if (_minCount < stats.count) { |
| 445 ret.add([stats.typeName, stats.frame, stats.count.round()]); |
| 446 } |
386 } | 447 } |
387 | 448 |
388 return ret; | 449 return ret; |
389 } | 450 } |
390 | 451 |
391 clearDynamicStats() { | 452 clearDynamicStats() { |
392 _callMethodStats.clear(); | 453 _callMethodStats.clear(); |
| 454 _callMethodRecords.clear(); |
393 } | 455 } |
394 | 456 |
395 bool trackProfile = JS('bool', 'dart.global.trackDdcProfile'); | 457 bool trackProfile = JS('bool', 'dart.global.trackDdcProfile'); |
396 | 458 |
397 _trackCall(obj) { | 459 _trackCall(obj) { |
398 if (JS('bool', '!#', trackProfile)) return; | 460 if (JS('bool', '!#', trackProfile)) return; |
399 | 461 int index = -1; |
400 var actual = getReifiedType(obj); | 462 totalCallRecords++; |
401 String stackStr = JS('String', "new Error().stack"); | 463 if (_callMethodRecords.length == _callRecordSampleSize) { |
402 var stack = stackStr.split('\n at '); | 464 // Unfortunately we can't use the excellent Random.nextInt method defined |
403 var src = ''; | 465 // by Dart from within this library. |
404 for (int i = 2; i < stack.length; ++i) { | 466 index = JS('int', 'Math.floor(Math.random() * #)', totalCallRecords); |
405 var frame = stack[i]; | 467 if (index >= _callMethodRecords.length) return; // don't sample |
406 if (!frame.contains('dart_sdk.js')) { | |
407 src = frame; | |
408 break; | |
409 } | |
410 } | 468 } |
411 | 469 var record = |
412 var actualTypeName = typeName(actual); | 470 new _CallMethodRecord(JS('', 'new Error()'), getReifiedType(obj)); |
413 _callMethodStats | 471 if (index == -1) { |
414 .putIfAbsent( | 472 _callMethodRecords.add(record); |
415 "$actualTypeName <$src>", () => new _MethodStats(actualTypeName, src)) | 473 } else { |
416 .count++; | 474 _callMethodRecords[index] = record; |
| 475 } |
417 } | 476 } |
418 | 477 |
419 /// Shared code for dsend, dindex, and dsetindex. | 478 /// Shared code for dsend, dindex, and dsetindex. |
420 _callMethod(obj, name, typeArgs, args, displayName) { | 479 _callMethod(obj, name, typeArgs, args, displayName) { |
421 var symbol = _canonicalMember(obj, name); | 480 var symbol = _canonicalMember(obj, name); |
422 if (symbol == null) { | 481 if (symbol == null) { |
423 return noSuchMethod( | 482 return noSuchMethod( |
424 obj, new InvocationImpl(displayName, args, isMethod: true)); | 483 obj, new InvocationImpl(displayName, args, isMethod: true)); |
425 } | 484 } |
426 var f = obj != null ? JS('', '#[#]', obj, symbol) : null; | 485 var f = obj != null ? JS('', '#[#]', obj, symbol) : null; |
(...skipping 556 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
983 name = '+' + name; | 1042 name = '+' + name; |
984 } | 1043 } |
985 return name; | 1044 return name; |
986 } | 1045 } |
987 | 1046 |
988 /// Emulates the implicit "loadLibrary" function provided by a deferred library. | 1047 /// Emulates the implicit "loadLibrary" function provided by a deferred library. |
989 /// | 1048 /// |
990 /// Libraries are not actually deferred in DDC, so this just returns a future | 1049 /// Libraries are not actually deferred in DDC, so this just returns a future |
991 /// that completes immediately. | 1050 /// that completes immediately. |
992 Future loadLibrary() => new Future.value(); | 1051 Future loadLibrary() => new Future.value(); |
OLD | NEW |