Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: TTFD waits for next drawn frame #3505

Merged
merged 2 commits into from Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Expand Up @@ -6,6 +6,11 @@

- Add frames delay to transactions and spans (#3487, #3496)
- Add slow and frozen frames to spans (#3450, #3478)

### Fixes

- TTFD waits for next drawn frame (#3505)

## 8.17.2

### Fixes
Expand Down
Expand Up @@ -14,7 +14,10 @@ class LoremIpsumViewController: UIViewController {
if let contents = FileManager.default.contents(atPath: path) {
DispatchQueue.main.async {
self.textView.text = String(data: contents, encoding: .utf8)
SentrySDK.reportFullyDisplayed()

dispatchQueue.asyncAfter(deadline: .now() + 0.1) {
SentrySDK.reportFullyDisplayed()
}
}
}
}
Expand Down
63 changes: 29 additions & 34 deletions Sources/Sentry/SentryTimeToDisplayTracker.m
Expand Up @@ -26,7 +26,7 @@

@implementation SentryTimeToDisplayTracker {
BOOL _waitForFullDisplay;
BOOL _isReadyToDisplay;
BOOL _initialDisplayReported;
philipphofmann marked this conversation as resolved.
Show resolved Hide resolved
BOOL _fullyDisplayedReported;
NSString *_controllerName;
}
Expand All @@ -37,8 +37,7 @@ - (instancetype)initForController:(UIViewController *)controller
if (self = [super init]) {
_controllerName = [SwiftDescriptor getObjectClassName:controller];
_waitForFullDisplay = waitForFullDisplay;

_isReadyToDisplay = NO;
_initialDisplayReported = NO;
_fullyDisplayedReported = NO;
}
return self;
Expand Down Expand Up @@ -66,33 +65,26 @@ - (void)startForTracer:(SentryTracer *)tracer
self.initialDisplaySpan.startTimestamp = tracer.startTimestamp;

[SentryDependencyContainer.sharedInstance.framesTracker addListener:self];
[tracer setFinishCallback:^(
SentryTracer *_tracer) { [self trimTTFDIdNecessaryForTracer:_tracer]; }];
[tracer setFinishCallback:^(SentryTracer *_tracer) {
if (self.fullDisplaySpan.status != kSentrySpanStatusDeadlineExceeded) {
return;
}

self.fullDisplaySpan.timestamp = self.initialDisplaySpan.timestamp;
self.fullDisplaySpan.spanDescription = [NSString
stringWithFormat:@"%@ - Deadline Exceeded", self.fullDisplaySpan.spanDescription];
[self addTimeToDisplayMeasurement:self.fullDisplaySpan name:@"time_to_full_display"];
}];
}

- (void)reportReadyToDisplay
- (void)reportInitialDisplay
{
_isReadyToDisplay = YES;
_initialDisplayReported = YES;
}

- (void)reportFullyDisplayed
{
_fullyDisplayedReported = YES;
if (self.waitForFullDisplay && _isReadyToDisplay) {
// We need the timestamp to be able to calculate the duration
// but we can't finish first and add measure later because
// finishing the span may trigger the tracer finishInternal.
self.fullDisplaySpan.timestamp =
[SentryDependencyContainer.sharedInstance.dateProvider date];
[self addTimeToDisplayMeasurement:self.fullDisplaySpan name:@"time_to_full_display"];
[self.fullDisplaySpan finish];
}
}

- (void)addTimeToDisplayMeasurement:(SentrySpan *)span name:(NSString *)name
{
NSTimeInterval duration = [span.timestamp timeIntervalSinceDate:span.startTimestamp] * 1000;
[span setMeasurement:name value:@(duration) unit:SentryMeasurementUnitDuration.millisecond];
}

- (void)framesTrackerHasNewFrame
Expand All @@ -102,32 +94,35 @@ - (void)framesTrackerHasNewFrame
// The purpose of TTID and TTFD is to measure how long
// takes to the content of the screen to change.
// Thats why we need to wait for the next frame to be drawn.
if (_isReadyToDisplay && self.initialDisplaySpan.isFinished == NO) {
if (_initialDisplayReported && self.initialDisplaySpan.isFinished == NO) {
self.initialDisplaySpan.timestamp = finishTime;

[self addTimeToDisplayMeasurement:self.initialDisplaySpan name:@"time_to_initial_display"];

[self.initialDisplaySpan finish];
[SentryDependencyContainer.sharedInstance.framesTracker removeListener:self];

if (!_waitForFullDisplay) {
[SentryDependencyContainer.sharedInstance.framesTracker removeListener:self];
}
}
if (_waitForFullDisplay && _fullyDisplayedReported && self.fullDisplaySpan.isFinished == NO) {
if (_waitForFullDisplay && _fullyDisplayedReported && self.fullDisplaySpan.isFinished == NO
&& self.initialDisplaySpan.isFinished == YES) {
self.fullDisplaySpan.timestamp = finishTime;

[self addTimeToDisplayMeasurement:self.initialDisplaySpan name:@"time_to_full_display"];
[self addTimeToDisplayMeasurement:self.fullDisplaySpan name:@"time_to_full_display"];

[self.fullDisplaySpan finish];
}
}

- (void)trimTTFDIdNecessaryForTracer:(SentryTracer *)tracer
{
if (self.fullDisplaySpan.status != kSentrySpanStatusDeadlineExceeded) {
return;
if (self.initialDisplaySpan.isFinished == YES && self.fullDisplaySpan.isFinished == YES) {
[SentryDependencyContainer.sharedInstance.framesTracker removeListener:self];
}
}

self.fullDisplaySpan.timestamp = self.initialDisplaySpan.timestamp;
self.fullDisplaySpan.spanDescription =
[NSString stringWithFormat:@"%@ - Deadline Exceeded", self.fullDisplaySpan.spanDescription];
- (void)addTimeToDisplayMeasurement:(SentrySpan *)span name:(NSString *)name
{
NSTimeInterval duration = [span.timestamp timeIntervalSinceDate:span.startTimestamp] * 1000;
[span setMeasurement:name value:@(duration) unit:SentryMeasurementUnitDuration.millisecond];
}

@end
Expand Down
Expand Up @@ -182,7 +182,7 @@ - (void)viewControllerViewWillAppear:(UIViewController *)controller

SentryTimeToDisplayTracker *ttdTracker
= objc_getAssociatedObject(controller, &SENTRY_UI_PERFORMANCE_TRACKER_TTD_TRACKER);
[ttdTracker reportReadyToDisplay];
[ttdTracker reportInitialDisplay];
};

[self limitOverride:@"viewWillAppear"
Expand Down
2 changes: 1 addition & 1 deletion Sources/Sentry/include/SentryTimeToDisplayTracker.h
Expand Up @@ -29,7 +29,7 @@ SENTRY_NO_INIT

- (void)startForTracer:(SentryTracer *)tracer;

- (void)reportReadyToDisplay;
- (void)reportInitialDisplay;

- (void)reportFullyDisplayed;

Expand Down
@@ -1,3 +1,4 @@
import Nimble
import SentryTestUtils
import XCTest

Expand Down