Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Delete view spans that were started during the prewarm phase
Browse files Browse the repository at this point in the history
kstenerud committed Nov 21, 2023
1 parent 4521e19 commit 40acf2e
Showing 9 changed files with 194 additions and 20 deletions.
6 changes: 5 additions & 1 deletion BugsnagPerformance.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
@@ -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 */; };
@@ -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; };
@@ -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 */,
@@ -541,6 +543,7 @@
96D55C872A1EE26A006D1F29 /* SpanStackingHandlerTests.mm */,
CB2B8A9A2A0924A50054FBBE /* SpanTests.mm */,
CB747D20299E5458003CA1B4 /* TimeTests.mm */,
09509B742ADFE9A900A358EC /* TracerTests.mm */,
CB0AD75A295F27DD002A3FB6 /* WorkerTests.mm */,
);
path = BugsnagPerformanceTests;
@@ -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 */,
3 changes: 2 additions & 1 deletion Sources/BugsnagPerformance/Private/EarlyConfiguration.h
Original file line number Diff line number Diff line change
@@ -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

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

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

@end

1 change: 1 addition & 0 deletions Sources/BugsnagPerformance/Private/EarlyConfiguration.mm
Original file line number Diff line number Diff line change
@@ -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;
4 changes: 4 additions & 0 deletions Sources/BugsnagPerformance/Private/Span.h
Original file line number Diff line number Diff line change
@@ -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)) {
12 changes: 10 additions & 2 deletions Sources/BugsnagPerformance/Private/Tracer.h
Original file line number Diff line number Diff line change
@@ -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;
@@ -55,23 +55,31 @@ class Tracer: public PhasedStartup {
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> isCapturingEarlyNetworkSpans_{true};
std::mutex earlyNetworkSpansMutex_;
NSMutableArray<BugsnagPerformanceSpan *> *earlyNetworkSpans_;

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

std::shared_ptr<Batch> batch_;
void (^onSpanStarted_)(){nil};
std::function<void(NSString *)> onViewLoadSpanStarted_{};
std::function<void(NSString *)> onViewLoadSpanStarted_{ [](NSString *){} };
BugsnagPerformanceNetworkRequestCallback networkRequestCallback_;

BugsnagPerformanceSpan *startSpan(NSString *name, SpanOptions options, BSGFirstClass defaultFirstClass) noexcept;
void tryAddSpanToBatch(std::shared_ptr<SpanData> spanData);
void markEarlyNetworkSpan(BugsnagPerformanceSpan *span) noexcept;
void markPrewarmSpan(BugsnagPerformanceSpan *span) noexcept;
void finishEarlyNetworkSpans() noexcept;
};
}
70 changes: 54 additions & 16 deletions Sources/BugsnagPerformance/Private/Tracer.mm
Original file line number Diff line number Diff line change
@@ -25,10 +25,16 @@
: spanStackingHandler_(spanStackingHandler)
, sampler_(sampler)
, earlyNetworkSpans_([NSMutableArray new])
, prewarmSpans_([NSMutableArray new])
, batch_(batch)
, onSpanStarted_(onSpanStarted)
{}

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

void
Tracer::configure(BugsnagPerformanceConfiguration *config) noexcept {
networkRequestCallback_ = config.networkRequestCallback;
@@ -111,7 +117,11 @@
options.firstClass = BSGFirstClassNo;
}
}
return startSpan(name, options, BSGFirstClassYes);
auto span = startSpan(name, options, BSGFirstClassYes);
if (isCapturingPrewarmSpans_) {
markPrewarmSpan(span);
}
return span;
}

BugsnagPerformanceSpan *
@@ -139,35 +149,63 @@
BugsnagPerformanceSpan *
Tracer::startViewLoadPhaseSpan(NSString *name,
SpanOptions options) noexcept {
return startSpan(name, options, BSGFirstClassUnset);
auto span = startSpan(name, options, BSGFirstClassUnset);
if (isCapturingPrewarmSpans_) {
markPrewarmSpan(span);
}
return span;
}

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

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

void
Tracer::onPrewarmPhaseEnded(void) noexcept {
std::lock_guard<std::mutex> guard(prewarmSpansMutex_);
if (isCapturingPrewarmSpans_) {
isCapturingPrewarmSpans_ = false;
for (BugsnagPerformanceSpan *span: prewarmSpans_) {
cancelQueuedSpan(span);
}
[prewarmSpans_ removeAllObjects];
}
}

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

void Tracer::finishEarlyNetworkSpans() noexcept {
isCapturingEarlyNetworkSpans_ = false;
std::lock_guard<std::mutex> guard(earlyNetworkSpansMutex_);
if (networkRequestCallback_ != nil) {
for (BugsnagPerformanceSpan *span: earlyNetworkSpans_) {
auto info = [BugsnagPerformanceNetworkRequestInfo new];
NSString *urlString = [span getAttribute:httpUrlAttributeKey];
info.url = [NSURL URLWithString:urlString];
info = networkRequestCallback_(info);
if (info.url == nil) {
batch_->removeSpan(span.traceId, span.spanId);
} else {
[span addAttribute:httpUrlAttributeKey withValue:(NSString *_Nonnull)info.url.absoluteString];
if (isCapturingEarlyNetworkSpans_) {
isCapturingEarlyNetworkSpans_ = false;
if (networkRequestCallback_ != nil) {
for (BugsnagPerformanceSpan *span: earlyNetworkSpans_) {
auto info = [BugsnagPerformanceNetworkRequestInfo new];
NSString *urlString = [span getAttribute:httpUrlAttributeKey];
info.url = [NSURL URLWithString:urlString];
info = networkRequestCallback_(info);
if (info.url == nil) {
cancelQueuedSpan(span);
} else {
[span addAttribute:httpUrlAttributeKey withValue:(NSString *_Nonnull)info.url.absoluteString];
}
}
}
[earlyNetworkSpans_ removeAllObjects];
}
[earlyNetworkSpans_ removeAllObjects];
}
4 changes: 4 additions & 0 deletions Sources/BugsnagPerformance/Public/BugsnagPerformanceSpan.mm
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
@@ -28,6 +28,8 @@ OBJC_EXPORT

- (instancetype)init NS_UNAVAILABLE;

- (void)abort;

- (void)end;

- (void)endWithEndTime:(NSDate *)endTime NS_SWIFT_NAME(end(endTime:));
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

0 comments on commit 40acf2e

Please sign in to comment.