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

[Plat-10875] Prewarm view spans #211

Merged
merged 1 commit into from
Nov 22, 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion BugsnagPerformance.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
01EAF980291AAF19007AC627 /* Utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 01EAF97F291AAF19007AC627 /* Utils.h */; };
0921F02B2A67CBD600C764EB /* BugsnagPerformanceNetworkRequestInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 0921F0292A67CBD600C764EB /* BugsnagPerformanceNetworkRequestInfo.h */; settings = {ATTRIBUTES = (Public, ); }; };
0921F02C2A67CBD600C764EB /* BugsnagPerformanceNetworkRequestInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 0921F02A2A67CBD600C764EB /* BugsnagPerformanceNetworkRequestInfo.m */; };
09509B752ADFE9A900A358EC /* TracerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 09509B742ADFE9A900A358EC /* TracerTests.mm */; };
8A80DA8B2966CE940035BDA9 /* BugsnagPerformance.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 72E4BB63359EA30E80116E2A /* BugsnagPerformance.framework */; };
8A80DA912966CEB30035BDA9 /* ConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A80DA80296588840035BDA9 /* ConfigurationTests.swift */; };
962F80F229DB03A400BE82BC /* PerformanceMicrobenchmarkTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 962F80F129DB03A400BE82BC /* PerformanceMicrobenchmarkTests.swift */; };
Expand Down Expand Up @@ -227,6 +228,7 @@
01EF4A3E29067786003CC5A5 /* BugsnagPerformance.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = BugsnagPerformance.xcconfig; sourceTree = "<group>"; };
0921F0292A67CBD600C764EB /* BugsnagPerformanceNetworkRequestInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BugsnagPerformanceNetworkRequestInfo.h; sourceTree = "<group>"; };
0921F02A2A67CBD600C764EB /* BugsnagPerformanceNetworkRequestInfo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BugsnagPerformanceNetworkRequestInfo.m; sourceTree = "<group>"; };
09509B742ADFE9A900A358EC /* TracerTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = TracerTests.mm; sourceTree = "<group>"; };
72E4BB63359EA30E80116E2A /* BugsnagPerformance.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = BugsnagPerformance.framework; sourceTree = BUILT_PRODUCTS_DIR; };
8A80DA80296588840035BDA9 /* ConfigurationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationTests.swift; sourceTree = "<group>"; };
8A80DA872966CE940035BDA9 /* BugsnagPerformance-SwiftTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "BugsnagPerformance-SwiftTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -531,8 +533,8 @@
CBEC51C2296EC0AC009C0CE3 /* JSONTests.mm */,
CBEC51DE29782471009C0CE3 /* OtlpPackageTests.mm */,
0122C26029019C05002D243C /* OtlpTraceEncodingTests.mm */,
CB68FABB2A3C4208005B2CDB /* PersistentDeviceIDTest.mm */,
CBEC51CD296EEF52009C0CE3 /* PersistenceTests.mm */,
CB68FABB2A3C4208005B2CDB /* PersistentDeviceIDTest.mm */,
CBEC51C4296ED8BA009C0CE3 /* PersistentStateTests.mm */,
01A414C42912B93F003152A4 /* ResourceAttributesTests.mm */,
CBEC51E029793B1E009C0CE3 /* RetryQueueTests.mm */,
Expand All @@ -541,6 +543,7 @@
96D55C872A1EE26A006D1F29 /* SpanStackingHandlerTests.mm */,
CB2B8A9A2A0924A50054FBBE /* SpanTests.mm */,
CB747D20299E5458003CA1B4 /* TimeTests.mm */,
09509B742ADFE9A900A358EC /* TracerTests.mm */,
CB0AD75A295F27DD002A3FB6 /* WorkerTests.mm */,
);
path = BugsnagPerformanceTests;
Expand Down Expand Up @@ -851,6 +854,7 @@
CB68FABC2A3C4208005B2CDB /* PersistentDeviceIDTest.mm in Sources */,
CB747D21299E5458003CA1B4 /* TimeTests.mm in Sources */,
0122C27129019CEF002D243C /* OtlpTraceEncodingTests.mm in Sources */,
09509B752ADFE9A900A358EC /* TracerTests.mm in Sources */,
962F80F229DB03A400BE82BC /* PerformanceMicrobenchmarkTests.swift in Sources */,
CB747D1F299E4984003CA1B4 /* SpanOptionsTests.mm in Sources */,
CB0AD76C296578D9002A3FB6 /* BugsnagPerformanceConfigurationTests.mm in Sources */,
Expand Down
3 changes: 2 additions & 1 deletion Sources/BugsnagPerformance/Private/EarlyConfiguration.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ NS_ASSUME_NONNULL_BEGIN
/**
* The early confguration happens duing the library early init phase (before main is called), and thus cannot be done
* using a configuration object passed in by the user (because control hasn't been passed to user code yet).
* Instead, we get the early configuration from the app's Info.plist file.
* Instead, we get the early configuration from the app's Info.plist file and environment variables.
*/
@interface BSGEarlyConfiguration : NSObject

Expand All @@ -30,6 +30,7 @@ NS_ASSUME_NONNULL_BEGIN

@property(nonatomic, readonly) BOOL enableSwizzling;
@property(nonatomic, readonly) BOOL swizzleViewLoadPreMain;
@property(nonatomic, readwrite) BOOL appWasLaunchedPreWarmed;

@end

Expand Down
1 change: 1 addition & 0 deletions Sources/BugsnagPerformance/Private/EarlyConfiguration.mm
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ - (instancetype) initWithBundleDictionary:(NSDictionary *)dict {
}
id swizzleViewLoadPreMain = [dict valueForKeyPath:@"bugsnag.performance.swizzleViewLoadPreMain"];
_swizzleViewLoadPreMain = swizzleViewLoadPreMain == nil || [swizzleViewLoadPreMain boolValue];
_appWasLaunchedPreWarmed = [NSProcessInfo.processInfo.environment[@"ActivePrewarm"] isEqualToString:@"1"];
}

return self;
Expand Down
4 changes: 4 additions & 0 deletions Sources/BugsnagPerformance/Private/Span.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ class Span {
return isEnded_;
}

void abort() noexcept {
isEnded_ = true;
}

void end(CFAbsoluteTime time) noexcept {
bool expected = false;
if (!isEnded_.compare_exchange_strong(expected, true)) {
Expand Down
18 changes: 13 additions & 5 deletions Sources/BugsnagPerformance/Private/Tracer.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class Tracer: public PhasedStartup {
void (^onSpanStarted)()) noexcept;
~Tracer() {};

void earlyConfigure(BSGEarlyConfiguration *) noexcept {}
void earlyConfigure(BSGEarlyConfiguration *) noexcept;
void earlySetup() noexcept {}
void configure(BugsnagPerformanceConfiguration *configuration) noexcept;
void start() noexcept;
Expand All @@ -51,19 +51,26 @@ class Tracer: public PhasedStartup {
SpanOptions options) noexcept;

BugsnagPerformanceSpan *startNetworkSpan(NSURL *url, NSString *httpMethod, SpanOptions options) noexcept;

BugsnagPerformanceSpan *startViewLoadPhaseSpan(NSString *name, SpanOptions options) noexcept;

void cancelQueuedSpan(BugsnagPerformanceSpan *span) noexcept;

void onPrewarmPhaseEnded(void) noexcept;

private:
Tracer() = delete;
std::shared_ptr<Sampler> sampler_;
std::shared_ptr<SpanStackingHandler> spanStackingHandler_;

std::atomic<bool> isEarlySpansPhase_{true};
std::mutex earlySpansMutex_;
std::atomic<bool> isCapturingEarlyNetworkSpans_{true};
std::mutex earlyNetworkSpansMutex_;
NSMutableArray<BugsnagPerformanceSpan *> *earlyNetworkSpans_;

std::atomic<bool> willDiscardPrewarmSpans_{false};
std::mutex prewarmSpansMutex_;
NSMutableArray<BugsnagPerformanceSpan *> *prewarmSpans_;

std::shared_ptr<Batch> batch_;
void (^onSpanStarted_)(){ ^(){} };
std::function<void(NSString *)> onViewLoadSpanStarted_{ [](NSString *){} };
Expand All @@ -76,6 +83,7 @@ class Tracer: public PhasedStartup {
BugsnagPerformanceSpan *startSpan(NSString *name, SpanOptions options, BSGFirstClass defaultFirstClass) noexcept;
void trySampleAndAddSpanToBatch(std::shared_ptr<SpanData> spanData);
void markEarlyNetworkSpan(BugsnagPerformanceSpan *span) noexcept;
void endEarlySpansPhase() noexcept;
void markPrewarmSpan(BugsnagPerformanceSpan *span) noexcept;
void endEarlyNetworkSpansPhase() noexcept;
};
}
53 changes: 44 additions & 9 deletions Sources/BugsnagPerformance/Private/Tracer.mm
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,23 @@
: spanStackingHandler_(spanStackingHandler)
, sampler_(sampler)
, earlyNetworkSpans_([NSMutableArray new])
, prewarmSpans_([NSMutableArray new])
, batch_(batch)
, onSpanStarted_(onSpanStarted)
{}

void
Tracer::earlyConfigure(BSGEarlyConfiguration *config) noexcept {
willDiscardPrewarmSpans_ = config.appWasLaunchedPreWarmed;
}

void
Tracer::configure(BugsnagPerformanceConfiguration *config) noexcept {
auto networkRequestCallback = config.networkRequestCallback;
if (networkRequestCallback != nullptr) {
networkRequestCallback_ = networkRequestCallback;
}
endEarlySpansPhase();
endEarlyNetworkSpansPhase();
}

void
Expand Down Expand Up @@ -112,7 +118,11 @@
options.firstClass = BSGFirstClassNo;
}
}
return startSpan(name, options, BSGFirstClassYes);
auto span = startSpan(name, options, BSGFirstClassYes);
if (willDiscardPrewarmSpans_) {
markPrewarmSpan(span);
}
return span;
}

BugsnagPerformanceSpan *
Expand All @@ -128,7 +138,7 @@
auto name = [NSString stringWithFormat:@"[HTTP/%@]", httpMethod];
auto span = startSpan(name, options, BSGFirstClassUnset);
[span addAttribute:httpUrlAttributeKey withValue:(NSString *_Nonnull)url.absoluteString];
if (isEarlySpansPhase_) {
if (isCapturingEarlyNetworkSpans_) {
markEarlyNetworkSpan(span);
}
return span;
Expand All @@ -137,25 +147,50 @@
BugsnagPerformanceSpan *
Tracer::startViewLoadPhaseSpan(NSString *name,
SpanOptions options) noexcept {
return startSpan(name, options, BSGFirstClassUnset);
auto span = startSpan(name, options, BSGFirstClassUnset);
if (willDiscardPrewarmSpans_) {
markPrewarmSpan(span);
}
return span;
}

void Tracer::cancelQueuedSpan(BugsnagPerformanceSpan *span) noexcept {
if (span) {
[span abort];
batch_->removeSpan(span.traceId, span.spanId);
}
}

void Tracer::markPrewarmSpan(BugsnagPerformanceSpan *span) noexcept {
std::lock_guard<std::mutex> guard(prewarmSpansMutex_);
if (willDiscardPrewarmSpans_) {
[prewarmSpans_ addObject:span];
}
}

void
Tracer::onPrewarmPhaseEnded(void) noexcept {
std::lock_guard<std::mutex> guard(prewarmSpansMutex_);
willDiscardPrewarmSpans_ = false;
for (BugsnagPerformanceSpan *span: prewarmSpans_) {
// Only cancel unfinished prewarm spans
if (span.isValid) {
cancelQueuedSpan(span);
}
}
[prewarmSpans_ removeAllObjects];
}

void Tracer::markEarlyNetworkSpan(BugsnagPerformanceSpan *span) noexcept {
std::lock_guard<std::mutex> guard(earlySpansMutex_);
if (isEarlySpansPhase_) {
std::lock_guard<std::mutex> guard(earlyNetworkSpansMutex_);
if (isCapturingEarlyNetworkSpans_) {
[earlyNetworkSpans_ addObject:span];
}
}

void Tracer::endEarlySpansPhase() noexcept {
std::lock_guard<std::mutex> guard(earlySpansMutex_);
isEarlySpansPhase_ = false;
void Tracer::endEarlyNetworkSpansPhase() noexcept {
std::lock_guard<std::mutex> guard(earlyNetworkSpansMutex_);
isCapturingEarlyNetworkSpans_ = false;
for (BugsnagPerformanceSpan *span: earlyNetworkSpans_) {
auto info = [BugsnagPerformanceNetworkRequestInfo new];
NSString *urlString = [span getAttribute:httpUrlAttributeKey];
Expand Down
4 changes: 4 additions & 0 deletions Sources/BugsnagPerformance/Public/BugsnagPerformanceSpan.mm
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ - (void)dealloc {
// We want direct ivar access to avoid accessors copying unique_ptrs
#pragma clang diagnostic ignored "-Wdirect-ivar-access"

- (void)abort {
_span->abort();
}

- (void)end {
_span->end(CFABSOLUTETIME_INVALID);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ OBJC_EXPORT

- (instancetype)init NS_UNAVAILABLE;

- (void)abort;

- (void)end;

- (void)endWithEndTime:(NSDate *)endTime NS_SWIFT_NAME(end(endTime:));
Expand Down
112 changes: 112 additions & 0 deletions Tests/BugsnagPerformanceTests/TracerTests.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
//
// TracerTests.m
// BugsnagPerformance-iOSTests
//
// Created by Karl Stenerud on 18.10.23.
// Copyright © 2023 Bugsnag. All rights reserved.
//

#import <XCTest/XCTest.h>
#import "Tracer.h"

using namespace bugsnag;

@interface TracerTests : XCTestCase

@end

static BugsnagPerformanceConfiguration *newConfig() {
return [[BugsnagPerformanceConfiguration alloc] initWithApiKey:@"11111111111111111111111111111111"];
}

@implementation TracerTests

- (void)testPrewarmEndBefore {
auto earlyConfig = [BSGEarlyConfiguration new];
earlyConfig.appWasLaunchedPreWarmed = YES;
auto config = newConfig();
auto stackingHandler = std::make_shared<SpanStackingHandler>();
auto sampler = std::make_shared<Sampler>();
sampler->setProbability(1.0);
auto batch = std::make_shared<Batch>();
auto tracer = std::make_shared<Tracer>(stackingHandler, sampler, batch, ^(){});
tracer->earlyConfigure(earlyConfig);
tracer->earlySetup();
tracer->configure(config);
tracer->start();

SpanOptions spanOptions;
auto span = tracer->startViewLoadSpan(BugsnagPerformanceViewTypeUIKit, @"myclass", spanOptions);
[span end];
tracer->onPrewarmPhaseEnded();
auto spans = batch->drain(true);
XCTAssertEqual(spans->size(), 1UL);
}

- (void)testPrewarmEndAfter {
auto earlyConfig = [BSGEarlyConfiguration new];
earlyConfig.appWasLaunchedPreWarmed = YES;
auto config = newConfig();
auto stackingHandler = std::make_shared<SpanStackingHandler>();
auto sampler = std::make_shared<Sampler>();
sampler->setProbability(1.0);
auto batch = std::make_shared<Batch>();
auto tracer = std::make_shared<Tracer>(stackingHandler, sampler, batch, ^(){});
tracer->earlyConfigure(earlyConfig);
tracer->earlySetup();
tracer->configure(config);
tracer->start();

SpanOptions spanOptions;
auto span = tracer->startViewLoadSpan(BugsnagPerformanceViewTypeUIKit, @"myclass", spanOptions);
tracer->onPrewarmPhaseEnded();
[span end];
auto spans = batch->drain(true);
XCTAssertEqual(spans->size(), 0UL);
}

- (void)testNoPrewarmEndBefore {
auto earlyConfig = [BSGEarlyConfiguration new];
earlyConfig.appWasLaunchedPreWarmed = NO;
auto config = newConfig();
auto stackingHandler = std::make_shared<SpanStackingHandler>();
auto sampler = std::make_shared<Sampler>();
sampler->setProbability(1.0);
auto batch = std::make_shared<Batch>();
auto tracer = std::make_shared<Tracer>(stackingHandler, sampler, batch, ^(){});
tracer->earlyConfigure(earlyConfig);
tracer->earlySetup();
tracer->configure(config);
tracer->start();

SpanOptions spanOptions;
auto span = tracer->startViewLoadSpan(BugsnagPerformanceViewTypeUIKit, @"myclass", spanOptions);
[span end];
tracer->onPrewarmPhaseEnded();
auto spans = batch->drain(true);
XCTAssertEqual(spans->size(), 1UL);
}

- (void)testNoPrewarmEndAfter {
auto earlyConfig = [BSGEarlyConfiguration new];
earlyConfig.appWasLaunchedPreWarmed = NO;
auto config = newConfig();
auto stackingHandler = std::make_shared<SpanStackingHandler>();
auto sampler = std::make_shared<Sampler>();
sampler->setProbability(1.0);
auto batch = std::make_shared<Batch>();
auto tracer = std::make_shared<Tracer>(stackingHandler, sampler, batch, ^(){});
tracer->earlyConfigure(earlyConfig);
tracer->earlySetup();
tracer->configure(config);
tracer->start();

SpanOptions spanOptions;
auto span = tracer->startViewLoadSpan(BugsnagPerformanceViewTypeUIKit, @"myclass", spanOptions);
tracer->onPrewarmPhaseEnded();
[span end];
auto spans = batch->drain(true);
XCTAssertEqual(spans->size(), 1UL);
}

@end
Loading