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

feat: Add extra app start span #1952

Merged
merged 16 commits into from
Jul 12, 2022
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Features

- feat: Add extra app start span (#1952)
- Add enableAutoBreadcrumbTracking option (#1958)

### Fixes
Expand Down Expand Up @@ -40,7 +41,7 @@
## 7.18.0

### Features

- Replace tracestate header with baggage (#1867)

### Fixes
Expand Down
1 change: 1 addition & 0 deletions Samples/tvOS-Swift/tvOS-Swift/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ struct ContentView: View {
Button(action: captureMessageAction) {
Text("Capture Message")
}
.accessibility(identifier: "captureMessageButton")

Button(action: captureUserFeedbackAction) {
Text("Capture User Feedback")
Expand Down
20 changes: 18 additions & 2 deletions Samples/tvOS-Swift/tvOS-SwiftUITests/LaunchUITests.swift
Original file line number Diff line number Diff line change
@@ -1,14 +1,30 @@
import XCTest

class LaunchUITests: XCTestCase {

private let app: XCUIApplication = XCUIApplication()

override func setUpWithError() throws {
try super.setUpWithError()
continueAfterFailure = false

app.launch()
}

override func tearDown() {
app.terminate()
super.tearDown()
}

func testLaunch() throws {
let app = XCUIApplication()
app.launch()
XCTAssertTrue(app.buttons["captureMessageButton"].waitForExistence(), "Home Screen doesn't exist.")
}
}

extension XCUIElement {

@discardableResult
func waitForExistence() -> Bool {
self.waitForExistence(timeout: TimeInterval(10))
}
}
20 changes: 19 additions & 1 deletion Sources/Sentry/Public/SentryAppStartMeasurement.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,20 @@ SENTRY_NO_INIT
appStartTimestamp:(NSDate *)appStartTimestamp
duration:(NSTimeInterval)duration
runtimeInitTimestamp:(NSDate *)runtimeInitTimestamp
didFinishLaunchingTimestamp:(NSDate *)didFinishLaunchingTimestamp;
didFinishLaunchingTimestamp:(NSDate *)didFinishLaunchingTimestamp
DEPRECATED_MSG_ATTRIBUTE("Use "
"initWithType:appStartTimestamp:duration:mainTimestamp:"
"runtimeInitTimestamp:didFinishLaunchingTimestamp instead.");

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

/**
* The type of the app start.
Expand All @@ -41,6 +54,11 @@ SENTRY_NO_INIT
*/
@property (readonly, nonatomic, strong) NSDate *runtimeInitTimestamp;

/**
* When application main function is called.
*/
@property (readonly, nonatomic, strong) NSDate *moduleInitializationTimestamp;

/**
* When OS posts UIApplicationDidFinishLaunchingNotification.
*/
Expand Down
17 changes: 17 additions & 0 deletions Sources/Sentry/SentryAppStartMeasurement.m
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#import "SentryAppStartMeasurement.h"
#import "NSDate+SentryExtras.h"
#import <Foundation/Foundation.h>

@implementation SentryAppStartMeasurement
Expand All @@ -8,12 +9,28 @@ - (instancetype)initWithType:(SentryAppStartType)type
duration:(NSTimeInterval)duration
runtimeInitTimestamp:(NSDate *)runtimeInitTimestamp
didFinishLaunchingTimestamp:(NSDate *)didFinishLaunchingTimestamp
{
return [self initWithType:type
appStartTimestamp:appStartTimestamp
duration:duration
runtimeInitTimestamp:runtimeInitTimestamp
moduleInitializationTimestamp:[NSDate dateWithTimeIntervalSince1970:0]
didFinishLaunchingTimestamp:didFinishLaunchingTimestamp];
}

- (instancetype)initWithType:(SentryAppStartType)type
appStartTimestamp:(NSDate *)appStartTimestamp
duration:(NSTimeInterval)duration
runtimeInitTimestamp:(NSDate *)runtimeInitTimestamp
moduleInitializationTimestamp:(NSDate *)moduleInitializationTimestamp
didFinishLaunchingTimestamp:(NSDate *)didFinishLaunchingTimestamp
{
if (self = [super init]) {
_type = type;
_appStartTimestamp = appStartTimestamp;
_duration = duration;
_runtimeInitTimestamp = runtimeInitTimestamp;
_moduleInitializationTimestamp = moduleInitializationTimestamp;
_didFinishLaunchingTimestamp = didFinishLaunchingTimestamp;
}

Expand Down
13 changes: 7 additions & 6 deletions Sources/Sentry/SentryAppStartTracker.m
Original file line number Diff line number Diff line change
Expand Up @@ -176,12 +176,13 @@ - (void)buildAppStartMeasurement
appStartDuration = 0;
}

SentryAppStartMeasurement *appStartMeasurement =
[[SentryAppStartMeasurement alloc] initWithType:appStartType
appStartTimestamp:self.sysctl.processStartTimestamp
duration:appStartDuration
runtimeInitTimestamp:runtimeInit
didFinishLaunchingTimestamp:self.didFinishLaunchingTimestamp];
SentryAppStartMeasurement *appStartMeasurement = [[SentryAppStartMeasurement alloc]
initWithType:appStartType
appStartTimestamp:self.sysctl.processStartTimestamp
duration:appStartDuration
runtimeInitTimestamp:runtimeInit
moduleInitializationTimestamp:self.sysctl.moduleInitializationTimestamp
didFinishLaunchingTimestamp:self.didFinishLaunchingTimestamp];

SentrySDK.appStartMeasurement = appStartMeasurement;
};
Expand Down
34 changes: 34 additions & 0 deletions Sources/Sentry/SentrySysctl.m
Original file line number Diff line number Diff line change
@@ -1,8 +1,37 @@
#import "SentrySysctl.h"
#import "SentryCrashSysCtl.h"
#include <stdio.h>
#include <time.h>

static NSDate *moduleInitializationTimestamp;
static NSDate *runtimeInit = nil;

void
sentryModuleInitializationHook(int argc, char **argv, char **envp)
{
moduleInitializationTimestamp = [NSDate date];
}
/**
* Module initialization functions. The C++ compiler places static constructors here. For more info
* visit:
* https://github.com/aidansteele/osx-abi-macho-file-format-reference#table-2-the-sections-of-a__datasegment
*/
__attribute__((section("__DATA,__mod_init_func"))) typeof(sentryModuleInitializationHook) *__init
= sentryModuleInitializationHook;

@implementation SentrySysctl

+ (void)load
{
// Invoked whenever this class is added to the Objective-C runtime.
runtimeInit = [NSDate date];
}

- (NSDate *)runtimeInitTimestamp
{
return runtimeInit;
}

- (NSDate *)systemBootTimestamp
{
struct timeval value = sentrycrashsysctl_timeval(CTL_KERN, KERN_BOOTTIME);
Expand All @@ -15,4 +44,9 @@ - (NSDate *)processStartTimestamp
return [NSDate dateWithTimeIntervalSince1970:startTime.tv_sec + startTime.tv_usec / 1E6];
}

- (NSDate *)moduleInitializationTimestamp
{
return moduleInitializationTimestamp;
}

@end
16 changes: 11 additions & 5 deletions Sources/Sentry/SentryTracer.m
Original file line number Diff line number Diff line change
Expand Up @@ -588,16 +588,22 @@ - (nullable SentryAppStartMeasurement *)getAppStartMeasurement
description:type];
[appStartSpan setStartTimestamp:appStartMeasurement.appStartTimestamp];

SentrySpan *premainSpan = [self buildSpan:appStartSpan.context.spanId
operation:operation
description:@"Pre Runtime Init"];
[premainSpan setStartTimestamp:appStartMeasurement.appStartTimestamp];
[premainSpan setTimestamp:appStartMeasurement.runtimeInitTimestamp];

SentrySpan *runtimeInitSpan = [self buildSpan:appStartSpan.context.spanId
operation:operation
description:@"Pre main"];
[runtimeInitSpan setStartTimestamp:appStartMeasurement.appStartTimestamp];
[runtimeInitSpan setTimestamp:appStartMeasurement.runtimeInitTimestamp];
description:@"Runtime Init to Pre Main Initializers"];
[runtimeInitSpan setStartTimestamp:appStartMeasurement.runtimeInitTimestamp];
[runtimeInitSpan setTimestamp:appStartMeasurement.moduleInitializationTimestamp];

SentrySpan *appInitSpan = [self buildSpan:appStartSpan.context.spanId
operation:operation
description:@"UIKit and Application Init"];
[appInitSpan setStartTimestamp:appStartMeasurement.runtimeInitTimestamp];
[appInitSpan setStartTimestamp:appStartMeasurement.moduleInitializationTimestamp];
[appInitSpan setTimestamp:appStartMeasurement.didFinishLaunchingTimestamp];

SentrySpan *frameRenderSpan = [self buildSpan:appStartSpan.context.spanId
Expand All @@ -608,7 +614,7 @@ - (nullable SentryAppStartMeasurement *)getAppStartMeasurement

[appStartSpan setTimestamp:appStartEndTimestamp];

return @[ appStartSpan, runtimeInitSpan, appInitSpan, frameRenderSpan ];
return @[ appStartSpan, premainSpan, runtimeInitSpan, appInitSpan, frameRenderSpan ];
}

- (void)addMeasurements:(SentryTransaction *)transaction
Expand Down
4 changes: 4 additions & 0 deletions Sources/Sentry/include/SentrySysctl.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ NS_ASSUME_NONNULL_BEGIN

@property (readonly) NSDate *processStartTimestamp;

@property (readonly) NSDate *runtimeInitTimestamp;

@property (readonly) NSDate *moduleInitializationTimestamp;

@end

NS_ASSUME_NONNULL_END
30 changes: 30 additions & 0 deletions Tests/SentryTests/Helper/SentrySysctlTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,34 @@ class SentrySysctlTests: XCTestCase {

XCTAssertGreaterThan(distance, 0)
}

func testMainTimestamp_IsInThePast() {
let distance = Date().timeIntervalSince(sut.moduleInitializationTimestamp)

XCTAssertGreaterThan(distance, 0)
}

func testMainTimestamp_IsBiggerThan_ProcessStartTime() {
let distance = sut.moduleInitializationTimestamp.timeIntervalSince(sut.processStartTimestamp)

XCTAssertGreaterThan(distance, 0)
}

func testMainTimestamp_IsBiggerThan_RuntimeInitTimestamp() {
let distance = sut.moduleInitializationTimestamp.timeIntervalSince(sut.runtimeInitTimestamp)

XCTAssertGreaterThan(distance, 0)
}

func testRuntimeInitTimestamp_IsBiggerThan_ProcessStartTimestamp() {
let distance = sut.runtimeInitTimestamp.timeIntervalSince(sut.processStartTimestamp)

XCTAssertGreaterThan(distance, 0)
}

func testRuntimeInitTimestamp_IsInThePast() {
let distance = Date().timeIntervalSince(sut.runtimeInitTimestamp)

XCTAssertGreaterThan(distance, 0)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -134,15 +134,15 @@ class SentryAppStartTrackerTests: XCTestCase {

startApp()

#if os(iOS)
#if os(iOS)
if #available(iOS 14.0, *) {
assertNoAppStartUp()
} else {
assertValidStart(type: .warm)
}
#else
#else
assertValidStart(type: .warm)
#endif
#endif
}

func testAppLaunches_WrongEnvValue_AppStartUp() {
Expand Down Expand Up @@ -317,20 +317,21 @@ class SentryAppStartTrackerTests: XCTestCase {
private func sendAppMeasurement() {
SentrySDK.setAppStartMeasurement(nil)
}

private func assertValidStart(type: SentryAppStartType, expectedDuration: TimeInterval? = nil) {
guard let appStartMeasurement = SentrySDK.getAppStartMeasurement() else {
XCTFail("AppStartMeasurement must not be nil")
return
}

XCTAssertEqual(type.rawValue, appStartMeasurement.type.rawValue)

let expectedAppStartDuration = expectedDuration ?? fixture.appStartDuration
let actualAppStartDuration = appStartMeasurement.duration
XCTAssertEqual(expectedAppStartDuration, actualAppStartDuration, accuracy: 0.000_1)

XCTAssertEqual(fixture.sysctl.processStartTimestamp, appStartMeasurement.appStartTimestamp)
XCTAssertEqual(fixture.sysctl.moduleInitializationTimestamp, appStartMeasurement.moduleInitializationTimestamp)
XCTAssertEqual(fixture.runtimeInitTimestamp, appStartMeasurement.runtimeInitTimestamp)
XCTAssertEqual(fixture.didFinishLaunchingTimestamp, appStartMeasurement.didFinishLaunchingTimestamp)
}
Expand All @@ -342,15 +343,15 @@ class SentryAppStartTrackerTests: XCTestCase {
}

XCTAssertEqual(type.rawValue, appStartMeasurement.type.rawValue)

let actualAppStartDuration = appStartMeasurement.duration
XCTAssertEqual(0.0, actualAppStartDuration, accuracy: 0.000_1)

XCTAssertEqual(fixture.sysctl.processStartTimestamp, appStartMeasurement.appStartTimestamp)
XCTAssertEqual(fixture.runtimeInitTimestamp, appStartMeasurement.runtimeInitTimestamp)
XCTAssertEqual(Date(timeIntervalSinceReferenceDate: 0), appStartMeasurement.didFinishLaunchingTimestamp)
}

private func assertNoAppStartUp() {
XCTAssertNil(SentrySDK.getAppStartMeasurement())
}
Expand Down