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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Use correct rendered frames timestamp #3531

Merged
merged 5 commits into from
Jan 4, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
- TTFD waits for next drawn frame (#3505)
- Fix TTID/TTFD for app start transactions (#3512): TTID/TTFD spans and measurements for app start transaction now include the app start duration.
- Fix a race condition in SentryTracer (#3523)
- App start ends when first frame is drawn when performanceV2 is enabled (#3530)
- Use correct rendered frames timestamp for TTID/TTFD and app start (#3531)


## 8.17.2

Expand Down
1 change: 1 addition & 0 deletions Samples/iOS-Swift/iOS-Swift/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
options.attachViewHierarchy = true
options.environment = "test-app"
options.enableTimeToFullDisplayTracing = true
options.enablePerformanceV2 = true

options.add(inAppInclude: "iOS_External")

Expand Down
3 changes: 2 additions & 1 deletion SentryTestUtils/TestCurrentDateProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ public class TestCurrentDateProvider: CurrentDateProvider {
private var internalDate = defaultStartingDate
private var internalSystemTime: UInt64 = 0
public var driftTimeForEveryRead = false
public var driftTimeInterval = 0.1

public override func date() -> Date {

defer {
if driftTimeForEveryRead {
internalDate = internalDate.addingTimeInterval(0.1)
internalDate = internalDate.addingTimeInterval(driftTimeInterval)
}
}

Expand Down
11 changes: 11 additions & 0 deletions Sources/Sentry/Public/SentryOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,17 @@ NS_SWIFT_NAME(Options)
*/
@property (nonatomic, assign) BOOL enableAutoPerformanceTracing;

/**
* @warning This is an experimental feature and may still have bugs.
*
* Sentry works on reworking the whole performance offering with the code Mobile Starfish, which
* aims to provide better insights into the performance of mobile apps and highlight clear actions
* to improve app performance to developers. This feature flag enables experimental features that
* impact the v1 performance offering and would require a major version update. Sentry aims to
* include most features in the next major by default.
*/
@property (nonatomic, assign) BOOL enablePerformanceV2;

/**
* A block that configures the initial scope when starting the SDK.
* @discussion The block receives a suggested default scope. You can either
Expand Down
21 changes: 0 additions & 21 deletions Sources/Sentry/SentryAppStartMeasurement.m
Original file line number Diff line number Diff line change
Expand Up @@ -19,27 +19,6 @@ @implementation SentryAppStartMeasurement
}
# endif // SENTRY_HAS_UIKIT

- (instancetype)initWithType:(SentryAppStartType)type
appStartTimestamp:(NSDate *)appStartTimestamp
duration:(NSTimeInterval)duration
runtimeInitTimestamp:(NSDate *)runtimeInitTimestamp
didFinishLaunchingTimestamp:(NSDate *)didFinishLaunchingTimestamp
{
# if SENTRY_HAS_UIKIT
return [self initWithType:type
isPreWarmed:NO
appStartTimestamp:appStartTimestamp
duration:duration
runtimeInitTimestamp:runtimeInitTimestamp
moduleInitializationTimestamp:[NSDate dateWithTimeIntervalSince1970:0]
didFinishLaunchingTimestamp:didFinishLaunchingTimestamp];
# else
SENTRY_LOG_DEBUG(@"SentryAppStartMeasurement only works with UIKit enabled. Ensure you're "
@"using the right configuration of Sentry that links UIKit.");
return nil;
# endif // SENTRY_HAS_UIKIT
}

- (instancetype)initWithType:(SentryAppStartType)type
isPreWarmed:(BOOL)isPreWarmed
appStartTimestamp:(NSDate *)appStartTimestamp
Expand Down
44 changes: 35 additions & 9 deletions Sources/Sentry/SentryAppStartTracker.m
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
# import "SentryAppStartMeasurement.h"
# import "SentryAppStateManager.h"
# import "SentryDefines.h"
# import "SentryFramesTracker.h"
# import "SentryLog.h"
# import "SentrySysctl.h"
# import <Foundation/Foundation.h>
Expand All @@ -28,14 +29,16 @@
static const NSTimeInterval SENTRY_APP_START_MAX_DURATION = 180.0;

@interface
SentryAppStartTracker ()
SentryAppStartTracker () <SentryFramesTrackerListener>

@property (nonatomic, strong) SentryAppState *previousAppState;
@property (nonatomic, strong) SentryDispatchQueueWrapper *dispatchQueue;
@property (nonatomic, strong) SentryAppStateManager *appStateManager;
@property (nonatomic, strong) SentryFramesTracker *framesTracker;
@property (nonatomic, assign) BOOL wasInBackground;
@property (nonatomic, strong) NSDate *didFinishLaunchingTimestamp;
@property (nonatomic, assign) BOOL enablePreWarmedAppStartTracing;
@property (nonatomic, assign) BOOL enablePerformanceV2;

@end

Expand All @@ -55,11 +58,19 @@ + (void)load

- (instancetype)initWithDispatchQueueWrapper:(SentryDispatchQueueWrapper *)dispatchQueueWrapper
appStateManager:(SentryAppStateManager *)appStateManager
framesTracker:(SentryFramesTracker *)framesTracker
enablePreWarmedAppStartTracing:(BOOL)enablePreWarmedAppStartTracing
enablePerformanceV2:(BOOL)enablePerformanceV2
{
if (self = [super init]) {
self.dispatchQueue = dispatchQueueWrapper;
self.appStateManager = appStateManager;
_enablePerformanceV2 = enablePerformanceV2;
if (_enablePerformanceV2) {
self.framesTracker = framesTracker;
[framesTracker addListener:self];
}

self.previousAppState = [self.appStateManager loadPreviousAppState];
self.wasInBackground = NO;
self.didFinishLaunchingTimestamp =
Expand Down Expand Up @@ -109,7 +120,8 @@ - (void)start
object:nil];

if (PrivateSentrySDKOnly.appStartMeasurementHybridSDKMode) {
[self buildAppStartMeasurement];
[self
buildAppStartMeasurement:[SentryDependencyContainer.sharedInstance.dateProvider date]];
}

# if SENTRY_HAS_UIKIT
Expand All @@ -119,7 +131,7 @@ - (void)start
self.isRunning = YES;
}

- (void)buildAppStartMeasurement
- (void)buildAppStartMeasurement:(NSDate *)appStartEnd
{
void (^block)(void) = ^(void) {
[self stop];
Expand Down Expand Up @@ -170,12 +182,11 @@ - (void)buildAppStartMeasurement
NSDate *appStartTimestamp;
SentrySysctl *sysctl = SentryDependencyContainer.sharedInstance.sysctlWrapper;
if (isPreWarmed) {
appStartDuration = [[SentryDependencyContainer.sharedInstance.dateProvider date]
timeIntervalSinceDate:sysctl.moduleInitializationTimestamp];
appStartDuration =
[appStartEnd timeIntervalSinceDate:sysctl.moduleInitializationTimestamp];
appStartTimestamp = sysctl.moduleInitializationTimestamp;
} else {
appStartDuration = [[SentryDependencyContainer.sharedInstance.dateProvider date]
timeIntervalSinceDate:sysctl.processStartTimestamp];
appStartDuration = [appStartEnd timeIntervalSinceDate:sysctl.processStartTimestamp];
appStartTimestamp = sysctl.processStartTimestamp;
}

Expand Down Expand Up @@ -222,11 +233,24 @@ - (void)buildAppStartMeasurement
}

/**
* This is when the first frame is drawn.
* This is when the window becomes visible, which is not when the first frame of the app is drawn.
* When this is posted, the app screen is usually white. The correct time when the first frame is
* drawn is called in framesTrackerHasNewFrame only when `enablePerformanceV2` is enabled.
*/
- (void)didBecomeVisible
{
[self buildAppStartMeasurement];
if (!_enablePerformanceV2) {
[self
buildAppStartMeasurement:[SentryDependencyContainer.sharedInstance.dateProvider date]];
}
}

/**
* This is when the first frame is drawn.
*/
- (void)framesTrackerHasNewFrame:(NSDate *)newFrameDate
{
[self buildAppStartMeasurement:newFrameDate];
}

- (SentryAppStartType)getStartType
Expand Down Expand Up @@ -288,6 +312,8 @@ - (void)stop
name:UIApplicationDidEnterBackgroundNotification
object:nil];

[self.framesTracker removeListener:self];

# if TEST
self.isRunning = NO;
# endif
Expand Down
4 changes: 3 additions & 1 deletion Sources/Sentry/SentryAppStartTrackingIntegration.m
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ - (BOOL)installWithOptions:(SentryOptions *)options
self.tracker = [[SentryAppStartTracker alloc]
initWithDispatchQueueWrapper:[[SentryDispatchQueueWrapper alloc] init]
appStateManager:appStateManager
enablePreWarmedAppStartTracing:options.enablePreWarmedAppStartTracing];
framesTracker:SentryDependencyContainer.sharedInstance.framesTracker
enablePreWarmedAppStartTracing:options.enablePreWarmedAppStartTracing
enablePerformanceV2:options.enablePerformanceV2];
[self.tracker start];

return YES;
Expand Down
4 changes: 3 additions & 1 deletion Sources/Sentry/SentryFramesTracker.m
Original file line number Diff line number Diff line change
Expand Up @@ -200,8 +200,10 @@ - (void)reportNewFrame
localListeners = [self.listeners allObjects];
}

NSDate *newFrameDate = [self.dateProvider date];

for (id<SentryFramesTrackerListener> listener in localListeners) {
[listener framesTrackerHasNewFrame];
[listener framesTrackerHasNewFrame:newFrameDate];
}
}

Expand Down
4 changes: 4 additions & 0 deletions Sources/Sentry/SentryOptions.m
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ - (instancetype)init
self.maxAttachmentSize = 20 * 1024 * 1024;
self.sendDefaultPii = NO;
self.enableAutoPerformanceTracing = YES;
self.enablePerformanceV2 = NO;
self.enableCaptureFailedRequests = YES;
self.environment = kSentryDefaultEnvironment;
self.enableTimeToFullDisplayTracing = NO;
Expand Down Expand Up @@ -375,6 +376,9 @@ - (BOOL)validateOptions:(NSDictionary<NSString *, id> *)options
[self setBool:options[@"enableAutoPerformanceTracing"]
block:^(BOOL value) { self->_enableAutoPerformanceTracing = value; }];

[self setBool:options[@"enablePerformanceV2"]
block:^(BOOL value) { self->_enablePerformanceV2 = value; }];

[self setBool:options[@"enableCaptureFailedRequests"]
block:^(BOOL value) { self->_enableCaptureFailedRequests = value; }];

Expand Down
9 changes: 3 additions & 6 deletions Sources/Sentry/SentryTimeToDisplayTracker.m
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

#if SENTRY_HAS_UIKIT

# import "SentryCurrentDateProvider.h"
# import "SentryDependencyContainer.h"
# import "SentryFramesTracker.h"
# import "SentryMeasurementValue.h"
Expand Down Expand Up @@ -99,15 +98,13 @@ - (void)reportFullyDisplayed
_fullyDisplayedReported = YES;
}

- (void)framesTrackerHasNewFrame
- (void)framesTrackerHasNewFrame:(NSDate *)newFrameDate
{
NSDate *finishTime = [SentryDependencyContainer.sharedInstance.dateProvider date];

// 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 (_initialDisplayReported && self.initialDisplaySpan.isFinished == NO) {
self.initialDisplaySpan.timestamp = finishTime;
self.initialDisplaySpan.timestamp = newFrameDate;

[self.initialDisplaySpan finish];

Expand All @@ -117,7 +114,7 @@ - (void)framesTrackerHasNewFrame
}
if (_waitForFullDisplay && _fullyDisplayedReported && self.fullDisplaySpan.isFinished == NO
&& self.initialDisplaySpan.isFinished == YES) {
self.fullDisplaySpan.timestamp = finishTime;
self.fullDisplaySpan.timestamp = newFrameDate;

[self.fullDisplaySpan finish];
}
Expand Down
3 changes: 2 additions & 1 deletion Sources/Sentry/SentryTracer.m
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,6 @@ - (instancetype)initWithTransactionContext:(SentryTransactionContext *)transacti
}

#if SENTRY_HAS_UIKIT
appStartMeasurement = [self getAppStartMeasurement];
viewNames = [SentryDependencyContainer.sharedInstance.application relevantViewControllersNames];
#endif // SENTRY_HAS_UIKIT

Expand Down Expand Up @@ -497,6 +496,8 @@ - (void)finishInternal
[super finishWithStatus:_finishStatus];
}
#if SENTRY_HAS_UIKIT
appStartMeasurement = [self getAppStartMeasurement];

if (appStartMeasurement != nil) {
[self updateStartTime:appStartMeasurement.appStartTimestamp];
}
Expand Down
15 changes: 2 additions & 13 deletions Sources/Sentry/include/HybridPublic/SentryAppStartMeasurement.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,6 @@ typedef NS_ENUM(NSUInteger, SentryAppStartType) {
@interface SentryAppStartMeasurement : NSObject
SENTRY_NO_INIT

/**
* Initializes SentryAppStartMeasurement with the given parameters.
*/
- (instancetype)initWithType:(SentryAppStartType)type
appStartTimestamp:(NSDate *)appStartTimestamp
duration:(NSTimeInterval)duration
runtimeInitTimestamp:(NSDate *)runtimeInitTimestamp
didFinishLaunchingTimestamp:(NSDate *)didFinishLaunchingTimestamp
DEPRECATED_MSG_ATTRIBUTE("Use "
"initWithType:appStartTimestamp:duration:mainTimestamp:"
"runtimeInitTimestamp:didFinishLaunchingTimestamp instead.");

/**
* Initializes SentryAppStartMeasurement with the given parameters.
*/
Expand All @@ -49,7 +37,8 @@ SENTRY_NO_INIT

/**
* How long the app start took. From appStartTimestamp to when the SDK creates the
* AppStartMeasurement, which is done when the OS posts UIWindowDidBecomeVisibleNotification.
* AppStartMeasurement, which is done when the OS posts UIWindowDidBecomeVisibleNotification and
* when `enablePerformanceV2` is enabled when the app draws it's first frame.
*/
@property (readonly, nonatomic, assign) NSTimeInterval duration;

Expand Down
5 changes: 4 additions & 1 deletion Sources/Sentry/include/SentryAppStartTracker.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

@class SentryDispatchQueueWrapper;
@class SentryAppStateManager;
@class SentryFramesTracker;

NS_ASSUME_NONNULL_BEGIN

Expand All @@ -22,7 +23,9 @@ SENTRY_NO_INIT

- (instancetype)initWithDispatchQueueWrapper:(SentryDispatchQueueWrapper *)dispatchQueueWrapper
appStateManager:(SentryAppStateManager *)appStateManager
enablePreWarmedAppStartTracing:(BOOL)enablePreWarmedAppStartTracing;
framesTracker:(SentryFramesTracker *)framesTracker
enablePreWarmedAppStartTracing:(BOOL)enablePreWarmedAppStartTracing
enablePerformanceV2:(BOOL)enablePerformanceV2;

- (void)start;
- (void)stop;
Expand Down
2 changes: 1 addition & 1 deletion Sources/Sentry/include/SentryFramesTracker.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ NS_ASSUME_NONNULL_BEGIN

@protocol SentryFramesTrackerListener

- (void)framesTrackerHasNewFrame;
- (void)framesTrackerHasNewFrame:(NSDate *)newFrameDate;

@end

Expand Down
Loading
Loading