Skip to content

Commit 972ad54

Browse files
buenaflordenrasegetsentry-botgetsentry-bot
authored
Release 8.14.2 (#2888)
* Fix: Improve platform memory collection on windows/linux (#2798) * deps: bump Android from `7.22.1` to `7.22.4` (#2810) * update android deps * Update CHANGELOG.md * Fix adding runtime to contexts (#2813) * Fix CHANGELOG formatting * release: 8.14.1 * fix: `options.diagnosticLevel` not affecting logs (#2856) * v9: Set log level to `warning` by default (#2836) * update * update test * update init native sdk test * Update CHANGELOG * Add additional test * Update CHANGELOG * Update CHANGELOG * Update * Update * Fix test * Fix test * Fix analyze * Remove prod scheme * Update mocks * Update mocks * Improve performance of frames tracking (#2854) * Improve performance * Update tests * Improve performance * Formatting * Separate function * Remove separate function * Update * Improve frames tracking performance * update naming * Update * update * update * Edge case * Update mocks * formatting * Improvements * Analyze * Update mocks * Update mocks * Clean up `getSpan()` log (#2865) * Remove unnecessary log and document the behaviour * Typo * Typo * Add CHANGELOG entry * release: 8.14.2 * Update * Update --------- Co-authored-by: Denis Andrašec <[email protected]> Co-authored-by: getsentry-bot <[email protected]> Co-authored-by: getsentry-bot <[email protected]>
2 parents db64b25 + 15b97e6 commit 972ad54

13 files changed

+156
-168
lines changed

CHANGELOG.md

+11
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,17 @@ await SentryFlutter.init(
6767
- [changelog](https://github.com/dart-lang/native/blob/main/pkgs/jni/CHANGELOG.md#0141)
6868
- [diff](https://github.com/dart-lang/native/compare/jnigen-v0.14.0..jnigen-v0.14.1)
6969

70+
## 8.14.2
71+
72+
### Improvements
73+
74+
- Improve performance of frames tracking ([#2854](https://github.com/getsentry/sentry-dart/pull/2854))
75+
- Clean up `getSpan()` log ([#2865](https://github.com/getsentry/sentry-dart/pull/2865))
76+
77+
### Fixes
78+
79+
- `options.diagnosticLevel` not affecting logs ([#2856](https://github.com/getsentry/sentry-dart/pull/2856))
80+
7081
## 9.0.0-alpha.2
7182

7283
### Features

dart/lib/src/hub.dart

+1-6
Original file line numberDiff line numberDiff line change
@@ -507,12 +507,7 @@ class Hub {
507507
SentryLevel.warning,
508508
"Instance is disabled and this 'getSpan' call is a no-op.",
509509
);
510-
} else if (!_options.isTracingEnabled()) {
511-
_options.logger(
512-
SentryLevel.info,
513-
"Tracing is disabled and this 'getSpan' returns null.",
514-
);
515-
} else {
510+
} else if (_options.isTracingEnabled()) {
516511
final item = _peek();
517512

518513
span = item.scope.span;

dart/lib/src/sentry.dart

+1
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,7 @@ class Sentry {
362362
);
363363

364364
/// Gets the current active transaction or span bound to the scope.
365+
/// Returns `null` if performance is disabled in the options.
365366
static ISentrySpan? getSpan() => _hub.getSpan();
366367

367368
static Future<void> addFeatureFlag(String name, dynamic value) async {

dart/test/sentry_test.dart

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// ignore_for_file: deprecated_member_use_from_same_package
2+
13
import 'dart:async';
24
import 'dart:isolate';
35

flutter/lib/src/binding_wrapper.dart

+48-25
Original file line numberDiff line numberDiff line change
@@ -73,37 +73,50 @@ typedef FrameTimingCallback = void Function(
7373
DateTime startTimestamp, DateTime endTimestamp);
7474

7575
mixin SentryWidgetsBindingMixin on WidgetsBinding {
76-
DateTime? _startTimestamp;
77-
FrameTimingCallback? _frameTimingCallback;
78-
ClockProvider? _clock;
79-
80-
SentryOptions get _options => Sentry.currentHub.options;
76+
FrameTimingCallback? _onDelayedFrame;
77+
FrameTimingCallback? get onDelayedFrame => _onDelayedFrame;
78+
Duration? _expectedFrameDuration;
79+
Duration? get expectedFrameDuration => _expectedFrameDuration;
80+
bool _isTrackingActive = false;
81+
SentryOptions? _options;
82+
SentryOptions? get options => _options;
83+
final Stopwatch _stopwatch = Stopwatch();
8184

8285
@internal
83-
void registerFramesTracking(
84-
FrameTimingCallback callback, ClockProvider clock) {
85-
_frameTimingCallback ??= callback;
86-
_clock ??= clock;
86+
void initializeFramesTracking(FrameTimingCallback onDelayedFrame,
87+
SentryOptions options, Duration expectedFrameDuration) {
88+
_onDelayedFrame ??= onDelayedFrame;
89+
_options ??= options;
90+
_expectedFrameDuration ??= expectedFrameDuration;
91+
}
92+
93+
void resumeTrackingFrames() {
94+
_isTrackingActive = true;
8795
}
8896

89-
@visibleForTesting
90-
bool isFramesTrackingInitialized() {
91-
return _frameTimingCallback != null && _clock != null;
97+
void pauseTrackingFrames() {
98+
// Stopwatch could continue running if we pause tracking in between a frame
99+
_stopwatch.stop();
100+
_stopwatch.reset();
101+
_isTrackingActive = false;
92102
}
93103

94104
@internal
95105
void removeFramesTracking() {
96-
_frameTimingCallback = null;
97-
_clock = null;
106+
_onDelayedFrame = null;
107+
_expectedFrameDuration = null;
108+
_options = null;
98109
}
99110

100111
@override
101112
void handleBeginFrame(Duration? rawTimeStamp) {
102-
try {
103-
_startTimestamp = _clock?.call();
104-
} catch (_) {
105-
if (_options.automatedTestMode) {
106-
rethrow;
113+
if (_isTrackingActive) {
114+
try {
115+
_stopwatch.start();
116+
} catch (_) {
117+
if (_options?.automatedTestMode == true) {
118+
rethrow;
119+
}
107120
}
108121
}
109122

@@ -114,15 +127,25 @@ mixin SentryWidgetsBindingMixin on WidgetsBinding {
114127
void handleDrawFrame() {
115128
super.handleDrawFrame();
116129

130+
if (!_isTrackingActive) {
131+
return;
132+
}
133+
final expectedFrameDuration = _expectedFrameDuration;
134+
final options = _options;
117135
try {
118-
final endTimestamp = _clock?.call();
119-
if (_startTimestamp != null &&
120-
endTimestamp != null &&
121-
_startTimestamp!.isBefore(endTimestamp)) {
122-
_frameTimingCallback?.call(_startTimestamp!, endTimestamp);
136+
_stopwatch.stop();
137+
if (options != null &&
138+
expectedFrameDuration != null &&
139+
_stopwatch.elapsedMilliseconds >
140+
expectedFrameDuration.inMilliseconds) {
141+
final endTimestamp = options.clock();
142+
final startTimestamp = endTimestamp
143+
.subtract(Duration(milliseconds: _stopwatch.elapsedMilliseconds));
144+
_onDelayedFrame?.call(startTimestamp, endTimestamp);
123145
}
146+
_stopwatch.reset();
124147
} catch (_) {
125-
if (_options.automatedTestMode) {
148+
if (_options?.automatedTestMode == true) {
126149
rethrow;
127150
}
128151
}

flutter/lib/src/frames_tracking/sentry_delayed_frames_tracker.dart

+16-31
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import 'dart:math';
44

55
import 'package:meta/meta.dart';
6+
67
import '../../sentry_flutter.dart';
78

89
/// This is just an upper limit, ensuring that the buffer does not grow
@@ -35,22 +36,13 @@ class SentryDelayedFramesTracker {
3536
/// Since startFrame and endFrame is always called sequentially by Flutter we
3637
/// don't need a SplayTree
3738
final List<SentryFrameTiming> _delayedFrames = [];
39+
@visibleForTesting
40+
List<SentryFrameTiming> get delayedFrames => _delayedFrames.toList();
3841
final SentryFlutterOptions _options;
3942
final Duration _expectedFrameDuration;
43+
DateTime? _oldestFrameEndTimestamp;
4044
@visibleForTesting
4145
DateTime? get oldestFrameEndTimestamp => _oldestFrameEndTimestamp;
42-
DateTime? _oldestFrameEndTimestamp;
43-
bool _isTrackingActive = false;
44-
45-
/// Resumes the collecting of frames.
46-
void resume() {
47-
_isTrackingActive = true;
48-
}
49-
50-
/// Pauses the collecting of frames.
51-
void pause() {
52-
_isTrackingActive = false;
53-
}
5446

5547
/// Retrieves the frames the intersect with the provided [startTimestamp] and [endTimestamp].
5648
@visibleForTesting
@@ -80,27 +72,27 @@ class SentryDelayedFramesTracker {
8072
}).toList(growable: false);
8173
}
8274

75+
/// Records the start and end time of a delayed frame.
76+
///
77+
/// [startTimestamp] The time when the delayed frame rendering started.
78+
/// [endTimestamp] The time when the delayed frame rendering ended.
8379
@pragma('vm:prefer-inline')
84-
void addFrame(DateTime startTimestamp, DateTime endTimestamp) {
85-
if (!_isTrackingActive || !_options.enableFramesTracking) {
80+
void addDelayedFrame(DateTime startTimestamp, DateTime endTimestamp) {
81+
if (!_options.enableFramesTracking) {
8682
return;
8783
}
8884
if (startTimestamp.isAfter(endTimestamp)) {
8985
return;
9086
}
9187
if (_delayedFrames.length > maxDelayedFramesBuffer) {
92-
// buffer is full, we stop collecting frames until all active spans have
93-
// finished processing
94-
pause();
88+
_options.logger(SentryLevel.debug,
89+
'Frame tracking buffer is full, stopping frame collection until all active spans have finished processing');
9590
return;
9691
}
97-
final duration = endTimestamp.difference(startTimestamp);
98-
if (duration > _expectedFrameDuration) {
99-
final frameTiming = SentryFrameTiming(
100-
startTimestamp: startTimestamp, endTimestamp: endTimestamp);
101-
_delayedFrames.add(frameTiming);
102-
_oldestFrameEndTimestamp ??= endTimestamp;
103-
}
92+
final frameTiming = SentryFrameTiming(
93+
startTimestamp: startTimestamp, endTimestamp: endTimestamp);
94+
_delayedFrames.add(frameTiming);
95+
_oldestFrameEndTimestamp ??= endTimestamp;
10496
}
10597

10698
void removeIrrelevantFrames(DateTime spanStartTimestamp) {
@@ -227,15 +219,8 @@ class SentryDelayedFramesTracker {
227219
/// Clears the state of the tracker.
228220
void clear() {
229221
_delayedFrames.clear();
230-
pause();
231222
_oldestFrameEndTimestamp = null;
232223
}
233-
234-
@visibleForTesting
235-
List<SentryFrameTiming> get delayedFrames => _delayedFrames.toList();
236-
237-
@visibleForTesting
238-
bool get isTrackingActive => _isTrackingActive;
239224
}
240225

241226
/// Frame timing that represents an approximation of the frame's build duration.

flutter/lib/src/frames_tracking/span_frame_metrics_collector.dart

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,26 @@
11
// ignore_for_file: invalid_use_of_internal_member
22

33
import 'package:meta/meta.dart';
4+
45
import '../../sentry_flutter.dart';
56
import 'sentry_delayed_frames_tracker.dart';
67

78
/// Collects frames from [SentryDelayedFramesTracker], calculates the metrics
89
/// and attaches them to spans.
910
@internal
1011
class SpanFrameMetricsCollector implements PerformanceContinuousCollector {
11-
SpanFrameMetricsCollector(this._options, this._frameTracker);
12+
SpanFrameMetricsCollector(
13+
this._options,
14+
this._frameTracker, {
15+
required void Function() resumeFrameTracking,
16+
required void Function() pauseFrameTracking,
17+
}) : _resumeFrameTracking = resumeFrameTracking,
18+
_pauseFrameTracking = pauseFrameTracking;
1219

1320
final SentryFlutterOptions _options;
1421
final SentryDelayedFramesTracker _frameTracker;
22+
final void Function() _resumeFrameTracking;
23+
final void Function() _pauseFrameTracking;
1524

1625
/// Stores the spans that are actively being tracked.
1726
/// After the frames are calculated and stored in the span the span is removed from this list.
@@ -26,7 +35,7 @@ class SpanFrameMetricsCollector implements PerformanceContinuousCollector {
2635
}
2736

2837
activeSpans.add(span);
29-
_frameTracker.resume();
38+
_resumeFrameTracking();
3039
});
3140
}
3241

@@ -69,6 +78,7 @@ class SpanFrameMetricsCollector implements PerformanceContinuousCollector {
6978

7079
@override
7180
void clear() {
81+
_pauseFrameTracking();
7282
_frameTracker.clear();
7383
activeSpans.clear();
7484
// we don't need to clear the expected frame duration as that realistically

flutter/lib/src/integrations/frames_tracking_integration.dart

+9-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
// ignore_for_file: invalid_use_of_internal_member
22

3+
import 'dart:core';
4+
35
import '../../sentry_flutter.dart';
46
import '../binding_wrapper.dart';
57
import '../frames_tracking/sentry_delayed_frames_tracker.dart';
@@ -9,6 +11,7 @@ import '../native/sentry_native_binding.dart';
911
class FramesTrackingIntegration implements Integration<SentryFlutterOptions> {
1012
FramesTrackingIntegration(this._native);
1113

14+
static const integrationName = 'FramesTracking';
1215
final SentryNativeBinding _native;
1316
SentryFlutterOptions? _options;
1417
PerformanceCollector? _collector;
@@ -45,13 +48,15 @@ class FramesTrackingIntegration implements Integration<SentryFlutterOptions> {
4548
// Everything valid, we can initialize now
4649
final framesTracker =
4750
SentryDelayedFramesTracker(options, expectedFrameDuration);
48-
widgetsBinding.registerFramesTracking(
49-
framesTracker.addFrame, options.clock);
50-
final collector = SpanFrameMetricsCollector(options, framesTracker);
51+
widgetsBinding.initializeFramesTracking(
52+
framesTracker.addDelayedFrame, options, expectedFrameDuration);
53+
final collector = SpanFrameMetricsCollector(options, framesTracker,
54+
resumeFrameTracking: () => widgetsBinding.resumeTrackingFrames(),
55+
pauseFrameTracking: () => widgetsBinding.pauseTrackingFrames());
5156
options.addPerformanceCollector(collector);
5257
_collector = collector;
5358

54-
options.sdk.addIntegration('framesTrackingIntegration');
59+
options.sdk.addIntegration(integrationName);
5560
options.logger(SentryLevel.debug,
5661
'$FramesTrackingIntegration successfully initialized with an expected frame duration of ${expectedFrameDuration.inMilliseconds}ms');
5762
}

flutter/test/frame_tracking/frames_tracking_integration_test.dart

+11-4
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,15 @@ void main() {
5353
await integration.call(Hub(options), options);
5454
}
5555

56+
bool isFramesTrackingInitialized(SentryWidgetsBindingMixin binding) {
57+
return binding.options != null &&
58+
binding.onDelayedFrame != null &&
59+
binding.expectedFrameDuration != null;
60+
}
61+
5662
void assertInitFailure() {
5763
if (widgetsBinding != null) {
58-
expect(widgetsBinding!.isFramesTrackingInitialized(), isFalse);
64+
expect(isFramesTrackingInitialized(widgetsBinding!), isFalse);
5965
}
6066
expect(options.performanceCollectors, isEmpty);
6167
}
@@ -74,18 +80,19 @@ void main() {
7480
test('adds integration to SDK list', () async {
7581
await fromWorkingState(options);
7682

77-
expect(options.sdk.integrations, contains('framesTrackingIntegration'));
83+
expect(options.sdk.integrations,
84+
contains(FramesTrackingIntegration.integrationName));
7885
});
7986

8087
test('properly cleans up resources on close', () async {
8188
await fromWorkingState(options);
8289

83-
expect(widgetsBinding!.isFramesTrackingInitialized(), isTrue);
90+
expect(isFramesTrackingInitialized(widgetsBinding!), isTrue);
8491
expect(options.performanceCollectors, isNotEmpty);
8592

8693
integration.close();
8794

88-
expect(widgetsBinding!.isFramesTrackingInitialized(), isFalse);
95+
expect(isFramesTrackingInitialized(widgetsBinding!), isFalse);
8996
expect(options.performanceCollectors, isEmpty);
9097
});
9198

0 commit comments

Comments
 (0)