Skip to content

Commit

Permalink
Span ending conditions (#384)
Browse files Browse the repository at this point in the history
* Span ending conditions

* Changes requested in code review

---------

Co-authored-by: Robert <[email protected]>
  • Loading branch information
robert-smartbear and Robert authored Feb 12, 2025
1 parent fa26ce7 commit b40fc3d
Show file tree
Hide file tree
Showing 28 changed files with 1,193 additions and 24 deletions.
20 changes: 20 additions & 0 deletions BugsnagPerformance.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@
960EECF32B24CA24009FAA11 /* BugsnagPerformanceTrackedViewContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = 960EECF22B24C89A009FAA11 /* BugsnagPerformanceTrackedViewContainer.h */; settings = {ATTRIBUTES = (Public, ); }; };
962D7B342C7DEDD0004330E4 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 962D7B332C7DEDD0004330E4 /* QuartzCore.framework */; };
962F80F229DB03A400BE82BC /* PerformanceMicrobenchmarkTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 962F80F129DB03A400BE82BC /* PerformanceMicrobenchmarkTests.swift */; };
964B78502D39D36900FF077D /* BugsnagPerformanceSpanCondition.h in Headers */ = {isa = PBXBuildFile; fileRef = 964B784F2D39D36900FF077D /* BugsnagPerformanceSpanCondition.h */; settings = {ATTRIBUTES = (Public, ); }; };
964B78522D47D26F00FF077D /* ConditionTimeoutExecutor.h in Headers */ = {isa = PBXBuildFile; fileRef = 964B78512D47D26F00FF077D /* ConditionTimeoutExecutor.h */; };
964B78562D4B924C00FF077D /* BugsnagPerformanceSpanCondition.mm in Sources */ = {isa = PBXBuildFile; fileRef = 964B78552D4B924C00FF077D /* BugsnagPerformanceSpanCondition.mm */; };
964B785B2D4C20C000FF077D /* BugsnagPerformanceSpanConditionTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 964B785A2D4C20C000FF077D /* BugsnagPerformanceSpanConditionTests.mm */; };
964B785F2D504FD800FF077D /* BugsnagPerformanceSpanCondition+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 964B785E2D504FD800FF077D /* BugsnagPerformanceSpanCondition+Private.h */; };
966634D12C8909BB004A934D /* FrameMetricsCollector.h in Headers */ = {isa = PBXBuildFile; fileRef = 966634D02C8909BB004A934D /* FrameMetricsCollector.h */; };
966634D32C8909D9004A934D /* FrameMetricsCollector.mm in Sources */ = {isa = PBXBuildFile; fileRef = 966634D22C8909D9004A934D /* FrameMetricsCollector.mm */; };
966634D52C8A3647004A934D /* FrameMetricsSnapshot.h in Headers */ = {isa = PBXBuildFile; fileRef = 966634D42C8A3640004A934D /* FrameMetricsSnapshot.h */; };
Expand Down Expand Up @@ -323,6 +328,11 @@
960EECF22B24C89A009FAA11 /* BugsnagPerformanceTrackedViewContainer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BugsnagPerformanceTrackedViewContainer.h; sourceTree = "<group>"; };
962D7B332C7DEDD0004330E4 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.4.sdk/System/Library/Frameworks/QuartzCore.framework; sourceTree = DEVELOPER_DIR; };
962F80F129DB03A400BE82BC /* PerformanceMicrobenchmarkTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PerformanceMicrobenchmarkTests.swift; sourceTree = "<group>"; };
964B784F2D39D36900FF077D /* BugsnagPerformanceSpanCondition.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BugsnagPerformanceSpanCondition.h; sourceTree = "<group>"; };
964B78512D47D26F00FF077D /* ConditionTimeoutExecutor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ConditionTimeoutExecutor.h; sourceTree = "<group>"; };
964B78552D4B924C00FF077D /* BugsnagPerformanceSpanCondition.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = BugsnagPerformanceSpanCondition.mm; sourceTree = "<group>"; };
964B785A2D4C20C000FF077D /* BugsnagPerformanceSpanConditionTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = BugsnagPerformanceSpanConditionTests.mm; sourceTree = "<group>"; };
964B785E2D504FD800FF077D /* BugsnagPerformanceSpanCondition+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "BugsnagPerformanceSpanCondition+Private.h"; sourceTree = "<group>"; };
966634D02C8909BB004A934D /* FrameMetricsCollector.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FrameMetricsCollector.h; sourceTree = "<group>"; };
966634D22C8909D9004A934D /* FrameMetricsCollector.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FrameMetricsCollector.mm; sourceTree = "<group>"; };
966634D42C8A3640004A934D /* FrameMetricsSnapshot.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FrameMetricsSnapshot.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -518,6 +528,7 @@
CB0AD76929657381002A3FB6 /* BugsnagPerformanceErrors.h */,
0921F0292A67CBD600C764EB /* BugsnagPerformanceNetworkRequestInfo.h */,
0122C21729019770002D243C /* BugsnagPerformanceSpan.h */,
964B784F2D39D36900FF077D /* BugsnagPerformanceSpanCondition.h */,
0987F2772C32D4AD00777FD8 /* BugsnagPerformanceSpanContext.h */,
CB7FD92F299D2EF200499E13 /* BugsnagPerformanceSpanOptions.h */,
960EECF22B24C89A009FAA11 /* BugsnagPerformanceTrackedViewContainer.h */,
Expand Down Expand Up @@ -554,11 +565,13 @@
CB78819B29E587CE00A58906 /* BugsnagPerformanceLibrary.h */,
CB78819A29E587CE00A58906 /* BugsnagPerformanceLibrary.mm */,
0122C23229019770002D243C /* BugsnagPerformanceSpan+Private.h */,
964B785E2D504FD800FF077D /* BugsnagPerformanceSpanCondition+Private.h */,
09DC62282C6A2EF6000AA8E1 /* BugsnagPerformanceSpanContext+Private.h */,
CBE0873029FA984C007455F2 /* BugsnagPerformanceViewType.mm */,
CBE0873129FA984C007455F2 /* BugsnagPerformanceViewType+Private.h */,
09F23A8A2CE351ED00F0D769 /* BugsnagSwiftTools.h */,
09F23A8B2CE351ED00F0D769 /* BugsnagSwiftTools.m */,
964B78512D47D26F00FF077D /* ConditionTimeoutExecutor.h */,
CBA22C942A0137230066A2C1 /* EarlyConfiguration.h */,
CBA22C952A0137230066A2C1 /* EarlyConfiguration.mm */,
CBEC51D42976BCAD009C0CE3 /* Filesystem.h */,
Expand Down Expand Up @@ -599,6 +612,7 @@
01A414CC2913C0F0003152A4 /* SpanAttributes.mm */,
96D4160D29F276FE00AEE435 /* SpanAttributesProvider.h */,
96D4160B29F276E400AEE435 /* SpanAttributesProvider.mm */,
964B78552D4B924C00FF077D /* BugsnagPerformanceSpanCondition.mm */,
0122C22329019770002D243C /* SpanKind.h */,
CB7FD935299D330500499E13 /* SpanOptions.h */,
96D55C7D2A1EA5A8006D1F29 /* SpanStackingHandler.h */,
Expand Down Expand Up @@ -661,6 +675,7 @@
children = (
CB0AD75C29641B59002A3FB6 /* BatchTests.mm */,
CB0AD76B296578D9002A3FB6 /* BugsnagPerformanceConfigurationTests.mm */,
964B785A2D4C20C000FF077D /* BugsnagPerformanceSpanConditionTests.mm */,
0122C25F29019C05002D243C /* BugsnagPerformanceTests.mm */,
09E313032BF363020081F219 /* CrossTalkTests.mm */,
CBEC51C8296ED98F009C0CE3 /* FileBasedTest.h */,
Expand Down Expand Up @@ -847,11 +862,14 @@
96D55C832A1EBA35006D1F29 /* SpanActivityState.h in Headers */,
CBE0873329FA984C007455F2 /* BugsnagPerformanceViewType+Private.h in Headers */,
CBEC51D62976BCAD009C0CE3 /* Filesystem.h in Headers */,
964B78502D39D36900FF077D /* BugsnagPerformanceSpanCondition.h in Headers */,
0987F2792C32D4AD00777FD8 /* BugsnagPerformanceSpanContext.h in Headers */,
964B78522D47D26F00FF077D /* ConditionTimeoutExecutor.h in Headers */,
CBEC51C0296DB312009C0CE3 /* JSON.h in Headers */,
09B473072B23087D0024CF11 /* WeakSpansList.h in Headers */,
09F23A8C2CE351ED00F0D769 /* BugsnagSwiftTools.h in Headers */,
0921F02B2A67CBD600C764EB /* BugsnagPerformanceNetworkRequestInfo.h in Headers */,
964B785F2D504FD800FF077D /* BugsnagPerformanceSpanCondition+Private.h in Headers */,
0122C24A29019770002D243C /* AppStartupInstrumentation.h in Headers */,
CB04969B2915194E0097E526 /* OtlpUploader.h in Headers */,
966634D12C8909BB004A934D /* FrameMetricsCollector.h in Headers */,
Expand Down Expand Up @@ -1167,6 +1185,7 @@
01A414C52912B93F003152A4 /* ResourceAttributesTests.mm in Sources */,
09B4730A2B2313570024CF11 /* WeakSpansListTests.mm in Sources */,
CB2B8A9B2A0924A50054FBBE /* SpanTests.mm in Sources */,
964B785B2D4C20C000FF077D /* BugsnagPerformanceSpanConditionTests.mm in Sources */,
CBEC51CE296EEF52009C0CE3 /* PersistenceTests.mm in Sources */,
96D55C882A1EE26A006D1F29 /* SpanStackingHandlerTests.mm in Sources */,
CBEC51D92976D54B009C0CE3 /* FilesystemTests.m in Sources */,
Expand Down Expand Up @@ -1232,6 +1251,7 @@
0987F27A2C32D4AD00777FD8 /* BugsnagPerformanceSpanContext.mm in Sources */,
09F23A8D2CE351ED00F0D769 /* BugsnagSwiftTools.m in Sources */,
968AA5FD2CCA5AB000BA69CF /* BSGPerformanceSharedSessionProxy.mm in Sources */,
964B78562D4B924C00FF077D /* BugsnagPerformanceSpanCondition.mm in Sources */,
CBEC51DD2976F1F9009C0CE3 /* RetryQueue.mm in Sources */,
01A414CE2913C0F0003152A4 /* SpanAttributes.mm in Sources */,
CB34771E29068C350033759C /* NSURLSession+Instrumentation.mm in Sources */,
Expand Down
2 changes: 2 additions & 0 deletions Sources/BugsnagPerformance/Private/BugsnagPerformanceImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#import "NetworkHeaderInjector.h"
#import "OtlpTraceEncoding.h"
#import "FrameRateMetrics/FrameMetricsCollector.h"
#import "ConditionTimeoutExecutor.h"

#import <mutex>

Expand Down Expand Up @@ -81,6 +82,7 @@ class BugsnagPerformanceImpl: public PhasedStartup {
std::shared_ptr<class Sampler> sampler_;
std::shared_ptr<NetworkHeaderInjector> networkHeaderInjector_;
FrameMetricsCollector *frameMetricsCollector_;
std::shared_ptr<ConditionTimeoutExecutor> conditionTimeoutExecutor_;
std::shared_ptr<Tracer> tracer_;
std::unique_ptr<RetryQueue> retryQueue_;
AppStateTracker *appStateTracker_;
Expand Down
4 changes: 3 additions & 1 deletion Sources/BugsnagPerformance/Private/BugsnagPerformanceImpl.mm
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#import "BugsnagPerformanceCrossTalkAPI.h"
#import "Utils.h"
#import "FrameRateMetrics/FrameMetricsCollector.h"
#import "ConditionTimeoutExecutor.h"

using namespace bugsnag;

Expand All @@ -42,7 +43,8 @@
, sampler_(std::make_shared<Sampler>())
, networkHeaderInjector_(std::make_shared<NetworkHeaderInjector>(spanAttributesProvider_, spanStackingHandler_, sampler_))
, frameMetricsCollector_([FrameMetricsCollector new])
, tracer_(std::make_shared<Tracer>(spanStackingHandler_, sampler_, batch_, frameMetricsCollector_, ^{this->onSpanStarted();}))
, conditionTimeoutExecutor_(std::make_shared<ConditionTimeoutExecutor>())
, tracer_(std::make_shared<Tracer>(spanStackingHandler_, sampler_, batch_, frameMetricsCollector_, conditionTimeoutExecutor_, ^{this->onSpanStarted();}))
, retryQueue_(std::make_unique<RetryQueue>([persistence_->bugsnagPerformanceDir() stringByAppendingPathComponent:@"retry-queue"]))
, appStateTracker_(appStateTracker)
, viewControllersToSpans_([NSMapTable mapTableWithKeyOptions:NSMapTableWeakMemory | NSMapTableObjectPointerPersonality
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#import "SpanKind.h"
#import "FrameRateMetrics/FrameMetricsSnapshot.h"
#import "SpanOptions.h"
#import "BugsnagPerformanceSpanCondition+Private.h"

#import <memory>

Expand All @@ -29,6 +30,7 @@ typedef enum {
} SpanState;

typedef void (^SpanLifecycleCallback)(BugsnagPerformanceSpan * _Nonnull);
typedef BugsnagPerformanceSpanCondition *_Nullable(^SpanBlockedCallback)(BugsnagPerformanceSpan * _Nonnull, NSTimeInterval timeout);

NS_ASSUME_NONNULL_BEGIN

Expand All @@ -49,19 +51,20 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic,readonly) NSMutableDictionary *attributes;
@property (nonatomic) SpanLifecycleCallback onSpanEndSet;
@property (nonatomic) SpanLifecycleCallback onSpanClosed;
@property (nonatomic) SpanBlockedCallback onSpanBlocked;
@property (nonatomic,readwrite) SpanId parentId;
@property (nonatomic) double samplingProbability;
@property (nonatomic) BSGTriState firstClass;
@property (nonatomic) SpanKind kind;
@property (nonatomic,readwrite) BOOL isMutable;
@property (nonatomic,readwrite) BOOL hasBeenProcessed;
@property (nonatomic,readonly) BOOL isBlocked;
@property (nonatomic,readonly) NSUInteger attributeCountLimit;
@property (nonatomic,readwrite) BOOL wasStartOrEndTimeProvided;
@property (nonatomic) MetricsOptions metricsOptions;
@property (nonatomic, strong) FrameMetricsSnapshot *startFramerateSnapshot;
@property (nonatomic, strong) FrameMetricsSnapshot *endFramerateSnapshot;


@property (nonatomic, strong) NSMutableArray<BugsnagPerformanceSpanCondition *> *activeConditions;

@property(nonatomic) uint64_t startClock;

Expand All @@ -76,7 +79,8 @@ NS_ASSUME_NONNULL_BEGIN
attributeCountLimit:(NSUInteger)attributeCountLimit
metricsOptions:(MetricsOptions) metricsOptions
onSpanEndSet:(SpanLifecycleCallback) onSpanEndSet
onSpanClosed:(SpanLifecycleCallback) onSpanEnded NS_DESIGNATED_INITIALIZER;
onSpanClosed:(SpanLifecycleCallback) onSpanEnded
onSpanBlocked:(SpanBlockedCallback) onSpanBlocked NS_DESIGNATED_INITIALIZER;

- (void)internalSetAttribute:(NSString *)attributeName withValue:(_Nullable id)value;
- (void)internalSetMultipleAttributes:(NSDictionary *)attributes;
Expand All @@ -93,6 +97,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)updateName:(NSString *)name;
- (void)updateStartTime:(NSDate *)startTime;
- (void)updateSamplingProbability:(double) value;
- (void)markEndAbsoluteTime:(CFAbsoluteTime)endTime;

- (void)forceMutate:(void (^)())block;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// BugsnagPerformanceSpanCondition+Private.h
// BugsnagPerformance-iOS
//
// Created by Robert B on 15/01/2025.
// Copyright © 2025 Bugsnag. All rights reserved.
//

#import <BugsnagPerformance/BugsnagPerformanceSpanCondition.h>

@class BugsnagPerformanceSpan;
@class BugsnagPerformanceSpanContext;

typedef uint64_t SpanConditionId;
typedef void (^ SpanConditionClosedCallback)(BugsnagPerformanceSpanCondition *condition, CFAbsoluteTime endTime);
typedef BugsnagPerformanceSpanContext *(^ SpanConditionUpgradedCallback)(BugsnagPerformanceSpanCondition *condition);
typedef void (^ SpanConditionDeavtivatedCallback)(BugsnagPerformanceSpanCondition *condition);

@interface BugsnagPerformanceSpanCondition ()

@property (nonatomic, readonly) SpanConditionId conditionId;
@property (nonatomic, weak) BugsnagPerformanceSpan *span;

+ (instancetype)conditionWithSpan:(BugsnagPerformanceSpan *)span
onClosedCallback:(SpanConditionClosedCallback)onClosedCallback
onUpgradedCallback:(SpanConditionUpgradedCallback)onUpgradedCallback;

- (void)didTimeout;
- (void)addOnDeactivatedCallback:(SpanConditionDeavtivatedCallback)onDeactivated;

@end

177 changes: 177 additions & 0 deletions Sources/BugsnagPerformance/Private/BugsnagPerformanceSpanCondition.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
//
// SpanCondition.m
// BugsnagPerformance-iOS
//
// Created by Robert B on 15/01/2025.
// Copyright © 2025 Bugsnag. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "BugsnagPerformanceSpanCondition+Private.h"

typedef NS_ENUM(uint8_t, BSGSpanConditionState) {
BSGSpanConditionStateInitial = 0,
BSGSpanConditionStateUpgraded = 1,
BSGSpanConditionStateClosed = 2,
BSGSpanConditionStateCancelled = 3,
};

@interface SpanConditionIdProvider: NSObject

@property (atomic) SpanConditionId conditionId;

+ (instancetype)sharedInstance;

- (SpanConditionId)next;

@end

@implementation SpanConditionIdProvider

+ (instancetype)sharedInstance {
static id sharedInstance;
static dispatch_once_t once;
dispatch_once(&once, ^{ sharedInstance = [[self alloc] init]; });
return sharedInstance;
}

- (instancetype)init {
self = [super init];
if (self) {
_conditionId = 0;
}
return self;
}

- (SpanConditionId)next {
@synchronized (self) {
return self.conditionId++;
}
}

@end

@interface BugsnagPerformanceSpanCondition ()

@property (nonatomic) SpanConditionId conditionId_;
@property (nonatomic) BSGSpanConditionState state;
@property (nonatomic) SpanConditionClosedCallback onClosedCallback;
@property (nonatomic) SpanConditionUpgradedCallback onUpgradedCallback;
@property (nonatomic) NSMutableArray<SpanConditionDeavtivatedCallback> *onDeactivatedCallbacks;

@end

@implementation BugsnagPerformanceSpanCondition

+ (instancetype)conditionWithSpan:(BugsnagPerformanceSpan *)span
onClosedCallback:(SpanConditionClosedCallback)onClosedCallback
onUpgradedCallback:(SpanConditionUpgradedCallback)onUpgradedCallback {
return [[self alloc] initWithId:[[SpanConditionIdProvider sharedInstance] next]
span:span
onClosedCallback:onClosedCallback
onUpgradedCallback:onUpgradedCallback];
}

- (instancetype)initWithId:(SpanConditionId)conditionId
span:(BugsnagPerformanceSpan *)span
onClosedCallback:(SpanConditionClosedCallback)onClosedCallback
onUpgradedCallback:(SpanConditionUpgradedCallback)onUpgradedCallback {
self = [super init];
if (self) {
_conditionId_ = conditionId;
_state = BSGSpanConditionStateInitial;
_span = span;
_onClosedCallback = onClosedCallback;
_onUpgradedCallback = onUpgradedCallback;
_onDeactivatedCallbacks = [NSMutableArray new];
}
return self;
}

- (BOOL)isActive {
@synchronized (self) {
return self.state == BSGSpanConditionStateInitial || self.state == BSGSpanConditionStateUpgraded;
}
}

- (SpanConditionId)conditionId {
return self.conditionId_;
}

- (void)closeWithEndTime:(NSDate *)endTime {
@synchronized (self) {
switch (self.state) {
case BSGSpanConditionStateClosed:
case BSGSpanConditionStateCancelled:
return;
case BSGSpanConditionStateInitial:
case BSGSpanConditionStateUpgraded:
self.state = BSGSpanConditionStateClosed;
break;
}
}
self.onClosedCallback(self, [endTime timeIntervalSinceReferenceDate]);
[self didDeactivate];
}

- (BugsnagPerformanceSpanContext *)upgrade {
@synchronized (self) {
switch (self.state) {
case BSGSpanConditionStateClosed:
case BSGSpanConditionStateCancelled:
case BSGSpanConditionStateUpgraded:
return nil;
case BSGSpanConditionStateInitial:
self.state = BSGSpanConditionStateUpgraded;
break;
}
}
return self.onUpgradedCallback(self);
}

- (void)cancel {
@synchronized (self) {
switch (self.state) {
case BSGSpanConditionStateClosed:
case BSGSpanConditionStateCancelled:
return;
case BSGSpanConditionStateInitial:
case BSGSpanConditionStateUpgraded:
self.state = BSGSpanConditionStateCancelled;
break;
}
}
[self didDeactivate];
}

- (void)didTimeout {
@synchronized (self) {
switch (self.state) {
case BSGSpanConditionStateClosed:
case BSGSpanConditionStateCancelled:
case BSGSpanConditionStateUpgraded:
return;
case BSGSpanConditionStateInitial:
break;
}
[self cancel];
}
}

- (void)addOnDeactivatedCallback:(SpanConditionDeavtivatedCallback)onDeactivated {
@synchronized (self) {
[self.onDeactivatedCallbacks addObject:onDeactivated];
}
}

- (void)didDeactivate {
NSArray *callbacks;
@synchronized (self) {
callbacks = [self.onDeactivatedCallbacks copy];
}
for (SpanConditionDeavtivatedCallback callback in callbacks) {
callback(self);
}
}

@end
Loading

0 comments on commit b40fc3d

Please sign in to comment.