Skip to content

Commit

Permalink
Merge 9fe74c3 into 216bdf9
Browse files Browse the repository at this point in the history
  • Loading branch information
philipphofmann committed Dec 18, 2023
2 parents 216bdf9 + 9fe74c3 commit dcef2fe
Show file tree
Hide file tree
Showing 15 changed files with 183 additions and 16 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Expand Up @@ -6,6 +6,11 @@

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

### Fixes

- TTFD waits for next drawn frame (#3505) only when enabling `options.performanceV2 = true`.

## 8.17.2

### Fixes
Expand Down
1 change: 1 addition & 0 deletions Samples/iOS-Swift/iOS-Swift/AppDelegate.swift
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
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
11 changes: 11 additions & 0 deletions Sources/Sentry/Public/SentryOptions.h
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
4 changes: 4 additions & 0 deletions Sources/Sentry/SentryOptions.m
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
2 changes: 2 additions & 0 deletions Sources/Sentry/SentryPerformanceTrackingIntegration.m
Expand Up @@ -47,6 +47,8 @@ - (BOOL)installWithOptions:(SentryOptions *)options
[self.swizzling start];
SentryUIViewControllerPerformanceTracker.shared.enableWaitForFullDisplay
= options.enableTimeToFullDisplayTracing;
SentryUIViewControllerPerformanceTracker.shared.enablePerformanceV2
= options.enablePerformanceV2;

return YES;
}
Expand Down
18 changes: 15 additions & 3 deletions Sources/Sentry/SentryTimeToDisplayTracker.m
Expand Up @@ -26,17 +26,20 @@

@implementation SentryTimeToDisplayTracker {
BOOL _waitForFullDisplay;
BOOL _fullDisplayWaitsForNextFrame;
BOOL _isReadyToDisplay;
BOOL _fullyDisplayedReported;
NSString *_controllerName;
}

- (instancetype)initForController:(UIViewController *)controller
waitForFullDisplay:(BOOL)waitForFullDisplay
fullDisplayWaitsForNextFrame:(BOOL)fullDisplayWaitsForNextFrame
{
if (self = [super init]) {
_controllerName = [SwiftDescriptor getObjectClassName:controller];
_waitForFullDisplay = waitForFullDisplay;
_fullDisplayWaitsForNextFrame = fullDisplayWaitsForNextFrame;

_isReadyToDisplay = NO;
_fullyDisplayedReported = NO;
Expand Down Expand Up @@ -78,7 +81,8 @@ - (void)reportReadyToDisplay
- (void)reportFullyDisplayed
{
_fullyDisplayedReported = YES;
if (self.waitForFullDisplay && _isReadyToDisplay) {

if (self.waitForFullDisplay && _isReadyToDisplay && !_fullDisplayWaitsForNextFrame) {
// 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.
Expand Down Expand Up @@ -108,15 +112,23 @@ - (void)framesTrackerHasNewFrame
[self addTimeToDisplayMeasurement:self.initialDisplaySpan name:@"time_to_initial_display"];

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

if (!_fullDisplayWaitsForNextFrame) {
[SentryDependencyContainer.sharedInstance.framesTracker removeListener:self];
}
}
if (_waitForFullDisplay && _fullyDisplayedReported && self.fullDisplaySpan.isFinished == NO) {
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];
}

if (_fullDisplayWaitsForNextFrame && self.initialDisplaySpan.isFinished == YES
&& self.fullDisplaySpan.isFinished == YES) {
[SentryDependencyContainer.sharedInstance.framesTracker removeListener:self];
}
}

- (void)trimTTFDIdNecessaryForTracer:(SentryTracer *)tracer
Expand Down
4 changes: 3 additions & 1 deletion Sources/Sentry/SentryUIViewControllerPerformanceTracker.m
Expand Up @@ -45,6 +45,7 @@ - (instancetype)init
inAppExcludes:options.inAppExcludes];

_enableWaitForFullDisplay = NO;
_enablePerformanceV2 = NO;
}
return self;
}
Expand Down Expand Up @@ -141,7 +142,8 @@ - (void)createTimeToDisplay:(UIViewController *)controller

SentryTimeToDisplayTracker *ttdTracker =
[[SentryTimeToDisplayTracker alloc] initForController:controller
waitForFullDisplay:self.enableWaitForFullDisplay];
waitForFullDisplay:self.enableWaitForFullDisplay
fullDisplayWaitsForNextFrame:self.enablePerformanceV2];

objc_setAssociatedObject(controller, &SENTRY_UI_PERFORMANCE_TRACKER_TTD_TRACKER, ttdTracker,
OBJC_ASSOCIATION_ASSIGN);
Expand Down
3 changes: 2 additions & 1 deletion Sources/Sentry/include/SentryTimeToDisplayTracker.h
Expand Up @@ -25,7 +25,8 @@ SENTRY_NO_INIT
@property (nonatomic, readonly) BOOL waitForFullDisplay;

- (instancetype)initForController:(UIViewController *)controller
waitForFullDisplay:(BOOL)waitForFullDisplay;
waitForFullDisplay:(BOOL)waitForFullDisplay
fullDisplayWaitsForNextFrame:(BOOL)fullDisplayWaitsForNextFrame;

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

Expand Down
Expand Up @@ -31,6 +31,7 @@ static NSString *const SENTRY_UI_PERFORMANCE_TRACKER_TTD_TRACKER
@property (nonatomic, strong) SentryInAppLogic *inAppLogic;

@property (nonatomic) BOOL enableWaitForFullDisplay;
@property (nonatomic) BOOL enablePerformanceV2;

/**
* Measures @c controller's @c loadView method.
Expand Down
@@ -1,3 +1,4 @@
import Nimble
import SentryTestUtils
import XCTest

Expand Down Expand Up @@ -88,5 +89,26 @@ class SentryPerformanceTrackingIntegrationTests: XCTestCase {
XCTAssertFalse(SentryUIViewControllerPerformanceTracker.shared.enableWaitForFullDisplay)
}

func testConfigure_PerformanceV2_Default() {
let sut = SentryPerformanceTrackingIntegration()

let options = Options()
options.tracesSampleRate = 0.1
sut.install(with: options)

expect(SentryUIViewControllerPerformanceTracker.shared.enablePerformanceV2) == false
}

func testConfigure_PerformanceV2_Enabled() {
let sut = SentryPerformanceTrackingIntegration()

let options = Options()
options.tracesSampleRate = 0.1
options.enablePerformanceV2 = true
sut.install(with: options)

expect(SentryUIViewControllerPerformanceTracker.shared.enablePerformanceV2) == true
}

#endif
}
@@ -1,4 +1,5 @@
import Foundation
import Nimble
import Sentry
import SentryTestUtils
import XCTest
Expand All @@ -20,8 +21,8 @@ class SentryTimeToDisplayTrackerTest: XCTestCase {
framesTracker.start()
}

func getSut(for controller: UIViewController, waitForFullDisplay: Bool) -> SentryTimeToDisplayTracker {
return SentryTimeToDisplayTracker(for: controller, waitForFullDisplay: waitForFullDisplay)
func getSut(for controller: UIViewController, waitForFullDisplay: Bool, fullDisplayWaitsForNextFrame: Bool = false) -> SentryTimeToDisplayTracker {
return SentryTimeToDisplayTracker(for: controller, waitForFullDisplay: waitForFullDisplay, fullDisplayWaitsForNextFrame: fullDisplayWaitsForNextFrame)
}
}

Expand Down Expand Up @@ -92,7 +93,7 @@ class SentryTimeToDisplayTrackerTest: XCTestCase {
XCTAssertTrue(ttidSpan.isFinished)
}

func testreportInitialDisplay_waitForFullDisplay() {
func testReportInitialDisplay_waitForFullDisplay() {
fixture.dateProvider.setDate(date: Date(timeIntervalSince1970: 7))

let sut = fixture.getSut(for: UIViewController(), waitForFullDisplay: true)
Expand Down Expand Up @@ -122,7 +123,7 @@ class SentryTimeToDisplayTrackerTest: XCTestCase {
XCTAssertEqual(tracer.children.count, 2)
}

func testreportFullDisplay_notWaitingForFullDisplay() {
func testReportFullDisplay_notWaitingForFullDisplay() {
let sut = fixture.getSut(for: UIViewController(), waitForFullDisplay: false)
let tracer = fixture.tracer

Expand All @@ -137,7 +138,7 @@ class SentryTimeToDisplayTrackerTest: XCTestCase {
XCTAssertEqual(tracer.children.count, 1)
}

func testreportFullDisplay_waitingForFullDisplay() {
func testReportFullDisplay_waitingForFullDisplay() {
fixture.dateProvider.setDate(date: Date(timeIntervalSince1970: 9))

let sut = fixture.getSut(for: UIViewController(), waitForFullDisplay: true)
Expand All @@ -164,6 +165,39 @@ class SentryTimeToDisplayTrackerTest: XCTestCase {
XCTAssertEqual(sut.fullDisplaySpan?.operation, SentrySpanOperationUILoadFullDisplay)
XCTAssertEqual(sut.fullDisplaySpan?.origin, "manual.ui.time_to_display")
}

func testReportFullDisplay_waitingForFullDisplayAndNextFrame() {
fixture.dateProvider.setDate(date: Date(timeIntervalSince1970: 9))

let sut = fixture.getSut(for: UIViewController(), waitForFullDisplay: true, fullDisplayWaitsForNextFrame: true)
let tracer = fixture.tracer

sut.start(for: tracer)

fixture.dateProvider.setDate(date: Date(timeIntervalSince1970: 10))
sut.reportReadyToDisplay()
fixture.displayLinkWrapper.normalFrame()

fixture.dateProvider.setDate(date: Date(timeIntervalSince1970: 11))
sut.reportFullyDisplayed()

fixture.dateProvider.setDate(date: Date(timeIntervalSince1970: 12))
fixture.displayLinkWrapper.normalFrame()

fixture.dateProvider.setDate(date: Date(timeIntervalSince1970: 13))
tracer.finish()

expect(sut.fullDisplaySpan) != nil
expect(sut.fullDisplaySpan?.startTimestamp) == Date(timeIntervalSince1970: 9)
expect(sut.fullDisplaySpan?.timestamp) == Date(timeIntervalSince1970: 12)
expect(sut.fullDisplaySpan?.status) == .ok

expect(sut.fullDisplaySpan?.spanDescription) == "UIViewController full display"
expect(sut.fullDisplaySpan?.operation) == SentrySpanOperationUILoadFullDisplay
expect(sut.fullDisplaySpan?.origin) == "manual.ui.time_to_display"

assertMeasurement(tracer: tracer, name: "time_to_full_display", duration: 3_000)
}

func testReportFullDisplay_waitingForFullDisplay_notReadyToDisplay() {
let sut = fixture.getSut(for: UIViewController(), waitForFullDisplay: true)
Expand All @@ -176,7 +210,36 @@ class SentryTimeToDisplayTrackerTest: XCTestCase {
fixture.dateProvider.setDate(date: Date(timeIntervalSince1970: 11))
sut.reportFullyDisplayed()

XCTAssertFalse(sut.fullDisplaySpan?.isFinished ?? true)
expect(sut.fullDisplaySpan?.isFinished) == false
}

func testReportFullDisplay_waitingForFullDisplayAndNextFrame_notReadyToDisplay() {
let sut = fixture.getSut(for: UIViewController(), waitForFullDisplay: true, fullDisplayWaitsForNextFrame: true)
let tracer = fixture.tracer

sut.start(for: tracer)

fixture.displayLinkWrapper.normalFrame()

fixture.dateProvider.setDate(date: Date(timeIntervalSince1970: 11))
sut.reportFullyDisplayed()

expect(sut.fullDisplaySpan?.isFinished) == false
sut.reportReadyToDisplay()
expect(sut.fullDisplaySpan?.isFinished) == false

fixture.dateProvider.setDate(date: Date(timeIntervalSince1970: 12))
fixture.displayLinkWrapper.normalFrame()

expect(sut.initialDisplaySpan?.isFinished) == true
expect(sut.initialDisplaySpan?.timestamp) == Date(timeIntervalSince1970: 12)
expect(sut.initialDisplaySpan?.status) == .ok

expect(sut.fullDisplaySpan?.isFinished) == true
expect(sut.fullDisplaySpan?.timestamp) == Date(timeIntervalSince1970: 12)
expect(sut.fullDisplaySpan?.status) == .ok

expect(Dynamic(self.fixture.framesTracker).listeners.count) == 0
}

func testReportFullDisplay_expires() {
Expand Down

0 comments on commit dcef2fe

Please sign in to comment.