Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
buenaflor committed Oct 8, 2024
1 parent cac54be commit 2b6a8ab
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 39 deletions.
44 changes: 19 additions & 25 deletions flutter/lib/src/span_frame_metrics_collector.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ class SpanFrameMetricsCollector implements PerformanceContinuousCollector {

final bool _isTestMode;

/// Stores frame timestamps and their durations in milliseconds.
/// Stores timestamps and durations (in milliseconds) of frames exceeding the expected duration.
/// Keys are frame timestamps, values are frame durations.
/// The timestamps mark the end of the frame.
@visibleForTesting
final frames = SplayTreeMap<DateTime, int>();
final exceededFrames = SplayTreeMap<DateTime, int>();

/// Stores the spans that are actively being tracked.
/// After the frames are calculated and stored in the span the span is removed from this list.
Expand Down Expand Up @@ -103,7 +103,7 @@ class SpanFrameMetricsCollector implements PerformanceContinuousCollector {
if (activeSpans.isEmpty) {
clear();
} else {
frames.removeWhere((frameTimestamp, _) =>
exceededFrames.removeWhere((frameTimestamp, _) =>
frameTimestamp.isBefore(activeSpans.first.startTimestamp));
}
}
Expand All @@ -122,21 +122,27 @@ class SpanFrameMetricsCollector implements PerformanceContinuousCollector {
}
}

/// Records the duration of a single frame and stores it in [frames].
/// Records the duration of a single frame and stores it in [exceededFrames].
///
/// This method is called for each frame when frame tracking is active.
Future<void> measureFrameDuration(Duration duration) async {
if (frames.length >= maxFramesToTrack) {
if (exceededFrames.length >= maxFramesToTrack) {
options.logger(SentryLevel.warning,
'Frame tracking limit reached. Clearing frames and cancelling frame tracking for all active spans');
clear();
return;
}

if (expectedFrameDuration == null) {
options.logger(SentryLevel.info,
'Expected frame duration is null. Dropping frame duration.');
return;
}

// Using the stopwatch to measure the frame duration is flaky in ci
if (_isTestMode) {
// ignore: invalid_use_of_internal_member
frames[options.clock().add(duration)] = duration.inMilliseconds;
exceededFrames[options.clock().add(duration)] = duration.inMilliseconds;
return;
}

Expand All @@ -149,9 +155,9 @@ class SpanFrameMetricsCollector implements PerformanceContinuousCollector {
await _frameCallbackHandler.endOfFrame;

final frameDuration = _stopwatch.elapsedMilliseconds;
if (_isFrozenFrame(frameDuration) || _isSlowFrame(frameDuration)) {
if (frameDuration > expectedFrameDuration!.inMilliseconds) {
// ignore: invalid_use_of_internal_member
frames[options.clock()] = frameDuration;
exceededFrames[options.clock()] = frameDuration;
}

_stopwatch.reset();
Expand Down Expand Up @@ -184,7 +190,7 @@ class SpanFrameMetricsCollector implements PerformanceContinuousCollector {
@visibleForTesting
Map<String, int> calculateFrameMetrics(
ISentrySpan span, DateTime spanEndTimestamp, int displayRefreshRate) {
if (frames.isEmpty) {
if (exceededFrames.isEmpty) {
options.logger(
SentryLevel.info, 'No frame durations available in frame tracker.');
return {};
Expand All @@ -202,7 +208,7 @@ class SpanFrameMetricsCollector implements PerformanceContinuousCollector {
int frozenFramesDuration = 0;
int framesDelay = 0;

for (final entry in frames.entries) {
for (final entry in exceededFrames.entries) {
final frameDuration = entry.value;
final frameEndTimestamp = entry.key;
final frameStartMs =
Expand Down Expand Up @@ -241,10 +247,10 @@ class SpanFrameMetricsCollector implements PerformanceContinuousCollector {
break;
}

if (_isFrozenFrame(effectiveDuration)) {
if (effectiveDuration >= _frozenFrameThreshold.inMilliseconds) {
frozenFramesCount++;
frozenFramesDuration += effectiveDuration;
} else if (_isSlowFrame(effectiveDuration)) {
} else if (effectiveDuration > expectedFrameDuration!.inMilliseconds) {
slowFramesCount++;
slowFramesDuration += effectiveDuration;
}
Expand Down Expand Up @@ -284,23 +290,11 @@ class SpanFrameMetricsCollector implements PerformanceContinuousCollector {
};
}

bool _isFrozenFrame(int frameDuration) {
return frameDuration > _frozenFrameThreshold.inMilliseconds;
}

bool _isSlowFrame(int frameDuration) {
if (expectedFrameDuration != null) {
return frameDuration > expectedFrameDuration!.inMilliseconds;
} else {
return false;
}
}

@override
void clear() {
_isTrackingPaused = true;
_stopwatch.reset();
frames.clear();
exceededFrames.clear();
activeSpans.clear();
displayRefreshRate = null;
}
Expand Down
29 changes: 15 additions & 14 deletions flutter/test/span_frame_metrics_collector_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ void main() {

test('clear() clears frames, running spans and pauses frame tracking', () {
final sut = fixture.sut;
sut.frames[DateTime.now()] = 1;
sut.exceededFrames[DateTime.now()] = 1;
final mockSpan = MockSentrySpan();
when(mockSpan.startTimestamp).thenReturn(DateTime.now());

sut.onSpanStarted(mockSpan);
sut.clear();

expect(sut.frames, isEmpty);
expect(sut.exceededFrames, isEmpty);
expect(sut.activeSpans, isEmpty);
expect(sut.isTrackingPaused, isTrue);
});
Expand Down Expand Up @@ -97,14 +97,15 @@ void main() {
sut.activeSpans.add(span1);
sut.activeSpans.add(span2);

sut.frames[spanStartTimestamp.subtract(Duration(seconds: 5))] = 1;
sut.frames[spanStartTimestamp.subtract(Duration(seconds: 3))] = 1;
sut.frames[spanStartTimestamp.add(Duration(seconds: 4))] = 1;
sut.exceededFrames[spanStartTimestamp.subtract(Duration(seconds: 5))] = 1;
sut.exceededFrames[spanStartTimestamp.subtract(Duration(seconds: 3))] = 1;
sut.exceededFrames[spanStartTimestamp.add(Duration(seconds: 4))] = 1;

await sut.onSpanFinished(span1, spanEndTimestamp);

expect(sut.frames, hasLength(1));
expect(sut.frames.keys.first, spanStartTimestamp.add(Duration(seconds: 4)));
expect(sut.exceededFrames, hasLength(1));
expect(sut.exceededFrames.keys.first,
spanStartTimestamp.add(Duration(seconds: 4)));
});

test(
Expand Down Expand Up @@ -141,7 +142,7 @@ void main() {
final now = DateTime.now();
when(span.startTimestamp).thenReturn(now);
when(span.endTimestamp).thenReturn(now.add(Duration(milliseconds: 500)));
sut.frames[now.add(Duration(milliseconds: 200))] = 100;
sut.exceededFrames[now.add(Duration(milliseconds: 200))] = 100;

final metrics = sut.calculateFrameMetrics(span, span.endTimestamp!, 60);

Expand All @@ -159,7 +160,7 @@ void main() {
final now = DateTime.now();
when(span.startTimestamp).thenReturn(now);
when(span.endTimestamp).thenReturn(now.add(Duration(milliseconds: 500)));
sut.frames[now.subtract(Duration(milliseconds: 200))] = 100;
sut.exceededFrames[now.subtract(Duration(milliseconds: 200))] = 100;

final metrics = sut.calculateFrameMetrics(span, span.endTimestamp!, 60);

Expand All @@ -179,7 +180,7 @@ void main() {
when(span.startTimestamp).thenReturn(now);
when(span.endTimestamp).thenReturn(now.add(Duration(milliseconds: 500)));
// 50ms before span starts and ends 50ms after span starts
sut.frames[now.add(Duration(milliseconds: 50))] = 100;
sut.exceededFrames[now.add(Duration(milliseconds: 50))] = 100;

final metrics = sut.calculateFrameMetrics(span, span.endTimestamp!, 60);

Expand All @@ -198,7 +199,7 @@ void main() {
final now = DateTime.now();
when(span.startTimestamp).thenReturn(now);
when(span.endTimestamp).thenReturn(now.add(Duration(milliseconds: 500)));
sut.frames[now.add(Duration(milliseconds: 550))] = 100;
sut.exceededFrames[now.add(Duration(milliseconds: 550))] = 100;

final metrics = sut.calculateFrameMetrics(span, span.endTimestamp!, 60);

Expand Down Expand Up @@ -269,20 +270,20 @@ void main() {

when(span.startTimestamp).thenReturn(DateTime.now());
sut.activeSpans.add(span);
sut.frames[DateTime.now()] = 1;
sut.exceededFrames[DateTime.now()] = 1;
const maxFramesToTrack = 1000;
sut.maxFramesToTrack = maxFramesToTrack;

for (var i = 1; i <= maxFramesToTrack; i++) {
await Future<void>.delayed(
Duration(milliseconds: 1)); // Add a small delay
if (i == maxFramesToTrack - 1) {
expect(sut.frames.length, maxFramesToTrack - 1);
expect(sut.exceededFrames.length, maxFramesToTrack - 1);
}
await sut.measureFrameDuration(Duration.zero);
}

expect(sut.frames, isEmpty);
expect(sut.exceededFrames, isEmpty);
expect(sut.activeSpans, isEmpty);
expect(sut.displayRefreshRate, isNull);
expect(sut.isTrackingPaused, isTrue);
Expand Down

0 comments on commit 2b6a8ab

Please sign in to comment.