From ba971cbaee48143a5a68fe62259e31ac73c0cdc7 Mon Sep 17 00:00:00 2001 From: Andrew McKnight Date: Thu, 6 Jul 2023 16:18:23 -0800 Subject: [PATCH 01/15] extract implementation of SentryProfilerState; extract SentrySample --- Sentry.xcodeproj/project.pbxproj | 41 ++-- .../Sentry/Profiling/SentryProfilerState.mm | 186 ++++++++++++++++++ Sources/Sentry/Profiling/SentrySample.m | 4 + Sources/Sentry/SentryProfileTimeseries.mm | 4 +- Sources/Sentry/SentryProfiler.mm | 177 +---------------- .../Sentry/include/SentryProfileTimeseries.h | 10 +- Sources/Sentry/include/SentrySample.h | 13 ++ 7 files changed, 233 insertions(+), 202 deletions(-) create mode 100644 Sources/Sentry/Profiling/SentryProfilerState.mm create mode 100644 Sources/Sentry/Profiling/SentrySample.m create mode 100644 Sources/Sentry/include/SentrySample.h diff --git a/Sentry.xcodeproj/project.pbxproj b/Sentry.xcodeproj/project.pbxproj index dab18d0dd8a..364df1cd441 100644 --- a/Sentry.xcodeproj/project.pbxproj +++ b/Sentry.xcodeproj/project.pbxproj @@ -601,6 +601,9 @@ 7DC83100239826280043DD9A /* SentryIntegrationProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 7DC830FF239826280043DD9A /* SentryIntegrationProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7DC8310A2398283C0043DD9A /* SentryCrashIntegration.h in Headers */ = {isa = PBXBuildFile; fileRef = 7DC831082398283C0043DD9A /* SentryCrashIntegration.h */; }; 7DC8310C2398283C0043DD9A /* SentryCrashIntegration.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DC831092398283C0043DD9A /* SentryCrashIntegration.m */; }; + 84281C432A578E5600EE88F2 /* SentryProfilerState.mm in Sources */ = {isa = PBXBuildFile; fileRef = 84281C422A578E5600EE88F2 /* SentryProfilerState.mm */; }; + 84281C462A57905700EE88F2 /* SentrySample.h in Headers */ = {isa = PBXBuildFile; fileRef = 84281C442A57905700EE88F2 /* SentrySample.h */; }; + 84281C472A57905700EE88F2 /* SentrySample.m in Sources */ = {isa = PBXBuildFile; fileRef = 84281C452A57905700EE88F2 /* SentrySample.m */; }; 8431EE5B29ADB8EA00D8DC56 /* SentryTimeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8431EE5A29ADB8EA00D8DC56 /* SentryTimeTests.m */; }; 8431EFD129B27B1100D8DC56 /* Sentry.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 63AA759B1EB8AEF500D153DE /* Sentry.framework */; settings = {ATTRIBUTES = (Required, ); }; }; 8431EFD329B27B1100D8DC56 /* Resources in Resources */ = {isa = PBXBuildFile; fileRef = 630C01951EC341D600C52CEF /* Resources */; }; @@ -1521,6 +1524,9 @@ 7DC831082398283C0043DD9A /* SentryCrashIntegration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryCrashIntegration.h; path = include/SentryCrashIntegration.h; sourceTree = ""; }; 7DC831092398283C0043DD9A /* SentryCrashIntegration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryCrashIntegration.m; sourceTree = ""; }; 8419C0C328C1889D001C8259 /* SentryProfilerSwiftTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryProfilerSwiftTests.swift; sourceTree = ""; }; + 84281C422A578E5600EE88F2 /* SentryProfilerState.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = SentryProfilerState.mm; path = "/Users/andrewmcknight/Code/organization/getsentry/repos/public/sentry-cocoa/Sources/Sentry/Profiling/SentryProfilerState.mm"; sourceTree = ""; }; + 84281C442A57905700EE88F2 /* SentrySample.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentrySample.h; path = ../include/SentrySample.h; sourceTree = ""; }; + 84281C452A57905700EE88F2 /* SentrySample.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentrySample.m; sourceTree = ""; }; 8431EE5A29ADB8EA00D8DC56 /* SentryTimeTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryTimeTests.m; sourceTree = ""; }; 8431EFD929B27B1100D8DC56 /* SentryProfilerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SentryProfilerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 8431EFDA29B27B1200D8DC56 /* SentryTests copy-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "SentryTests copy-Info.plist"; path = "/Users/andrewmcknight/Code/organization/getsentry/repos/public/sentry-cocoa/SentryTests copy-Info.plist"; sourceTree = ""; }; @@ -1556,7 +1562,6 @@ 844EDC74294144DB00C86F34 /* SentrySystemWrapper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentrySystemWrapper.h; path = include/SentrySystemWrapper.h; sourceTree = ""; }; 844EDC75294144DB00C86F34 /* SentrySystemWrapper.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SentrySystemWrapper.mm; sourceTree = ""; }; 844EDC7829415AB300C86F34 /* TestSentrySystemWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestSentrySystemWrapper.swift; sourceTree = ""; }; - 844EDC7B2942843400C86F34 /* SentryProfiler+SwiftTest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "SentryProfiler+SwiftTest.h"; path = "Tests/SentryTests/Helper/SentryProfiler+SwiftTest.h"; sourceTree = SOURCE_ROOT; }; 844EDCE32947DC3100C86F34 /* SentryNSTimerFactory.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryNSTimerFactory.h; path = include/SentryNSTimerFactory.h; sourceTree = ""; }; 844EDCE42947DC3100C86F34 /* SentryNSTimerFactory.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryNSTimerFactory.m; sourceTree = ""; }; 844EDCE72947DCD700C86F34 /* TestSentryNSTimerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestSentryNSTimerFactory.swift; sourceTree = ""; }; @@ -3086,37 +3091,40 @@ 8405A517279906EF001B38A1 /* Profiling */ = { isa = PBXGroup; children = ( - 84354E0F29BF944900CDBB8B /* SentryProfileTimeseries.h */, - 84354E1029BF944900CDBB8B /* SentryProfileTimeseries.mm */, 03F84D1327DD414C008FE43F /* SentryAsyncSafeLogging.h */, - 03F84D1227DD414C008FE43F /* SentryBacktrace.hpp */, 03F84D3127DD4191008FE43F /* SentryBacktrace.cpp */, + 03F84D1227DD414C008FE43F /* SentryBacktrace.hpp */, 03F84D1827DD414C008FE43F /* SentryCompiler.h */, 03F84D1C27DD414C008FE43F /* SentryCPU.h */, - 03F84D1B27DD414C008FE43F /* SentryMachLogging.hpp */, 03F84D2C27DD4191008FE43F /* SentryMachLogging.cpp */, - 03F84D1127DD414C008FE43F /* SentryProfiler.h */, + 03F84D1B27DD414C008FE43F /* SentryMachLogging.hpp */, 844EDD6B2949387000C86F34 /* SentryMetricProfiler.h */, 8454CF8B293EAF9A006AC140 /* SentryMetricProfiler.mm */, - 0354A22A2A134D9C003C3A04 /* SentryProfilerState.h */, - 84A888FC28D9B11700C51DFD /* SentryProfiler+Test.h */, + 03F84D1127DD414C008FE43F /* SentryProfiler.h */, 03F84D2B27DD4191008FE43F /* SentryProfiler.mm */, + 84A888FC28D9B11700C51DFD /* SentryProfiler+Test.h */, + 0354A22A2A134D9C003C3A04 /* SentryProfilerState.h */, + 84281C422A578E5600EE88F2 /* SentryProfilerState.mm */, + 0356A56E288B4612008BF593 /* SentryProfilesSampler.h */, + 0356A56F288B4612008BF593 /* SentryProfilesSampler.m */, + 84354E0F29BF944900CDBB8B /* SentryProfileTimeseries.h */, + 84354E1029BF944900CDBB8B /* SentryProfileTimeseries.mm */, 03BCC38D27E2A377003232C7 /* SentryProfilingConditionals.h */, 03F84D2927DD416B008FE43F /* SentryProfilingLogging.hpp */, 03F84D2F27DD4191008FE43F /* SentryProfilingLogging.mm */, - 03F84D1527DD414C008FE43F /* SentrySamplingProfiler.hpp */, + 84281C442A57905700EE88F2 /* SentrySample.h */, + 84281C452A57905700EE88F2 /* SentrySample.m */, 03F84D3027DD4191008FE43F /* SentrySamplingProfiler.cpp */, - 03F84D1727DD414C008FE43F /* SentryThreadHandle.hpp */, - 03F84D2E27DD4191008FE43F /* SentryThreadHandle.cpp */, - 03F84D1A27DD414C008FE43F /* SentryThreadMetadataCache.hpp */, - 03F84D2D27DD4191008FE43F /* SentryThreadMetadataCache.cpp */, + 03F84D1527DD414C008FE43F /* SentrySamplingProfiler.hpp */, 03F84D1427DD414C008FE43F /* SentryStackBounds.hpp */, 03F84D1627DD414C008FE43F /* SentryStackFrame.hpp */, + 03F84D2E27DD4191008FE43F /* SentryThreadHandle.cpp */, + 03F84D1727DD414C008FE43F /* SentryThreadHandle.hpp */, + 03F84D2D27DD4191008FE43F /* SentryThreadMetadataCache.cpp */, + 03F84D1A27DD414C008FE43F /* SentryThreadMetadataCache.hpp */, 03F84D1927DD414C008FE43F /* SentryThreadState.hpp */, 03BCC38927E1BF49003232C7 /* SentryTime.h */, 03BCC38B27E1C01A003232C7 /* SentryTime.mm */, - 0356A56E288B4612008BF593 /* SentryProfilesSampler.h */, - 0356A56F288B4612008BF593 /* SentryProfilesSampler.m */, ); path = Profiling; sourceTree = ""; @@ -3564,6 +3572,7 @@ D8603DD8284F894C000E1227 /* SentryBaggage.h in Headers */, 03F84D2127DD414C008FE43F /* SentrySamplingProfiler.hpp in Headers */, 84AC61D229F7541E009EEF61 /* SentryDispatchSourceWrapper.h in Headers */, + 84281C462A57905700EE88F2 /* SentrySample.h in Headers */, 63FE712B20DA4C1100CDBAE8 /* SentryCrashStackCursor.h in Headers */, D8C67E9C28000E24007E326E /* SentryScreenshot.h in Headers */, 7BA61CBF247CEA8100C130A8 /* SentryFormatter.h in Headers */, @@ -4091,6 +4100,7 @@ 7BB65501253DC1B500887E87 /* SentryUserFeedback.m in Sources */, 7D5C441A237C2E1F00DAB0A3 /* SentrySDK.m in Sources */, 7D65260E237F649E00113EA2 /* SentryScope.m in Sources */, + 84281C472A57905700EE88F2 /* SentrySample.m in Sources */, 84AC61D329F7541E009EEF61 /* SentryDispatchSourceWrapper.m in Sources */, 63FE712D20DA4C1100CDBAE8 /* SentryCrashJSONCodecObjC.m in Sources */, 7BBD18932449BEDD00427C76 /* SentryDefaultRateLimits.m in Sources */, @@ -4131,6 +4141,7 @@ 7BC9A20428F4166D001E7C4C /* SentryMeasurementValue.m in Sources */, D859696B27BECD8F0036A46E /* SentryCoreDataTrackingIntegration.m in Sources */, 7BD86EC7264A641D005439DB /* SentrySysctl.m in Sources */, + 84281C432A578E5600EE88F2 /* SentryProfilerState.mm in Sources */, D859697327BECDD20036A46E /* SentryCoreDataSwizzling.m in Sources */, 639889BD1EDED18400EA7442 /* SentrySwizzle.m in Sources */, D8ABB0BC29264275005D1E24 /* Sentry.swift in Sources */, diff --git a/Sources/Sentry/Profiling/SentryProfilerState.mm b/Sources/Sentry/Profiling/SentryProfilerState.mm new file mode 100644 index 00000000000..b68619ff491 --- /dev/null +++ b/Sources/Sentry/Profiling/SentryProfilerState.mm @@ -0,0 +1,186 @@ +#import "SentryProfilerState.h" +#if SENTRY_TARGET_PROFILING_SUPPORTED +# import "SentryBacktrace.hpp" +# import "SentryFormatter.h" +# import "SentryProfileTimeseries.h" +# import "SentrySample.h" +# import + +# if defined(DEBUG) +# include +# endif + +using namespace sentry::profiling; + +namespace { +NSString * +parseBacktraceSymbolsFunctionName(const char *symbol) +{ + static NSRegularExpression *regex = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + regex = [NSRegularExpression + regularExpressionWithPattern:@"\\d+\\s+\\S+\\s+0[xX][0-9a-fA-F]+\\s+(.+)\\s+\\+\\s+\\d+" + options:0 + error:nil]; + }); + const auto symbolNSStr = [NSString stringWithUTF8String:symbol]; + const auto match = [regex firstMatchInString:symbolNSStr + options:0 + range:NSMakeRange(0, [symbolNSStr length])]; + if (match == nil) { + return symbolNSStr; + } + return [symbolNSStr substringWithRange:[match rangeAtIndex:1]]; +} +} // namespace + +@implementation SentryProfilingMutableState + +- (instancetype)init +{ + if (self = [super init]) { + _samples = [NSMutableArray array]; + _stacks = [NSMutableArray *> array]; + _frames = [NSMutableArray *> array]; + _threadMetadata = [NSMutableDictionary dictionary]; + _queueMetadata = [NSMutableDictionary dictionary]; + _frameIndexLookup = [NSMutableDictionary dictionary]; + _stackIndexLookup = [NSMutableDictionary dictionary]; + } + return self; +} + +@end + +@implementation SentryProfilingState { + SentryProfilingMutableState *_mutableState; + std::mutex _lock; +} + +- (instancetype)init +{ + if (self = [super init]) { + _mutableState = [[SentryProfilingMutableState alloc] init]; + } + return self; +} + +- (void)mutate:(void (^)(SentryProfilingMutableState *))block +{ + NSParameterAssert(block); + std::lock_guard l(_lock); + block(_mutableState); +} + +- (void)appendBacktrace:(const Backtrace &)backtrace +{ + [self mutate:^(SentryProfilingMutableState *state) { + const auto threadID = sentry_stringForUInt64(backtrace.threadMetadata.threadID); + + NSString *queueAddress = nil; + if (backtrace.queueMetadata.address != 0) { + queueAddress = sentry_formatHexAddressUInt64(backtrace.queueMetadata.address); + } + NSMutableDictionary *metadata = state.threadMetadata[threadID]; + if (metadata == nil) { + metadata = [NSMutableDictionary dictionary]; + state.threadMetadata[threadID] = metadata; + } + if (!backtrace.threadMetadata.name.empty() && metadata[@"name"] == nil) { + metadata[@"name"] = + [NSString stringWithUTF8String:backtrace.threadMetadata.name.c_str()]; + } + if (backtrace.threadMetadata.priority != -1 && metadata[@"priority"] == nil) { + metadata[@"priority"] = @(backtrace.threadMetadata.priority); + } + if (queueAddress != nil && state.queueMetadata[queueAddress] == nil + && backtrace.queueMetadata.label != nullptr) { + NSString *const labelNSStr = + [NSString stringWithUTF8String:backtrace.queueMetadata.label->c_str()]; + // -[NSString stringWithUTF8String:] can return `nil` for malformed string data + if (labelNSStr != nil) { + state.queueMetadata[queueAddress] = @ { @"label" : labelNSStr }; + } + } +# if defined(DEBUG) + const auto symbols + = backtrace_symbols(reinterpret_cast(backtrace.addresses.data()), + static_cast(backtrace.addresses.size())); +# endif + + const auto stack = [NSMutableArray array]; + for (std::vector::size_type backtraceAddressIdx = 0; + backtraceAddressIdx < backtrace.addresses.size(); backtraceAddressIdx++) { + const auto instructionAddress + = sentry_formatHexAddressUInt64(backtrace.addresses[backtraceAddressIdx]); + + const auto frameIndex = state.frameIndexLookup[instructionAddress]; + if (frameIndex == nil) { + const auto frame = [NSMutableDictionary dictionary]; + frame[@"instruction_addr"] = instructionAddress; +# if defined(DEBUG) + frame[@"function"] + = parseBacktraceSymbolsFunctionName(symbols[backtraceAddressIdx]); +# endif + const auto newFrameIndex = @(state.frames.count); + [stack addObject:newFrameIndex]; + state.frameIndexLookup[instructionAddress] = newFrameIndex; + [state.frames addObject:frame]; + } else { + [stack addObject:frameIndex]; + } + } + + const auto sample = [[SentrySample alloc] init]; + sample.absoluteTimestamp = backtrace.absoluteTimestamp; + sample.threadID = backtrace.threadMetadata.threadID; + if (queueAddress != nil) { + sample.queueAddress = queueAddress; + } + + const auto stackKey = [stack componentsJoinedByString:@"|"]; + const auto stackIndex = state.stackIndexLookup[stackKey]; + if (stackIndex) { + sample.stackIndex = stackIndex; + } else { + const auto nextStackIndex = @(state.stacks.count); + sample.stackIndex = nextStackIndex; + state.stackIndexLookup[stackKey] = nextStackIndex; + [state.stacks addObject:stack]; + } + + [state.samples addObject:sample]; + }]; +} + +- (NSDictionary *)copyProfilingData +{ + std::lock_guard l(_lock); + + NSMutableArray *const samples = [_mutableState.samples copy]; + NSMutableArray *> *const stacks = [_mutableState.stacks copy]; + NSMutableArray *> *const frames = [_mutableState.frames copy]; + NSMutableDictionary *const queueMetadata = + [_mutableState.queueMetadata copy]; + + // thread metadata contains a mutable substructure, so it's not enough to perform a copy of + // the top-level dictionary, we need to go deeper to copy the mutable subdictionaries + const auto threadMetadata = [NSMutableDictionary dictionary]; + [_mutableState.threadMetadata enumerateKeysAndObjectsUsingBlock:^(NSString *_Nonnull key, + NSDictionary *_Nonnull obj, BOOL *_Nonnull stop) { threadMetadata[key] = [obj copy]; }]; + + return @{ + @"profile" : @ { + @"samples" : samples, + @"stacks" : stacks, + @"frames" : frames, + @"thread_metadata" : threadMetadata, + @"queue_metadata" : queueMetadata + } + }; +} + +@end + +#endif // SENTRY_TARGET_PROFILING_SUPPORTED diff --git a/Sources/Sentry/Profiling/SentrySample.m b/Sources/Sentry/Profiling/SentrySample.m new file mode 100644 index 00000000000..58dd48b575d --- /dev/null +++ b/Sources/Sentry/Profiling/SentrySample.m @@ -0,0 +1,4 @@ +#import "SentrySample.h" + +@implementation SentrySample +@end diff --git a/Sources/Sentry/SentryProfileTimeseries.mm b/Sources/Sentry/SentryProfileTimeseries.mm index bd1d01c83bc..b79024a4a22 100644 --- a/Sources/Sentry/SentryProfileTimeseries.mm +++ b/Sources/Sentry/SentryProfileTimeseries.mm @@ -5,6 +5,7 @@ # import "SentryEvent+Private.h" # import "SentryInternalDefines.h" # import "SentryLog.h" +# import "SentrySample.h" # import "SentryTransaction.h" /** @@ -83,7 +84,4 @@ return [samples objectsAtIndexes:indices]; } -@implementation SentrySample -@end - #endif // SENTRY_TARGET_PROFILING_SUPPORTED diff --git a/Sources/Sentry/SentryProfiler.mm b/Sources/Sentry/SentryProfiler.mm index abd8cdecb6b..8ca5a54c320 100644 --- a/Sources/Sentry/SentryProfiler.mm +++ b/Sources/Sentry/SentryProfiler.mm @@ -1,9 +1,7 @@ #import "SentryProfiler+Test.h" -#import "SentryProfilerState.h" #if SENTRY_TARGET_PROFILING_SUPPORTED # import "NSDate+SentryExtras.h" -# import "SentryBacktrace.hpp" # import "SentryClient+Private.h" # import "SentryCurrentDate.h" # import "SentryDebugImageProvider.h" @@ -26,6 +24,8 @@ # import "SentryNSProcessInfoWrapper.h" # import "SentryNSTimerFactory.h" # import "SentryProfileTimeseries.h" +# import "SentryProfilerState.h" +# import "SentrySample.h" # import "SentrySamplingProfiler.hpp" # import "SentryScope+Private.h" # import "SentryScreenFrames.h" @@ -38,10 +38,6 @@ # import "SentryTransaction.h" # import "SentryTransactionContext+Private.h" -# if defined(DEBUG) -# include -# endif - # import # import @@ -58,27 +54,6 @@ using namespace sentry::profiling; -NSString * -parseBacktraceSymbolsFunctionName(const char *symbol) -{ - static NSRegularExpression *regex = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - regex = [NSRegularExpression - regularExpressionWithPattern:@"\\d+\\s+\\S+\\s+0[xX][0-9a-fA-F]+\\s+(.+)\\s+\\+\\s+\\d+" - options:0 - error:nil]; - }); - const auto symbolNSStr = [NSString stringWithUTF8String:symbol]; - const auto match = [regex firstMatchInString:symbolNSStr - options:0 - range:NSMakeRange(0, [symbolNSStr length])]; - if (match == nil) { - return symbolNSStr; - } - return [symbolNSStr substringWithRange:[match rangeAtIndex:1]]; -} - std::mutex _gProfilerLock; SentryProfiler *_Nullable _gCurrentProfiler; @@ -292,154 +267,6 @@ return payload; } -@implementation SentryProfilingMutableState - -- (instancetype)init -{ - if (self = [super init]) { - _samples = [NSMutableArray array]; - _stacks = [NSMutableArray *> array]; - _frames = [NSMutableArray *> array]; - _threadMetadata = [NSMutableDictionary dictionary]; - _queueMetadata = [NSMutableDictionary dictionary]; - _frameIndexLookup = [NSMutableDictionary dictionary]; - _stackIndexLookup = [NSMutableDictionary dictionary]; - } - return self; -} - -@end - -@implementation SentryProfilingState { - SentryProfilingMutableState *_mutableState; - std::mutex _lock; -} - -- (instancetype)init -{ - if (self = [super init]) { - _mutableState = [[SentryProfilingMutableState alloc] init]; - } - return self; -} - -- (void)mutate:(void (^)(SentryProfilingMutableState *))block -{ - NSParameterAssert(block); - std::lock_guard l(_lock); - block(_mutableState); -} - -- (void)appendBacktrace:(const Backtrace &)backtrace -{ - [self mutate:^(SentryProfilingMutableState *state) { - const auto threadID = sentry_stringForUInt64(backtrace.threadMetadata.threadID); - - NSString *queueAddress = nil; - if (backtrace.queueMetadata.address != 0) { - queueAddress = sentry_formatHexAddressUInt64(backtrace.queueMetadata.address); - } - NSMutableDictionary *metadata = state.threadMetadata[threadID]; - if (metadata == nil) { - metadata = [NSMutableDictionary dictionary]; - state.threadMetadata[threadID] = metadata; - } - if (!backtrace.threadMetadata.name.empty() && metadata[@"name"] == nil) { - metadata[@"name"] = - [NSString stringWithUTF8String:backtrace.threadMetadata.name.c_str()]; - } - if (backtrace.threadMetadata.priority != -1 && metadata[@"priority"] == nil) { - metadata[@"priority"] = @(backtrace.threadMetadata.priority); - } - if (queueAddress != nil && state.queueMetadata[queueAddress] == nil - && backtrace.queueMetadata.label != nullptr) { - NSString *const labelNSStr = - [NSString stringWithUTF8String:backtrace.queueMetadata.label->c_str()]; - // -[NSString stringWithUTF8String:] can return `nil` for malformed string data - if (labelNSStr != nil) { - state.queueMetadata[queueAddress] = @ { @"label" : labelNSStr }; - } - } -# if defined(DEBUG) - const auto symbols - = backtrace_symbols(reinterpret_cast(backtrace.addresses.data()), - static_cast(backtrace.addresses.size())); -# endif - - const auto stack = [NSMutableArray array]; - for (std::vector::size_type backtraceAddressIdx = 0; - backtraceAddressIdx < backtrace.addresses.size(); backtraceAddressIdx++) { - const auto instructionAddress - = sentry_formatHexAddressUInt64(backtrace.addresses[backtraceAddressIdx]); - - const auto frameIndex = state.frameIndexLookup[instructionAddress]; - if (frameIndex == nil) { - const auto frame = [NSMutableDictionary dictionary]; - frame[@"instruction_addr"] = instructionAddress; -# if defined(DEBUG) - frame[@"function"] - = parseBacktraceSymbolsFunctionName(symbols[backtraceAddressIdx]); -# endif - const auto newFrameIndex = @(state.frames.count); - [stack addObject:newFrameIndex]; - state.frameIndexLookup[instructionAddress] = newFrameIndex; - [state.frames addObject:frame]; - } else { - [stack addObject:frameIndex]; - } - } - - const auto sample = [[SentrySample alloc] init]; - sample.absoluteTimestamp = backtrace.absoluteTimestamp; - sample.threadID = backtrace.threadMetadata.threadID; - if (queueAddress != nil) { - sample.queueAddress = queueAddress; - } - - const auto stackKey = [stack componentsJoinedByString:@"|"]; - const auto stackIndex = state.stackIndexLookup[stackKey]; - if (stackIndex) { - sample.stackIndex = stackIndex; - } else { - const auto nextStackIndex = @(state.stacks.count); - sample.stackIndex = nextStackIndex; - state.stackIndexLookup[stackKey] = nextStackIndex; - [state.stacks addObject:stack]; - } - - [state.samples addObject:sample]; - }]; -} - -- (NSDictionary *)copyProfilingData -{ - std::lock_guard l(_lock); - - NSMutableArray *const samples = [_mutableState.samples copy]; - NSMutableArray *> *const stacks = [_mutableState.stacks copy]; - NSMutableArray *> *const frames = [_mutableState.frames copy]; - NSMutableDictionary *const queueMetadata = - [_mutableState.queueMetadata copy]; - - // thread metadata contains a mutable substructure, so it's not enough to perform a copy of - // the top-level dictionary, we need to go deeper to copy the mutable subdictionaries - const auto threadMetadata = [NSMutableDictionary dictionary]; - [_mutableState.threadMetadata enumerateKeysAndObjectsUsingBlock:^(NSString *_Nonnull key, - NSDictionary *_Nonnull obj, BOOL *_Nonnull stop) { threadMetadata[key] = [obj copy]; }]; - - return @{ - @"profile" : @ { - @"samples" : samples, - @"stacks" : stacks, - @"frames" : frames, - @"thread_metadata" : threadMetadata, - @"queue_metadata" : queueMetadata - } - }; -} - -@end - @implementation SentryProfiler { SentryProfilingState *_state; std::shared_ptr _profiler; diff --git a/Sources/Sentry/include/SentryProfileTimeseries.h b/Sources/Sentry/include/SentryProfileTimeseries.h index a8d2203df5f..342cfce0a94 100644 --- a/Sources/Sentry/include/SentryProfileTimeseries.h +++ b/Sources/Sentry/include/SentryProfileTimeseries.h @@ -4,20 +4,12 @@ # import "SentryDefines.h" # import -# import +@class SentrySample; @class SentryTransaction; NS_ASSUME_NONNULL_BEGIN -/** A storage class to hold the data associated with a single profiler sample. */ -@interface SentrySample : NSObject -@property (nonatomic, assign) uint64_t absoluteTimestamp; -@property (nonatomic, strong) NSNumber *stackIndex; -@property (nonatomic, assign) uint64_t threadID; -@property (nullable, nonatomic, copy) NSString *queueAddress; -@end - NSArray *_Nullable slicedProfileSamples( NSArray *samples, SentryTransaction *transaction); diff --git a/Sources/Sentry/include/SentrySample.h b/Sources/Sentry/include/SentrySample.h new file mode 100644 index 00000000000..4c0852c388f --- /dev/null +++ b/Sources/Sentry/include/SentrySample.h @@ -0,0 +1,13 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +/** A storage class to hold the data associated with a single profiler sample. */ +@interface SentrySample : NSObject +@property (nonatomic, assign) uint64_t absoluteTimestamp; +@property (nonatomic, strong) NSNumber *stackIndex; +@property (nonatomic, assign) uint64_t threadID; +@property (nullable, nonatomic, copy) NSString *queueAddress; +@end + +NS_ASSUME_NONNULL_END From c88d79b66ff19c5fbf07bcbcb20a8569b3dd6873 Mon Sep 17 00:00:00 2001 From: Andrew McKnight Date: Thu, 6 Jul 2023 16:24:07 -0800 Subject: [PATCH 02/15] move function prototype --- Sources/Sentry/Profiling/SentryProfilerState.mm | 2 -- Sources/Sentry/include/SentryProfiler.h | 13 ------------- Sources/Sentry/include/SentryProfilerState.h | 13 +++++++++++++ 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/Sources/Sentry/Profiling/SentryProfilerState.mm b/Sources/Sentry/Profiling/SentryProfilerState.mm index b68619ff491..b80a3c307a0 100644 --- a/Sources/Sentry/Profiling/SentryProfilerState.mm +++ b/Sources/Sentry/Profiling/SentryProfilerState.mm @@ -12,7 +12,6 @@ using namespace sentry::profiling; -namespace { NSString * parseBacktraceSymbolsFunctionName(const char *symbol) { @@ -33,7 +32,6 @@ } return [symbolNSStr substringWithRange:[match rangeAtIndex:1]]; } -} // namespace @implementation SentryProfilingMutableState diff --git a/Sources/Sentry/include/SentryProfiler.h b/Sources/Sentry/include/SentryProfiler.h index 2fa99f8331c..a7e9c01eb4f 100644 --- a/Sources/Sentry/include/SentryProfiler.h +++ b/Sources/Sentry/include/SentryProfiler.h @@ -28,19 +28,6 @@ SENTRY_EXTERN NSString *const kSentryProfilerSerializationKeyFrameRates; SENTRY_EXTERN_C_BEGIN -/** - * Parses a symbol that is returned from @c backtrace_symbols() which encodes information - * like the frame index, image name, function name, and offset in a single string. - * @discussion For the input: - * @code - * 2 UIKitCore 0x00000001850d97ac -[UIFieldEditor _fullContentInsetsFromFonts] + 160 - * @endcode - * This function would return: - * @code -[UIFieldEditor _fullContentInsetsFromFonts] @endcode - * @note If the format does not match the expected format, this returns the input string. - */ -NSString *parseBacktraceSymbolsFunctionName(const char *symbol); - NSString *profilerTruncationReasonName(SentryProfilerTruncationReason reason); SENTRY_EXTERN_C_END diff --git a/Sources/Sentry/include/SentryProfilerState.h b/Sources/Sentry/include/SentryProfilerState.h index 8ba1a27e6f5..c4c1eb62cca 100644 --- a/Sources/Sentry/include/SentryProfilerState.h +++ b/Sources/Sentry/include/SentryProfilerState.h @@ -6,6 +6,19 @@ NS_ASSUME_NONNULL_BEGIN +/** + * Parses a symbol that is returned from @c backtrace_symbols() which encodes information + * like the frame index, image name, function name, and offset in a single string. + * @discussion For the input: + * @code + * 2 UIKitCore 0x00000001850d97ac -[UIFieldEditor _fullContentInsetsFromFonts] + 160 + * @endcode + * This function would return: + * @code -[UIFieldEditor _fullContentInsetsFromFonts] @endcode + * @note If the format does not match the expected format, this returns the input string. + */ +NSString *parseBacktraceSymbolsFunctionName(const char *symbol); + @class SentrySample; @interface SentryProfilingMutableState : NSObject From 4c2017faadfd45fad01b7b324e2e68726d84819b Mon Sep 17 00:00:00 2001 From: Andrew McKnight Date: Thu, 6 Jul 2023 20:40:41 -0800 Subject: [PATCH 03/15] rename Profiling -> Profiler --- Sources/Sentry/Profiling/SentryProfilerState.mm | 12 ++++++------ Sources/Sentry/SentryProfiler.mm | 4 ++-- Sources/Sentry/include/SentryProfilerState.h | 6 +++--- Tests/SentryProfilerTests/SentryProfilerTests.mm | 12 ++++++------ 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Sources/Sentry/Profiling/SentryProfilerState.mm b/Sources/Sentry/Profiling/SentryProfilerState.mm index b80a3c307a0..10b47e40a64 100644 --- a/Sources/Sentry/Profiling/SentryProfilerState.mm +++ b/Sources/Sentry/Profiling/SentryProfilerState.mm @@ -33,7 +33,7 @@ return [symbolNSStr substringWithRange:[match rangeAtIndex:1]]; } -@implementation SentryProfilingMutableState +@implementation SentryProfilerMutableState - (instancetype)init { @@ -51,20 +51,20 @@ - (instancetype)init @end -@implementation SentryProfilingState { - SentryProfilingMutableState *_mutableState; +@implementation SentryProfilerState { + SentryProfilerMutableState *_mutableState; std::mutex _lock; } - (instancetype)init { if (self = [super init]) { - _mutableState = [[SentryProfilingMutableState alloc] init]; + _mutableState = [[SentryProfilerMutableState alloc] init]; } return self; } -- (void)mutate:(void (^)(SentryProfilingMutableState *))block +- (void)mutate:(void (^)(SentryProfilerMutableState *))block { NSParameterAssert(block); std::lock_guard l(_lock); @@ -73,7 +73,7 @@ - (void)mutate:(void (^)(SentryProfilingMutableState *))block - (void)appendBacktrace:(const Backtrace &)backtrace { - [self mutate:^(SentryProfilingMutableState *state) { + [self mutate:^(SentryProfilerMutableState *state) { const auto threadID = sentry_stringForUInt64(backtrace.threadMetadata.threadID); NSString *queueAddress = nil; diff --git a/Sources/Sentry/SentryProfiler.mm b/Sources/Sentry/SentryProfiler.mm index 8ca5a54c320..29cda4d439e 100644 --- a/Sources/Sentry/SentryProfiler.mm +++ b/Sources/Sentry/SentryProfiler.mm @@ -268,7 +268,7 @@ } @implementation SentryProfiler { - SentryProfilingState *_state; + SentryProfilerState *_state; std::shared_ptr _profiler; SentryMetricProfiler *_metricProfiler; SentryDebugImageProvider *_debugImageProvider; @@ -469,7 +469,7 @@ - (void)start SENTRY_LOG_DEBUG(@"Starting profiler."); - SentryProfilingState *const state = [[SentryProfilingState alloc] init]; + SentryProfilerState *const state = [[SentryProfilerState alloc] init]; _state = state; _profiler = std::make_shared( [state](auto &backtrace) { diff --git a/Sources/Sentry/include/SentryProfilerState.h b/Sources/Sentry/include/SentryProfilerState.h index c4c1eb62cca..d7c12972d33 100644 --- a/Sources/Sentry/include/SentryProfilerState.h +++ b/Sources/Sentry/include/SentryProfilerState.h @@ -21,7 +21,7 @@ NSString *parseBacktraceSymbolsFunctionName(const char *symbol); @class SentrySample; -@interface SentryProfilingMutableState : NSObject +@interface SentryProfilerMutableState : NSObject @property (nonatomic, strong, readonly) NSMutableArray *samples; @property (nonatomic, strong, readonly) NSMutableArray *> *stacks; @property (nonatomic, strong, readonly) NSMutableArray *> *frames; @@ -70,9 +70,9 @@ NSString *parseBacktraceSymbolsFunctionName(const char *symbol); NSMutableDictionary *stackIndexLookup; @end -@interface SentryProfilingState : NSObject +@interface SentryProfilerState : NSObject // All functions are safe to call from multiple threads concurrently -- (void)mutate:(void (^)(SentryProfilingMutableState *))block; +- (void)mutate:(void (^)(SentryProfilerMutableState *))block; - (void)appendBacktrace:(const sentry::profiling::Backtrace &)backtrace; - (NSDictionary *)copyProfilingData; @end diff --git a/Tests/SentryProfilerTests/SentryProfilerTests.mm b/Tests/SentryProfilerTests/SentryProfilerTests.mm index 2cc4005e3d5..1d898ed4f0f 100644 --- a/Tests/SentryProfilerTests/SentryProfilerTests.mm +++ b/Tests/SentryProfilerTests/SentryProfilerTests.mm @@ -62,7 +62,7 @@ - (void)testProfilerCanBeInitializedOffMainThread - (void)testProfilerMutationDuringSlicing { - SentryProfilingState *state = [[SentryProfilingState alloc] init]; + SentryProfilerState *state = [[SentryProfilerState alloc] init]; // generate a large timeseries of simulated data const auto threads = 5; @@ -119,7 +119,7 @@ - (void)testProfilerMutationDuringSlicing sliceExpectation.expectedFulfillmentCount = operations; void (^sliceBlock)(void) = ^(void) { - [state mutate:^(SentryProfilingMutableState *mutableState) { + [state mutate:^(SentryProfilerMutableState *mutableState) { __unused const auto slice = slicedProfileSamples(mutableState.samples, transaction); [sliceExpectation fulfill]; }]; @@ -148,7 +148,7 @@ - (void)testProfilerMutationDuringSlicing void (^mutateBlock)(void) = ^(void) { [state mutate:^( - __unused SentryProfilingMutableState *mutableState) { [mutateExpectation fulfill]; }]; + __unused SentryProfilerMutableState *mutableState) { [mutateExpectation fulfill]; }]; }; const auto sliceOperations = [[NSOperationQueue alloc] init]; // concurrent queue @@ -177,7 +177,7 @@ - (void)testProfilerMutationDuringSlicing */ - (void)testProfilerMutationDuringSerialization { - SentryProfilingState *state = [[SentryProfilingState alloc] init]; + SentryProfilerState *state = [[SentryProfilerState alloc] init]; // initialize the data structures with some simulated data { ThreadMetadata threadMetadata; @@ -289,7 +289,7 @@ - (void)testProfilerMutationDuringSerialization - (void)testProfilerPayload { - SentryProfilingState *state = [[SentryProfilingState alloc] init]; + SentryProfilerState *state = [[SentryProfilerState alloc] init]; // record an initial backtrace @@ -337,7 +337,7 @@ - (void)testProfilerPayload [state appendBacktrace:backtrace2]; - [state mutate:^(SentryProfilingMutableState *mutableState) { + [state mutate:^(SentryProfilerMutableState *mutableState) { XCTAssertEqual(mutableState.frames.count, 5UL); const auto expectedOrdered = @[ @"0x0000000000000123", @"0x0000000000000456", @"0x0000000000000789", From f7bfb88f39282cb3bb96702901a80305d414eb40 Mon Sep 17 00:00:00 2001 From: Andrew McKnight Date: Thu, 6 Jul 2023 21:30:46 -0800 Subject: [PATCH 04/15] fix path --- Sentry.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sentry.xcodeproj/project.pbxproj b/Sentry.xcodeproj/project.pbxproj index 364df1cd441..b2aed0f8914 100644 --- a/Sentry.xcodeproj/project.pbxproj +++ b/Sentry.xcodeproj/project.pbxproj @@ -1524,7 +1524,7 @@ 7DC831082398283C0043DD9A /* SentryCrashIntegration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryCrashIntegration.h; path = include/SentryCrashIntegration.h; sourceTree = ""; }; 7DC831092398283C0043DD9A /* SentryCrashIntegration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryCrashIntegration.m; sourceTree = ""; }; 8419C0C328C1889D001C8259 /* SentryProfilerSwiftTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryProfilerSwiftTests.swift; sourceTree = ""; }; - 84281C422A578E5600EE88F2 /* SentryProfilerState.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = SentryProfilerState.mm; path = "/Users/andrewmcknight/Code/organization/getsentry/repos/public/sentry-cocoa/Sources/Sentry/Profiling/SentryProfilerState.mm"; sourceTree = ""; }; + 84281C422A578E5600EE88F2 /* SentryProfilerState.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SentryProfilerState.mm; sourceTree = ""; }; 84281C442A57905700EE88F2 /* SentrySample.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentrySample.h; path = ../include/SentrySample.h; sourceTree = ""; }; 84281C452A57905700EE88F2 /* SentrySample.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentrySample.m; sourceTree = ""; }; 8431EE5A29ADB8EA00D8DC56 /* SentryTimeTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryTimeTests.m; sourceTree = ""; }; From d1d8ac3a957b6c81b940f24fa79e9c14e79e2536 Mon Sep 17 00:00:00 2001 From: Andrew McKnight Date: Thu, 6 Jul 2023 20:38:41 -0800 Subject: [PATCH 05/15] wip extracting profile backtrace mocking from objc tests so it can be used from swift tests --- Sentry.xcodeproj/project.pbxproj | 189 ++++++++++++++++++ .../xcschemes/SentryTestUtils.xcscheme | 66 ++++++ SentryTestUtils/SentryProfilerMocks.h | 16 ++ SentryTestUtils/SentryProfilerMocks.mm | 28 +++ .../SentryProfilerMocksSwiftCompatible.h | 15 ++ .../SentryProfilerMocksSwiftCompatible.mm | 27 +++ .../SentryProfilerTests.mm | 128 ++---------- 7 files changed, 361 insertions(+), 108 deletions(-) create mode 100644 Sentry.xcodeproj/xcshareddata/xcschemes/SentryTestUtils.xcscheme create mode 100644 SentryTestUtils/SentryProfilerMocks.h create mode 100644 SentryTestUtils/SentryProfilerMocks.mm create mode 100644 SentryTestUtils/SentryProfilerMocksSwiftCompatible.h create mode 100644 SentryTestUtils/SentryProfilerMocksSwiftCompatible.mm diff --git a/Sentry.xcodeproj/project.pbxproj b/Sentry.xcodeproj/project.pbxproj index b2aed0f8914..ad3aa29faa3 100644 --- a/Sentry.xcodeproj/project.pbxproj +++ b/Sentry.xcodeproj/project.pbxproj @@ -604,6 +604,9 @@ 84281C432A578E5600EE88F2 /* SentryProfilerState.mm in Sources */ = {isa = PBXBuildFile; fileRef = 84281C422A578E5600EE88F2 /* SentryProfilerState.mm */; }; 84281C462A57905700EE88F2 /* SentrySample.h in Headers */ = {isa = PBXBuildFile; fileRef = 84281C442A57905700EE88F2 /* SentrySample.h */; }; 84281C472A57905700EE88F2 /* SentrySample.m in Sources */ = {isa = PBXBuildFile; fileRef = 84281C452A57905700EE88F2 /* SentrySample.m */; }; + 84281C612A579C8800EE88F2 /* libSentryTestUtilsObjc.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 84281C542A579C2B00EE88F2 /* libSentryTestUtilsObjc.a */; }; + 84281C622A579D0700EE88F2 /* SentryProfilerMocksSwiftCompatible.mm in Sources */ = {isa = PBXBuildFile; fileRef = 84281C4D2A579A0C00EE88F2 /* SentryProfilerMocksSwiftCompatible.mm */; }; + 84281C632A579D0700EE88F2 /* SentryProfilerMocks.mm in Sources */ = {isa = PBXBuildFile; fileRef = 84281C492A57933600EE88F2 /* SentryProfilerMocks.mm */; }; 8431EE5B29ADB8EA00D8DC56 /* SentryTimeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8431EE5A29ADB8EA00D8DC56 /* SentryTimeTests.m */; }; 8431EFD129B27B1100D8DC56 /* Sentry.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 63AA759B1EB8AEF500D153DE /* Sentry.framework */; settings = {ATTRIBUTES = (Required, ); }; }; 8431EFD329B27B1100D8DC56 /* Resources in Resources */ = {isa = PBXBuildFile; fileRef = 630C01951EC341D600C52CEF /* Resources */; }; @@ -875,6 +878,18 @@ }; /* End PBXContainerItemProxy section */ +/* Begin PBXCopyFilesBuildPhase section */ + 84281C522A579C2B00EE88F2 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "include/$(PRODUCT_NAME)"; + dstSubfolderSpec = 16; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ 0354A22A2A134D9C003C3A04 /* SentryProfilerState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SentryProfilerState.h; path = Sources/Sentry/include/SentryProfilerState.h; sourceTree = SOURCE_ROOT; }; 0356A56E288B4612008BF593 /* SentryProfilesSampler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryProfilesSampler.h; path = Sources/Sentry/include/SentryProfilesSampler.h; sourceTree = SOURCE_ROOT; }; @@ -1527,6 +1542,11 @@ 84281C422A578E5600EE88F2 /* SentryProfilerState.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SentryProfilerState.mm; sourceTree = ""; }; 84281C442A57905700EE88F2 /* SentrySample.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentrySample.h; path = ../include/SentrySample.h; sourceTree = ""; }; 84281C452A57905700EE88F2 /* SentrySample.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentrySample.m; sourceTree = ""; }; + 84281C482A57933600EE88F2 /* SentryProfilerMocks.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SentryProfilerMocks.h; sourceTree = ""; }; + 84281C492A57933600EE88F2 /* SentryProfilerMocks.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SentryProfilerMocks.mm; sourceTree = ""; }; + 84281C4C2A579A0C00EE88F2 /* SentryProfilerMocksSwiftCompatible.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SentryProfilerMocksSwiftCompatible.h; sourceTree = ""; }; + 84281C4D2A579A0C00EE88F2 /* SentryProfilerMocksSwiftCompatible.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SentryProfilerMocksSwiftCompatible.mm; sourceTree = ""; }; + 84281C542A579C2B00EE88F2 /* libSentryTestUtilsObjc.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libSentryTestUtilsObjc.a; sourceTree = BUILT_PRODUCTS_DIR; }; 8431EE5A29ADB8EA00D8DC56 /* SentryTimeTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryTimeTests.m; sourceTree = ""; }; 8431EFD929B27B1100D8DC56 /* SentryProfilerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SentryProfilerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 8431EFDA29B27B1200D8DC56 /* SentryTests copy-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "SentryTests copy-Info.plist"; path = "/Users/andrewmcknight/Code/organization/getsentry/repos/public/sentry-cocoa/SentryTests copy-Info.plist"; sourceTree = ""; }; @@ -1782,10 +1802,18 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 84281C512A579C2B00EE88F2 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 8431EFD029B27B1100D8DC56 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 84281C612A579C8800EE88F2 /* libSentryTestUtilsObjc.a in Frameworks */, 84B7FA3D29B2879C00AD93B1 /* libSentryTestUtils.a in Frameworks */, 8431EFD129B27B1100D8DC56 /* Sentry.framework in Frameworks */, ); @@ -2017,6 +2045,7 @@ 84C47B2B2A09239100DAEB8A /* .codecov.yml */, 844DA80628246D5000E6B62E /* .craft.yml */, 844DA80A28246D5000E6B62E /* .swiftlint.yml */, + 84281C552A579C2B00EE88F2 /* SentryTestUtilsObjc */, 6304360C1EC05CEF00C4D3FA /* Frameworks */, 6327C5D41EB8A783004E799B /* Products */, 63AA756E1EB8AEDB00D153DE /* Sources */, @@ -2039,6 +2068,7 @@ D8199DAA29376E9B0074249E /* SentrySwiftUI.framework */, 8431EFD929B27B1100D8DC56 /* SentryProfilerTests.xctest */, 8431F00A29B284F200D8DC56 /* libSentryTestUtils.a */, + 84281C542A579C2B00EE88F2 /* libSentryTestUtilsObjc.a */, ); name = Products; sourceTree = ""; @@ -3129,6 +3159,13 @@ path = Profiling; sourceTree = ""; }; + 84281C552A579C2B00EE88F2 /* SentryTestUtilsObjc */ = { + isa = PBXGroup; + children = ( + ); + path = SentryTestUtilsObjc; + sourceTree = ""; + }; 8431EFDB29B27B3D00D8DC56 /* SentryProfilerTests */ = { isa = PBXGroup; children = ( @@ -3161,6 +3198,10 @@ children = ( 7BD47B4C268F0B080076A663 /* ClearTestState.swift */, 84AC61D829F7643B009EEF61 /* TestDispatchFactory.swift */, + 84281C482A57933600EE88F2 /* SentryProfilerMocks.h */, + 84281C492A57933600EE88F2 /* SentryProfilerMocks.mm */, + 84281C4C2A579A0C00EE88F2 /* SentryProfilerMocksSwiftCompatible.h */, + 84281C4D2A579A0C00EE88F2 /* SentryProfilerMocksSwiftCompatible.mm */, 84AC61DA29F7654A009EEF61 /* TestDispatchSourceWrapper.swift */, 84A5D75A29D5170700388BFA /* TimeInterval+Sentry.swift */, 7B30B68126527C55006B2752 /* TestDisplayLinkWrapper.swift */, @@ -3754,6 +3795,23 @@ productReference = 63AA76651EB8CB2F00D153DE /* SentryTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; + 84281C532A579C2B00EE88F2 /* SentryTestUtilsObjc */ = { + isa = PBXNativeTarget; + buildConfigurationList = 84281C5A2A579C2B00EE88F2 /* Build configuration list for PBXNativeTarget "SentryTestUtilsObjc" */; + buildPhases = ( + 84281C502A579C2B00EE88F2 /* Sources */, + 84281C512A579C2B00EE88F2 /* Frameworks */, + 84281C522A579C2B00EE88F2 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = SentryTestUtilsObjc; + productName = SentryTestUtilsObjc; + productReference = 84281C542A579C2B00EE88F2 /* libSentryTestUtilsObjc.a */; + productType = "com.apple.product-type.library.static"; + }; 8431EECF29B27B1100D8DC56 /* SentryProfilerTests */ = { isa = PBXNativeTarget; buildConfigurationList = 8431EFD429B27B1100D8DC56 /* Build configuration list for PBXNativeTarget "SentryProfilerTests" */; @@ -3849,6 +3907,11 @@ LastSwiftMigration = 1120; ProvisioningStyle = Manual; }; + 84281C532A579C2B00EE88F2 = { + CreatedOnToolsVersion = 14.3; + DevelopmentTeam = 97JCY7859U; + ProvisioningStyle = Automatic; + }; 8431EECF29B27B1100D8DC56 = { ProvisioningStyle = Manual; }; @@ -3887,6 +3950,7 @@ D8199DA929376E9B0074249E /* SentrySwiftUI */, 8431EECF29B27B1100D8DC56 /* SentryProfilerTests */, 8431F00929B284F200D8DC56 /* SentryTestUtils */, + 84281C532A579C2B00EE88F2 /* SentryTestUtilsObjc */, ); }; /* End PBXProject section */ @@ -4415,6 +4479,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 84281C502A579C2B00EE88F2 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 8431EED429B27B1100D8DC56 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -4442,6 +4513,7 @@ 84AC61D929F7643B009EEF61 /* TestDispatchFactory.swift in Sources */, 8431F01929B2852D00D8DC56 /* Invocation.swift in Sources */, 84B7FA4629B2935F00AD93B1 /* ClearTestState.swift in Sources */, + 84281C622A579D0700EE88F2 /* SentryProfilerMocksSwiftCompatible.mm in Sources */, 8431F01529B2851500D8DC56 /* TestSentryNSTimerFactory.swift in Sources */, 84B7FA3C29B2876F00AD93B1 /* TestConstants.swift in Sources */, 8431F01A29B2852D00D8DC56 /* Dynamic.swift in Sources */, @@ -4453,6 +4525,7 @@ 84B7FA4529B2926900AD93B1 /* TestDisplayLinkWrapper.swift in Sources */, 84AC61DB29F7654A009EEF61 /* TestDispatchSourceWrapper.swift in Sources */, 8431F01729B2851500D8DC56 /* TestSentrySystemWrapper.swift in Sources */, + 84281C632A579D0700EE88F2 /* SentryProfilerMocks.mm in Sources */, 84B7FA4129B28CD200AD93B1 /* TestSentryDispatchQueueWrapper.swift in Sources */, 84B7FA3E29B28ADD00AD93B1 /* TestClient.swift in Sources */, ); @@ -4896,6 +4969,111 @@ }; name = TestCI; }; + 84281C5B2A579C2B00EE88F2 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 97JCY7859U; + GCC_C_LANGUAGE_STANDARD = "compiler-default"; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 84281C5C2A579C2B00EE88F2 /* Test */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 97JCY7859U; + GCC_C_LANGUAGE_STANDARD = "compiler-default"; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MTL_FAST_MATH = YES; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Test; + }; + 84281C5D2A579C2B00EE88F2 /* TestCI */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 97JCY7859U; + GCC_C_LANGUAGE_STANDARD = "compiler-default"; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MTL_FAST_MATH = YES; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = TestCI; + }; + 84281C5E2A579C2B00EE88F2 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 97JCY7859U; + GCC_C_LANGUAGE_STANDARD = "compiler-default"; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MTL_FAST_MATH = YES; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; 8431EFD529B27B1100D8DC56 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 63AA76AE1EB9D5CD00D153DE /* SentryTests.xcconfig */; @@ -5552,6 +5730,17 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 84281C5A2A579C2B00EE88F2 /* Build configuration list for PBXNativeTarget "SentryTestUtilsObjc" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 84281C5B2A579C2B00EE88F2 /* Debug */, + 84281C5C2A579C2B00EE88F2 /* Test */, + 84281C5D2A579C2B00EE88F2 /* TestCI */, + 84281C5E2A579C2B00EE88F2 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 8431EFD429B27B1100D8DC56 /* Build configuration list for PBXNativeTarget "SentryProfilerTests" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/Sentry.xcodeproj/xcshareddata/xcschemes/SentryTestUtils.xcscheme b/Sentry.xcodeproj/xcshareddata/xcschemes/SentryTestUtils.xcscheme new file mode 100644 index 00000000000..bcc7c632e3e --- /dev/null +++ b/Sentry.xcodeproj/xcshareddata/xcschemes/SentryTestUtils.xcscheme @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SentryTestUtils/SentryProfilerMocks.h b/SentryTestUtils/SentryProfilerMocks.h new file mode 100644 index 00000000000..bae31956fdc --- /dev/null +++ b/SentryTestUtils/SentryProfilerMocks.h @@ -0,0 +1,16 @@ +#import "SentryBacktrace.hpp" +#import + +using namespace sentry::profiling; + +NS_ASSUME_NONNULL_BEGIN + +Backtrace mockBacktrace(thread::TIDType threadID, const int threadPriority, + const char *_Nullable threadName, std::uint64_t queueAddress, std::string queueLabel, + std::vector addresses); + +@interface SentryProfilerMocks : NSObject + +@end + +NS_ASSUME_NONNULL_END diff --git a/SentryTestUtils/SentryProfilerMocks.mm b/SentryTestUtils/SentryProfilerMocks.mm new file mode 100644 index 00000000000..6234b52dcac --- /dev/null +++ b/SentryTestUtils/SentryProfilerMocks.mm @@ -0,0 +1,28 @@ +#import "SentryProfilerMocks.h" + +Backtrace +mockBacktrace(thread::TIDType threadID, const int threadPriority, const char *threadName, + std::uint64_t queueAddress, std::string queueLabel, std::vector addresses) +{ + ThreadMetadata threadMetadata; + if (threadName != nullptr) { + threadMetadata.name = threadName; + } + threadMetadata.threadID = threadID; + threadMetadata.priority = threadPriority; + + QueueMetadata queueMetadata; + queueMetadata.address = queueAddress; + queueMetadata.label = std::make_shared(queueLabel); + + Backtrace backtrace; + backtrace.threadMetadata = threadMetadata; + backtrace.queueMetadata = queueMetadata; + backtrace.addresses = std::vector(addresses); + + return backtrace; +} + +@implementation SentryProfilerMocks + +@end diff --git a/SentryTestUtils/SentryProfilerMocksSwiftCompatible.h b/SentryTestUtils/SentryProfilerMocksSwiftCompatible.h new file mode 100644 index 00000000000..c464ed82136 --- /dev/null +++ b/SentryTestUtils/SentryProfilerMocksSwiftCompatible.h @@ -0,0 +1,15 @@ +#import + +@class SentryProfilingState; + +NS_ASSUME_NONNULL_BEGIN + +void appendMockBacktrace(SentryProfilingState *state, uint64_t threadID, const int threadPriority, + const char *_Nullable threadName, uint64_t queueAddress, const char *queueLabel, + NSArray *addresses); + +@interface SentryProfilerMocksSwiftCompatible : NSObject + +@end + +NS_ASSUME_NONNULL_END diff --git a/SentryTestUtils/SentryProfilerMocksSwiftCompatible.mm b/SentryTestUtils/SentryProfilerMocksSwiftCompatible.mm new file mode 100644 index 00000000000..3cb90950f12 --- /dev/null +++ b/SentryTestUtils/SentryProfilerMocksSwiftCompatible.mm @@ -0,0 +1,27 @@ +#import "SentryProfilerMocksSwiftCompatible.h" +#import "SentryProfilerMocks.h" +#import "SentryProfilerState.h" +#include + +using namespace std; + +void +appendMockBacktrace(SentryProfilingState *state, uint64_t threadID, const int threadPriority, + const char *_Nullable threadName, uint64_t queueAddress, const char *queueLabel, + NSArray *addresses) +{ + + auto backtraceAddresses = std::vector(); + + for (NSNumber *address in addresses) { + backtraceAddresses.push_back(address.unsignedLongLongValue); + } + + const auto backtrace = mockBacktrace( + threadID, threadPriority, threadName, queueAddress, queueLabel, backtraceAddresses); + [state appendBacktrace:backtrace]; +} + +@implementation SentryProfilerMocksSwiftCompatible + +@end diff --git a/Tests/SentryProfilerTests/SentryProfilerTests.mm b/Tests/SentryProfilerTests/SentryProfilerTests.mm index 1d898ed4f0f..a1deaeed099 100644 --- a/Tests/SentryProfilerTests/SentryProfilerTests.mm +++ b/Tests/SentryProfilerTests/SentryProfilerTests.mm @@ -3,6 +3,7 @@ #import "SentryId.h" #import "SentryProfileTimeseries.h" #import "SentryProfiler+Test.h" +#import "SentryProfilerMocks.h" #import "SentryProfilerState.h" #import "SentryProfilingConditionals.h" #import "SentryThread.h" @@ -75,23 +76,16 @@ - (void)testProfilerMutationDuringSlicing const auto queue = thread + threads * 3; uint64_t address = thread + threads * 4; - ThreadMetadata threadMetadata; - threadMetadata.name = [[NSString stringWithFormat:@"testThread-%d", thread] + const auto threadName = [[NSString stringWithFormat:@"testThread-%d", thread] cStringUsingEncoding:NSUTF8StringEncoding]; - threadMetadata.threadID = threadID; - threadMetadata.priority = threadPriority; - - QueueMetadata queueMetadata; - queueMetadata.address = queue; - queueMetadata.label = std::make_shared([[NSString - stringWithFormat:@"testQueue-%d", thread] cStringUsingEncoding:NSUTF8StringEncoding]); - - Backtrace backtrace; - backtrace.threadMetadata = threadMetadata; - backtrace.queueMetadata = queueMetadata; - backtrace.addresses + const auto queueLabel = std::string([[NSString stringWithFormat:@"testQueue-%d", thread] + cStringUsingEncoding:NSUTF8StringEncoding]); + const auto addresses = std::vector({ address + 1, address + 2, address + 3 }); + auto backtrace + = mockBacktrace(threadID, threadPriority, threadName, queue, queueLabel, addresses); + for (auto sample = 0; sample < samplesPerThread; sample++) { backtrace.absoluteTimestamp = sampleIdx; // simulate 1 sample per nanosecond [state appendBacktrace:backtrace]; @@ -125,22 +119,8 @@ - (void)testProfilerMutationDuringSlicing }]; }; - ThreadMetadata threadMetadata; - threadMetadata.name = "testThread"; - threadMetadata.threadID = 12345568910; - threadMetadata.priority = 666; - - QueueMetadata queueMetadata; - queueMetadata.address = 9876543210; - queueMetadata.label = std::make_shared("testQueue"); - - const auto addresses = std::vector({ 777, 888, 789 }); - - Backtrace backtrace; - backtrace.threadMetadata = threadMetadata; - backtrace.queueMetadata = queueMetadata; - backtrace.absoluteTimestamp = 5; - backtrace.addresses = addresses; + const auto backtrace = mockBacktrace(12345568910, 666, "testThread", 9876543210, "testQueue", + std::vector({ 777, 888, 789 })); const auto mutateExpectation = [self expectationWithDescription:@"all mutating operations complete"]; @@ -180,19 +160,9 @@ - (void)testProfilerMutationDuringSerialization SentryProfilerState *state = [[SentryProfilerState alloc] init]; // initialize the data structures with some simulated data { - ThreadMetadata threadMetadata; // leave thread name as nil so it can be overwritten later - threadMetadata.threadID = 1; - threadMetadata.priority = 2; - - QueueMetadata queueMetadata; - queueMetadata.address = 3; - queueMetadata.label = std::make_shared("testQueue-1"); - - Backtrace backtrace; - backtrace.threadMetadata = threadMetadata; - backtrace.queueMetadata = queueMetadata; - backtrace.addresses = std::vector({ 0x4, 0x5, 0x6 }); + auto backtrace = mockBacktrace( + 1, 2, nullptr, 3, "testQueue-1", std::vector({ 0x4, 0x5, 0x6 })); backtrace.absoluteTimestamp = 1; [state appendBacktrace:backtrace]; @@ -222,42 +192,17 @@ - (void)testProfilerMutationDuringSerialization // cause the data structures to be modified again: add new addresses { - ThreadMetadata threadMetadata; - threadMetadata.name = "newThread-2"; - threadMetadata.threadID = 12345568910; - threadMetadata.priority = 666; - - QueueMetadata queueMetadata; - queueMetadata.address = 9876543210; - queueMetadata.label = std::make_shared("newQueue-2"); - - Backtrace backtrace; - backtrace.threadMetadata = threadMetadata; - backtrace.queueMetadata = queueMetadata; - backtrace.absoluteTimestamp = 5; - backtrace.addresses = std::vector({ 0x777, 0x888, 0x999 }); - + const auto backtrace = mockBacktrace(12345568910, 666, "newThread-2", 9876543210, + "newQueue-2", std::vector({ 0x777, 0x888, 0x999 })); [state appendBacktrace:backtrace]; } // cause the data structures to be modified again: overwrite previous thread metadata // subdictionary contents { - ThreadMetadata threadMetadata; - threadMetadata.name = "testThread-1"; - threadMetadata.threadID = 1; - threadMetadata.priority = 2; - - QueueMetadata queueMetadata; - queueMetadata.address = 3; - queueMetadata.label = std::make_shared("testQueue-1"); - - Backtrace backtrace; - backtrace.threadMetadata = threadMetadata; - backtrace.queueMetadata = queueMetadata; + auto backtrace = mockBacktrace( + 1, 2, "testThread-1", 3, "testQueue-1", std::vector({ 0x4, 0x5, 0x6 })); backtrace.absoluteTimestamp = 6; - backtrace.addresses = std::vector({ 0x4, 0x5, 0x6 }); - [state appendBacktrace:backtrace]; } @@ -292,49 +237,16 @@ - (void)testProfilerPayload SentryProfilerState *state = [[SentryProfilerState alloc] init]; // record an initial backtrace - - ThreadMetadata threadMetadata1; - threadMetadata1.name = "testThread"; - threadMetadata1.threadID = 12345568910; - threadMetadata1.priority = 666; - - QueueMetadata queueMetadata1; - queueMetadata1.address = 9876543210; - queueMetadata1.label = std::make_shared("testQueue"); - - const auto addresses1 = std::vector({ 0x123, 0x456, 0x789 }); - - Backtrace backtrace1; - backtrace1.threadMetadata = threadMetadata1; - backtrace1.queueMetadata = queueMetadata1; - backtrace1.absoluteTimestamp = 5; - backtrace1.addresses = addresses1; - + const auto backtrace1 = mockBacktrace(12345568910, 666, "testThread", 9876543210, "testQueue", + std::vector({ 0x123, 0x456, 0x789 })); [state appendBacktrace:backtrace1]; // record a second backtrace with some common addresses to test frame deduplication - - ThreadMetadata threadMetadata2; - threadMetadata2.name = "testThread"; - threadMetadata2.threadID = 12345568910; - threadMetadata2.priority = 666; - - QueueMetadata queueMetadata2; - queueMetadata2.address = 9876543210; - queueMetadata2.label = std::make_shared("testQueue"); - - const auto addresses2 = std::vector({ 0x777, 0x888, 0x789 }); - - Backtrace backtrace2; - backtrace2.threadMetadata = threadMetadata2; - backtrace2.queueMetadata = queueMetadata2; - backtrace2.absoluteTimestamp = 5; - backtrace2.addresses = addresses2; - + const auto backtrace2 = mockBacktrace(12345568910, 666, "testThread", 9876543210, "testQueue", + std::vector({ 0x777, 0x888, 0x789 })); [state appendBacktrace:backtrace2]; // record a third backtrace that's identical to the second to test stack/frame deduplication - [state appendBacktrace:backtrace2]; [state mutate:^(SentryProfilerMutableState *mutableState) { From 05ffde07617a8f4d09ffc0b0298168e531737d65 Mon Sep 17 00:00:00 2001 From: Andrew McKnight Date: Thu, 6 Jul 2023 21:29:45 -0800 Subject: [PATCH 06/15] wip replacing live backtrace gathering with mocks in swift tests --- Sentry.xcodeproj/project.pbxproj | 171 +----------------- .../xcschemes/SentryTestUtils.xcscheme | 4 +- .../SentryProfilerMocksSwiftCompatible.h | 18 +- .../SentryProfilerMocksSwiftCompatible.mm | 23 ++- Sources/Sentry/SentryProfiler.mm | 14 +- Sources/Sentry/include/SentryProfiler+Test.h | 13 +- Sources/Sentry/include/SentryProfilerState.h | 7 +- .../SentryProfilerSwiftTests.swift | 31 ++-- .../SentryProfilerTests.mm | 2 +- .../SentryTests/SentryTests-Bridging-Header.h | 4 +- 10 files changed, 79 insertions(+), 208 deletions(-) diff --git a/Sentry.xcodeproj/project.pbxproj b/Sentry.xcodeproj/project.pbxproj index ad3aa29faa3..f27e0aadb9f 100644 --- a/Sentry.xcodeproj/project.pbxproj +++ b/Sentry.xcodeproj/project.pbxproj @@ -604,7 +604,6 @@ 84281C432A578E5600EE88F2 /* SentryProfilerState.mm in Sources */ = {isa = PBXBuildFile; fileRef = 84281C422A578E5600EE88F2 /* SentryProfilerState.mm */; }; 84281C462A57905700EE88F2 /* SentrySample.h in Headers */ = {isa = PBXBuildFile; fileRef = 84281C442A57905700EE88F2 /* SentrySample.h */; }; 84281C472A57905700EE88F2 /* SentrySample.m in Sources */ = {isa = PBXBuildFile; fileRef = 84281C452A57905700EE88F2 /* SentrySample.m */; }; - 84281C612A579C8800EE88F2 /* libSentryTestUtilsObjc.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 84281C542A579C2B00EE88F2 /* libSentryTestUtilsObjc.a */; }; 84281C622A579D0700EE88F2 /* SentryProfilerMocksSwiftCompatible.mm in Sources */ = {isa = PBXBuildFile; fileRef = 84281C4D2A579A0C00EE88F2 /* SentryProfilerMocksSwiftCompatible.mm */; }; 84281C632A579D0700EE88F2 /* SentryProfilerMocks.mm in Sources */ = {isa = PBXBuildFile; fileRef = 84281C492A57933600EE88F2 /* SentryProfilerMocks.mm */; }; 8431EE5B29ADB8EA00D8DC56 /* SentryTimeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8431EE5A29ADB8EA00D8DC56 /* SentryTimeTests.m */; }; @@ -878,18 +877,6 @@ }; /* End PBXContainerItemProxy section */ -/* Begin PBXCopyFilesBuildPhase section */ - 84281C522A579C2B00EE88F2 /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = "include/$(PRODUCT_NAME)"; - dstSubfolderSpec = 16; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - /* Begin PBXFileReference section */ 0354A22A2A134D9C003C3A04 /* SentryProfilerState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SentryProfilerState.h; path = Sources/Sentry/include/SentryProfilerState.h; sourceTree = SOURCE_ROOT; }; 0356A56E288B4612008BF593 /* SentryProfilesSampler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryProfilesSampler.h; path = Sources/Sentry/include/SentryProfilesSampler.h; sourceTree = SOURCE_ROOT; }; @@ -1546,7 +1533,7 @@ 84281C492A57933600EE88F2 /* SentryProfilerMocks.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SentryProfilerMocks.mm; sourceTree = ""; }; 84281C4C2A579A0C00EE88F2 /* SentryProfilerMocksSwiftCompatible.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SentryProfilerMocksSwiftCompatible.h; sourceTree = ""; }; 84281C4D2A579A0C00EE88F2 /* SentryProfilerMocksSwiftCompatible.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SentryProfilerMocksSwiftCompatible.mm; sourceTree = ""; }; - 84281C542A579C2B00EE88F2 /* libSentryTestUtilsObjc.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libSentryTestUtilsObjc.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 84281C642A57D36100EE88F2 /* SentryProfilerState+ObjCpp.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "SentryProfilerState+ObjCpp.h"; path = "../include/SentryProfilerState+ObjCpp.h"; sourceTree = ""; }; 8431EE5A29ADB8EA00D8DC56 /* SentryTimeTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryTimeTests.m; sourceTree = ""; }; 8431EFD929B27B1100D8DC56 /* SentryProfilerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SentryProfilerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 8431EFDA29B27B1200D8DC56 /* SentryTests copy-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "SentryTests copy-Info.plist"; path = "/Users/andrewmcknight/Code/organization/getsentry/repos/public/sentry-cocoa/SentryTests copy-Info.plist"; sourceTree = ""; }; @@ -1802,18 +1789,10 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 84281C512A579C2B00EE88F2 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; 8431EFD029B27B1100D8DC56 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 84281C612A579C8800EE88F2 /* libSentryTestUtilsObjc.a in Frameworks */, 84B7FA3D29B2879C00AD93B1 /* libSentryTestUtils.a in Frameworks */, 8431EFD129B27B1100D8DC56 /* Sentry.framework in Frameworks */, ); @@ -2068,7 +2047,6 @@ D8199DAA29376E9B0074249E /* SentrySwiftUI.framework */, 8431EFD929B27B1100D8DC56 /* SentryProfilerTests.xctest */, 8431F00A29B284F200D8DC56 /* libSentryTestUtils.a */, - 84281C542A579C2B00EE88F2 /* libSentryTestUtilsObjc.a */, ); name = Products; sourceTree = ""; @@ -3134,6 +3112,7 @@ 03F84D2B27DD4191008FE43F /* SentryProfiler.mm */, 84A888FC28D9B11700C51DFD /* SentryProfiler+Test.h */, 0354A22A2A134D9C003C3A04 /* SentryProfilerState.h */, + 84281C642A57D36100EE88F2 /* SentryProfilerState+ObjCpp.h */, 84281C422A578E5600EE88F2 /* SentryProfilerState.mm */, 0356A56E288B4612008BF593 /* SentryProfilesSampler.h */, 0356A56F288B4612008BF593 /* SentryProfilesSampler.m */, @@ -3795,23 +3774,6 @@ productReference = 63AA76651EB8CB2F00D153DE /* SentryTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; - 84281C532A579C2B00EE88F2 /* SentryTestUtilsObjc */ = { - isa = PBXNativeTarget; - buildConfigurationList = 84281C5A2A579C2B00EE88F2 /* Build configuration list for PBXNativeTarget "SentryTestUtilsObjc" */; - buildPhases = ( - 84281C502A579C2B00EE88F2 /* Sources */, - 84281C512A579C2B00EE88F2 /* Frameworks */, - 84281C522A579C2B00EE88F2 /* CopyFiles */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = SentryTestUtilsObjc; - productName = SentryTestUtilsObjc; - productReference = 84281C542A579C2B00EE88F2 /* libSentryTestUtilsObjc.a */; - productType = "com.apple.product-type.library.static"; - }; 8431EECF29B27B1100D8DC56 /* SentryProfilerTests */ = { isa = PBXNativeTarget; buildConfigurationList = 8431EFD429B27B1100D8DC56 /* Build configuration list for PBXNativeTarget "SentryProfilerTests" */; @@ -3907,11 +3869,6 @@ LastSwiftMigration = 1120; ProvisioningStyle = Manual; }; - 84281C532A579C2B00EE88F2 = { - CreatedOnToolsVersion = 14.3; - DevelopmentTeam = 97JCY7859U; - ProvisioningStyle = Automatic; - }; 8431EECF29B27B1100D8DC56 = { ProvisioningStyle = Manual; }; @@ -3950,7 +3907,6 @@ D8199DA929376E9B0074249E /* SentrySwiftUI */, 8431EECF29B27B1100D8DC56 /* SentryProfilerTests */, 8431F00929B284F200D8DC56 /* SentryTestUtils */, - 84281C532A579C2B00EE88F2 /* SentryTestUtilsObjc */, ); }; /* End PBXProject section */ @@ -4479,13 +4435,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 84281C502A579C2B00EE88F2 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; 8431EED429B27B1100D8DC56 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -4969,111 +4918,6 @@ }; name = TestCI; }; - 84281C5B2A579C2B00EE88F2 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "c++14"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = 97JCY7859U; - GCC_C_LANGUAGE_STANDARD = "compiler-default"; - IPHONEOS_DEPLOYMENT_TARGET = 16.4; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - OTHER_LDFLAGS = "-ObjC"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 84281C5C2A579C2B00EE88F2 /* Test */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "c++14"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = 97JCY7859U; - GCC_C_LANGUAGE_STANDARD = "compiler-default"; - IPHONEOS_DEPLOYMENT_TARGET = 16.4; - MTL_FAST_MATH = YES; - OTHER_LDFLAGS = "-ObjC"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Test; - }; - 84281C5D2A579C2B00EE88F2 /* TestCI */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "c++14"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = 97JCY7859U; - GCC_C_LANGUAGE_STANDARD = "compiler-default"; - IPHONEOS_DEPLOYMENT_TARGET = 16.4; - MTL_FAST_MATH = YES; - OTHER_LDFLAGS = "-ObjC"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = TestCI; - }; - 84281C5E2A579C2B00EE88F2 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "c++14"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = 97JCY7859U; - GCC_C_LANGUAGE_STANDARD = "compiler-default"; - IPHONEOS_DEPLOYMENT_TARGET = 16.4; - MTL_FAST_MATH = YES; - OTHER_LDFLAGS = "-ObjC"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; 8431EFD529B27B1100D8DC56 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 63AA76AE1EB9D5CD00D153DE /* SentryTests.xcconfig */; @@ -5730,17 +5574,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 84281C5A2A579C2B00EE88F2 /* Build configuration list for PBXNativeTarget "SentryTestUtilsObjc" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 84281C5B2A579C2B00EE88F2 /* Debug */, - 84281C5C2A579C2B00EE88F2 /* Test */, - 84281C5D2A579C2B00EE88F2 /* TestCI */, - 84281C5E2A579C2B00EE88F2 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; 8431EFD429B27B1100D8DC56 /* Build configuration list for PBXNativeTarget "SentryProfilerTests" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/Sentry.xcodeproj/xcshareddata/xcschemes/SentryTestUtils.xcscheme b/Sentry.xcodeproj/xcshareddata/xcschemes/SentryTestUtils.xcscheme index bcc7c632e3e..3dc64e85045 100644 --- a/Sentry.xcodeproj/xcshareddata/xcschemes/SentryTestUtils.xcscheme +++ b/Sentry.xcodeproj/xcshareddata/xcschemes/SentryTestUtils.xcscheme @@ -23,14 +23,14 @@ -@class SentryProfilingState; +@class SentryProfilerState; NS_ASSUME_NONNULL_BEGIN -void appendMockBacktrace(SentryProfilingState *state, uint64_t threadID, const int threadPriority, - const char *_Nullable threadName, uint64_t queueAddress, const char *queueLabel, - NSArray *addresses); - +/** + * This delivers a wrapper around the C++ function to create a mock backtrace for incorporation into + * profiler state that can be called from Swift tests. + */ @interface SentryProfilerMocksSwiftCompatible : NSObject ++ (void)appendMockBacktraceToState:(SentryProfilerState *)state + threadID:(uint64_t)threadID + threadPriority:(const int)threadPriority + threadName:(nullable NSString *)threadName + queueAddress:(uint64_t)queueAddress + queueLabel:(NSString *)queueLabel + addresses:(NSArray *)addresses; + @end NS_ASSUME_NONNULL_END diff --git a/SentryTestUtils/SentryProfilerMocksSwiftCompatible.mm b/SentryTestUtils/SentryProfilerMocksSwiftCompatible.mm index 3cb90950f12..77b2820aa59 100644 --- a/SentryTestUtils/SentryProfilerMocksSwiftCompatible.mm +++ b/SentryTestUtils/SentryProfilerMocksSwiftCompatible.mm @@ -1,27 +1,30 @@ #import "SentryProfilerMocksSwiftCompatible.h" #import "SentryProfilerMocks.h" -#import "SentryProfilerState.h" +#import "SentryProfilerState+ObjCpp.h" #include using namespace std; -void -appendMockBacktrace(SentryProfilingState *state, uint64_t threadID, const int threadPriority, - const char *_Nullable threadName, uint64_t queueAddress, const char *queueLabel, - NSArray *addresses) -{ +@implementation SentryProfilerMocksSwiftCompatible ++ (void)appendMockBacktraceToState:(SentryProfilerState *)state + threadID:(uint64_t)threadID + threadPriority:(const int)threadPriority + threadName:(nullable NSString *)threadName + queueAddress:(uint64_t)queueAddress + queueLabel:(NSString *)queueLabel + addresses:(NSArray *)addresses +{ auto backtraceAddresses = std::vector(); for (NSNumber *address in addresses) { backtraceAddresses.push_back(address.unsignedLongLongValue); } - const auto backtrace = mockBacktrace( - threadID, threadPriority, threadName, queueAddress, queueLabel, backtraceAddresses); + const auto backtrace = mockBacktrace(threadID, threadPriority, + [threadName cStringUsingEncoding:NSUTF8StringEncoding], queueAddress, + [queueLabel cStringUsingEncoding:NSUTF8StringEncoding], backtraceAddresses); [state appendBacktrace:backtrace]; } -@implementation SentryProfilerMocksSwiftCompatible - @end diff --git a/Sources/Sentry/SentryProfiler.mm b/Sources/Sentry/SentryProfiler.mm index 29cda4d439e..bdecb656292 100644 --- a/Sources/Sentry/SentryProfiler.mm +++ b/Sources/Sentry/SentryProfiler.mm @@ -24,7 +24,7 @@ # import "SentryNSProcessInfoWrapper.h" # import "SentryNSTimerFactory.h" # import "SentryProfileTimeseries.h" -# import "SentryProfilerState.h" +# import "SentryProfilerState+ObjCpp.h" # import "SentrySample.h" # import "SentrySamplingProfiler.hpp" # import "SentryScope+Private.h" @@ -268,7 +268,6 @@ } @implementation SentryProfiler { - SentryProfilerState *_state; std::shared_ptr _profiler; SentryMetricProfiler *_metricProfiler; SentryDebugImageProvider *_debugImageProvider; @@ -375,7 +374,7 @@ + (SentryEnvelopeItem *)createProfilingEnvelopeItemForTransaction:(SentryTransac return nil; } - return serializedProfileData([_gCurrentProfiler->_state copyProfilingData], transaction, + return serializedProfileData([_gCurrentProfiler._state copyProfilingData], transaction, profileID, profilerTruncationReasonName(_gCurrentProfiler->_truncationReason), _gCurrentProfiler -> _hub.scope.environmentString ?: _gCurrentProfiler->_hub.getClient.options.environment, @@ -470,7 +469,7 @@ - (void)start SENTRY_LOG_DEBUG(@"Starting profiler."); SentryProfilerState *const state = [[SentryProfilerState alloc] init]; - _state = state; + self._state = state; _profiler = std::make_shared( [state](auto &backtrace) { // in test, we'll overwrite the sample's timestamp to one mocked by SentryCurrentDate @@ -529,6 +528,13 @@ - (BOOL)isRunning return _profiler->isSampling(); } +# if defined(TEST) || defined(TESTCI) ++ (SentryProfiler *)getCurrentProfiler +{ + return _gCurrentProfiler; +} +# endif // defined(TEST) || defined(TESTCI) + @end #endif diff --git a/Sources/Sentry/include/SentryProfiler+Test.h b/Sources/Sentry/include/SentryProfiler+Test.h index a063e41c09b..b8b778eb500 100644 --- a/Sources/Sentry/include/SentryProfiler+Test.h +++ b/Sources/Sentry/include/SentryProfiler+Test.h @@ -1,4 +1,3 @@ -#include "SentryBacktrace.hpp" #import "SentryProfiler.h" #import "SentryProfilingConditionals.h" @@ -6,6 +5,7 @@ @class SentryDebugMeta; @class SentryId; +@class SentryProfilerState; @class SentrySample; @class SentryTransaction; @@ -16,6 +16,17 @@ NSDictionary *serializedProfileData(NSDictionary NSString *environment, NSString *release, NSDictionary *serializedMetrics, NSArray *debugMeta, SentryHub *hub); +@interface +SentryProfiler () + +@property (strong, nonatomic) SentryProfilerState *_state; + +# if defined(TEST) || defined(TESTCI) ++ (SentryProfiler *)getCurrentProfiler; +# endif // defined(TEST) || defined(TESTCI) + +@end + NS_ASSUME_NONNULL_END #endif diff --git a/Sources/Sentry/include/SentryProfilerState.h b/Sources/Sentry/include/SentryProfilerState.h index d7c12972d33..9006a3dfff5 100644 --- a/Sources/Sentry/include/SentryProfilerState.h +++ b/Sources/Sentry/include/SentryProfilerState.h @@ -1,7 +1,11 @@ -#import "SentryBacktrace.hpp" #import "SentryProfilingConditionals.h" #import +/* + * This file should not contain any C++ interfaces so it can be used from Swift tests. See + * SentryProfilerState+ObjCpp.h. + */ + #if SENTRY_TARGET_PROFILING_SUPPORTED NS_ASSUME_NONNULL_BEGIN @@ -73,7 +77,6 @@ NSString *parseBacktraceSymbolsFunctionName(const char *symbol); @interface SentryProfilerState : NSObject // All functions are safe to call from multiple threads concurrently - (void)mutate:(void (^)(SentryProfilerMutableState *))block; -- (void)appendBacktrace:(const sentry::profiling::Backtrace &)backtrace; - (NSDictionary *)copyProfilingData; @end diff --git a/Tests/SentryProfilerTests/SentryProfilerSwiftTests.swift b/Tests/SentryProfilerTests/SentryProfilerSwiftTests.swift index 34cbcc9a789..165a59b9d6c 100644 --- a/Tests/SentryProfilerTests/SentryProfilerSwiftTests.swift +++ b/Tests/SentryProfilerTests/SentryProfilerSwiftTests.swift @@ -228,7 +228,7 @@ class SentryProfilerSwiftTests: XCTestCase { func testMetricProfiler() throws { let span = try fixture.newTransaction() - forceProfilerSample() + addMockBacktrace() try fixture.gatherMockedMetrics(span: span) self.fixture.currentDateProvider.advanceBy(nanoseconds: 1.toNanoSeconds()) span.finish() @@ -246,7 +246,7 @@ class SentryProfilerSwiftTests: XCTestCase { fixture.currentDateProvider.advanceBy(nanoseconds: 100) } - forceProfilerSample() + addMockBacktrace() for (i, span) in spans.enumerated() { try fixture.gatherMockedMetrics(span: span) @@ -280,7 +280,7 @@ class SentryProfilerSwiftTests: XCTestCase { func testConcurrentSpansWithTimeout() throws { let spanA = try fixture.newTransaction() fixture.currentDateProvider.advanceBy(nanoseconds: 1.toNanoSeconds()) - forceProfilerSample() + addMockBacktrace() fixture.currentDateProvider.advanceBy(nanoseconds: 30.toNanoSeconds()) fixture.timeoutTimerFactory.fire() @@ -288,7 +288,7 @@ class SentryProfilerSwiftTests: XCTestCase { let spanB = try fixture.newTransaction() fixture.currentDateProvider.advanceBy(nanoseconds: 0.5.toNanoSeconds()) - forceProfilerSample() + addMockBacktrace() spanB.finish() try self.assertValidProfileData() @@ -297,6 +297,14 @@ class SentryProfilerSwiftTests: XCTestCase { try self.assertValidProfileData() } + /** + * We received a report of unbounded memory growth from a customer. Was able to reproduce by endlessly starting transactions that go on to timeout, thereby also forcing the profiler to timeout. But, doing it on a nonmain queue meant that the timeout timer in the profiler would never fire. The fix was to schedule that timer on a dispatch to the main queue. + */ + func testRepeatedTransactionTimeoutsFromBgQueue() { + let state = SentryProfilerState() + SentryProfilerMocksSwiftCompatible.appendMockBacktrace(to: state, threadID: 1, threadPriority: 2, threadName: "test", queueAddress: 1, queueLabel: "test", addresses: [0x1, 0x2, 0x3]) + } + func testProfileTimeoutTimer() throws { try performTest(shouldTimeOut: true) } @@ -397,13 +405,9 @@ private extension SentryProfilerSwiftTests { return try XCTUnwrap(envelope.event as? Transaction) } - /// Keep a thread busy over a long enough period of time (long enough for 3 samples) for the sampler to pick it up. - func forceProfilerSample() { - let str = "a" - var concatStr = "" - for _ in 0..<100_000 { - concatStr = concatStr.appending(str) - } + func addMockBacktrace() { + let state = SentryProfiler.getCurrent()._state + SentryProfilerMocksSwiftCompatible.appendMockBacktrace(to: state, threadID: 1, threadPriority: 2, threadName: "test-thread", queueAddress: 3, queueLabel: "test-queue", addresses: [0x3, 0x4, 0x5]) } func performTest(transactionEnvironment: String = kSentryDefaultEnvironment, shouldTimeOut: Bool = false, launchType: SentryAppStartType? = nil, prewarmed: Bool = false) throws { @@ -415,7 +419,8 @@ private extension SentryProfilerSwiftTests { } let span = try fixture.newTransaction(testingAppLaunchSpans: testingAppLaunchSpans) - forceProfilerSample() + + addMockBacktrace() fixture.currentDateProvider.advance(by: 31) if shouldTimeOut { fixture.timeoutTimerFactory.fire() @@ -639,7 +644,7 @@ private extension SentryProfilerSwiftTests { Dynamic(hub).tracesSampler.random = TestRandom(value: 1.0) let span = try fixture.newTransaction() - forceProfilerSample() + addMockBacktrace() fixture.currentDateProvider.advance(by: 5) span.finish() diff --git a/Tests/SentryProfilerTests/SentryProfilerTests.mm b/Tests/SentryProfilerTests/SentryProfilerTests.mm index a1deaeed099..35b56021a08 100644 --- a/Tests/SentryProfilerTests/SentryProfilerTests.mm +++ b/Tests/SentryProfilerTests/SentryProfilerTests.mm @@ -4,7 +4,7 @@ #import "SentryProfileTimeseries.h" #import "SentryProfiler+Test.h" #import "SentryProfilerMocks.h" -#import "SentryProfilerState.h" +#import "SentryProfilerState+ObjCpp.h" #import "SentryProfilingConditionals.h" #import "SentryThread.h" #import "SentryTransaction.h" diff --git a/Tests/SentryTests/SentryTests-Bridging-Header.h b/Tests/SentryTests/SentryTests-Bridging-Header.h index fa681edffe0..6b37ed9c57a 100644 --- a/Tests/SentryTests/SentryTests-Bridging-Header.h +++ b/Tests/SentryTests/SentryTests-Bridging-Header.h @@ -124,7 +124,9 @@ #import "SentryPerformanceTracker.h" #import "SentryPerformanceTrackingIntegration.h" #import "SentryPredicateDescriptor.h" -#import "SentryProfiler.h" +#import "SentryProfiler+Test.h" +#import "SentryProfilerMocksSwiftCompatible.h" +#import "SentryProfilerState.h" #import "SentryQueueableRequestManager.h" #import "SentryRandom.h" #import "SentryRateLimitParser.h" From 9c41f30fc512740aa67d275cb5ee388daa7c4c7b Mon Sep 17 00:00:00 2001 From: Andrew McKnight Date: Thu, 6 Jul 2023 21:31:08 -0800 Subject: [PATCH 07/15] fixup! wip replacing live backtrace gathering with mocks in swift tests --- .../Sentry/include/SentryProfilerState+ObjCpp.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 Sources/Sentry/include/SentryProfilerState+ObjCpp.h diff --git a/Sources/Sentry/include/SentryProfilerState+ObjCpp.h b/Sources/Sentry/include/SentryProfilerState+ObjCpp.h new file mode 100644 index 00000000000..20f8f6f0ce6 --- /dev/null +++ b/Sources/Sentry/include/SentryProfilerState+ObjCpp.h @@ -0,0 +1,14 @@ +#import "SentryBacktrace.hpp" +#import "SentryProfilerState.h" + +/* + * This extension defines C++ interface on SentryProfilerState that is not able to be imported into + * a bridging header via SentryProfilerState.h due to C++/Swift interop limitations. + */ + +@interface +SentryProfilerState () + +- (void)appendBacktrace:(const sentry::profiling::Backtrace &)backtrace; + +@end From 8a683e0d8823348e5b1ff01037ace8231dd9702c Mon Sep 17 00:00:00 2001 From: Andrew McKnight Date: Thu, 6 Jul 2023 21:40:15 -0800 Subject: [PATCH 08/15] fix tests --- .../SentryProfilerMocksSwiftCompatible.mm | 4 +++- .../SentryProfilerSwiftTests.swift | 18 +++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/SentryTestUtils/SentryProfilerMocksSwiftCompatible.mm b/SentryTestUtils/SentryProfilerMocksSwiftCompatible.mm index 77b2820aa59..b9d4fe29165 100644 --- a/SentryTestUtils/SentryProfilerMocksSwiftCompatible.mm +++ b/SentryTestUtils/SentryProfilerMocksSwiftCompatible.mm @@ -1,4 +1,5 @@ #import "SentryProfilerMocksSwiftCompatible.h" +#import "SentryCurrentDate.h" #import "SentryProfilerMocks.h" #import "SentryProfilerState+ObjCpp.h" #include @@ -21,9 +22,10 @@ + (void)appendMockBacktraceToState:(SentryProfilerState *)state backtraceAddresses.push_back(address.unsignedLongLongValue); } - const auto backtrace = mockBacktrace(threadID, threadPriority, + auto backtrace = mockBacktrace(threadID, threadPriority, [threadName cStringUsingEncoding:NSUTF8StringEncoding], queueAddress, [queueLabel cStringUsingEncoding:NSUTF8StringEncoding], backtraceAddresses); + backtrace.absoluteTimestamp = SentryCurrentDate.getCurrentDateProvider.systemTime; [state appendBacktrace:backtrace]; } diff --git a/Tests/SentryProfilerTests/SentryProfilerSwiftTests.swift b/Tests/SentryProfilerTests/SentryProfilerSwiftTests.swift index 165a59b9d6c..63939e14e57 100644 --- a/Tests/SentryProfilerTests/SentryProfilerSwiftTests.swift +++ b/Tests/SentryProfilerTests/SentryProfilerSwiftTests.swift @@ -228,7 +228,7 @@ class SentryProfilerSwiftTests: XCTestCase { func testMetricProfiler() throws { let span = try fixture.newTransaction() - addMockBacktrace() + addMockSamples() try fixture.gatherMockedMetrics(span: span) self.fixture.currentDateProvider.advanceBy(nanoseconds: 1.toNanoSeconds()) span.finish() @@ -246,7 +246,7 @@ class SentryProfilerSwiftTests: XCTestCase { fixture.currentDateProvider.advanceBy(nanoseconds: 100) } - addMockBacktrace() + addMockSamples() for (i, span) in spans.enumerated() { try fixture.gatherMockedMetrics(span: span) @@ -280,7 +280,7 @@ class SentryProfilerSwiftTests: XCTestCase { func testConcurrentSpansWithTimeout() throws { let spanA = try fixture.newTransaction() fixture.currentDateProvider.advanceBy(nanoseconds: 1.toNanoSeconds()) - addMockBacktrace() + addMockSamples() fixture.currentDateProvider.advanceBy(nanoseconds: 30.toNanoSeconds()) fixture.timeoutTimerFactory.fire() @@ -288,7 +288,7 @@ class SentryProfilerSwiftTests: XCTestCase { let spanB = try fixture.newTransaction() fixture.currentDateProvider.advanceBy(nanoseconds: 0.5.toNanoSeconds()) - addMockBacktrace() + addMockSamples() spanB.finish() try self.assertValidProfileData() @@ -405,9 +405,13 @@ private extension SentryProfilerSwiftTests { return try XCTUnwrap(envelope.event as? Transaction) } - func addMockBacktrace() { + func addMockSamples() { let state = SentryProfiler.getCurrent()._state SentryProfilerMocksSwiftCompatible.appendMockBacktrace(to: state, threadID: 1, threadPriority: 2, threadName: "test-thread", queueAddress: 3, queueLabel: "test-queue", addresses: [0x3, 0x4, 0x5]) + fixture.currentDateProvider.advanceBy(nanoseconds: 1) + SentryProfilerMocksSwiftCompatible.appendMockBacktrace(to: state, threadID: 1, threadPriority: 2, threadName: "test-thread", queueAddress: 3, queueLabel: "test-queue", addresses: [0x3, 0x4, 0x5]) + fixture.currentDateProvider.advanceBy(nanoseconds: 1) + SentryProfilerMocksSwiftCompatible.appendMockBacktrace(to: state, threadID: 1, threadPriority: 2, threadName: "test-thread", queueAddress: 3, queueLabel: "test-queue", addresses: [0x3, 0x4, 0x5]) } func performTest(transactionEnvironment: String = kSentryDefaultEnvironment, shouldTimeOut: Bool = false, launchType: SentryAppStartType? = nil, prewarmed: Bool = false) throws { @@ -420,7 +424,7 @@ private extension SentryProfilerSwiftTests { let span = try fixture.newTransaction(testingAppLaunchSpans: testingAppLaunchSpans) - addMockBacktrace() + addMockSamples() fixture.currentDateProvider.advance(by: 31) if shouldTimeOut { fixture.timeoutTimerFactory.fire() @@ -644,7 +648,7 @@ private extension SentryProfilerSwiftTests { Dynamic(hub).tracesSampler.random = TestRandom(value: 1.0) let span = try fixture.newTransaction() - addMockBacktrace() + addMockSamples() fixture.currentDateProvider.advance(by: 5) span.finish() From d1ebb8e8c30fbbc1066413ebabbc7a448e91bbb5 Mon Sep 17 00:00:00 2001 From: Andrew McKnight Date: Thu, 6 Jul 2023 21:43:43 -0800 Subject: [PATCH 09/15] remove scheme needed for debugging --- .../xcschemes/SentryTestUtils.xcscheme | 66 ------------------- 1 file changed, 66 deletions(-) delete mode 100644 Sentry.xcodeproj/xcshareddata/xcschemes/SentryTestUtils.xcscheme diff --git a/Sentry.xcodeproj/xcshareddata/xcschemes/SentryTestUtils.xcscheme b/Sentry.xcodeproj/xcshareddata/xcschemes/SentryTestUtils.xcscheme deleted file mode 100644 index 3dc64e85045..00000000000 --- a/Sentry.xcodeproj/xcshareddata/xcschemes/SentryTestUtils.xcscheme +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - From 0e1274e411109f73b679b40520f93bd19b3672ec Mon Sep 17 00:00:00 2001 From: Andrew McKnight Date: Thu, 6 Jul 2023 21:49:40 -0800 Subject: [PATCH 10/15] remove unused interface --- SentryTestUtils/SentryProfilerMocks.h | 4 ---- SentryTestUtils/SentryProfilerMocks.mm | 4 ---- 2 files changed, 8 deletions(-) diff --git a/SentryTestUtils/SentryProfilerMocks.h b/SentryTestUtils/SentryProfilerMocks.h index bae31956fdc..1ada1db7b6a 100644 --- a/SentryTestUtils/SentryProfilerMocks.h +++ b/SentryTestUtils/SentryProfilerMocks.h @@ -9,8 +9,4 @@ Backtrace mockBacktrace(thread::TIDType threadID, const int threadPriority, const char *_Nullable threadName, std::uint64_t queueAddress, std::string queueLabel, std::vector addresses); -@interface SentryProfilerMocks : NSObject - -@end - NS_ASSUME_NONNULL_END diff --git a/SentryTestUtils/SentryProfilerMocks.mm b/SentryTestUtils/SentryProfilerMocks.mm index 6234b52dcac..6e3cedd7200 100644 --- a/SentryTestUtils/SentryProfilerMocks.mm +++ b/SentryTestUtils/SentryProfilerMocks.mm @@ -22,7 +22,3 @@ return backtrace; } - -@implementation SentryProfilerMocks - -@end From cc01579326f57fe953835bb5e35f789b36b7a6a1 Mon Sep 17 00:00:00 2001 From: Andrew McKnight Date: Thu, 6 Jul 2023 22:27:01 -0800 Subject: [PATCH 11/15] fix tvos build --- SentryTestUtils/SentryProfilerMocks.h | 8 +++++++- SentryTestUtils/SentryProfilerMocks.mm | 4 ++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/SentryTestUtils/SentryProfilerMocks.h b/SentryTestUtils/SentryProfilerMocks.h index 1ada1db7b6a..b5ff0f9cf69 100644 --- a/SentryTestUtils/SentryProfilerMocks.h +++ b/SentryTestUtils/SentryProfilerMocks.h @@ -1,6 +1,10 @@ -#import "SentryBacktrace.hpp" +#import "SentryProfilingConditionals.h" #import +#if SENTRY_TARGET_PROFILING_SUPPORTED + +# import "SentryBacktrace.hpp" + using namespace sentry::profiling; NS_ASSUME_NONNULL_BEGIN @@ -10,3 +14,5 @@ Backtrace mockBacktrace(thread::TIDType threadID, const int threadPriority, std::vector addresses); NS_ASSUME_NONNULL_END + +#endif // SENTRY_TARGET_PROFILING_SUPPORTED diff --git a/SentryTestUtils/SentryProfilerMocks.mm b/SentryTestUtils/SentryProfilerMocks.mm index 6e3cedd7200..b7c662980d1 100644 --- a/SentryTestUtils/SentryProfilerMocks.mm +++ b/SentryTestUtils/SentryProfilerMocks.mm @@ -1,5 +1,7 @@ #import "SentryProfilerMocks.h" +#if SENTRY_TARGET_PROFILING_SUPPORTED + Backtrace mockBacktrace(thread::TIDType threadID, const int threadPriority, const char *threadName, std::uint64_t queueAddress, std::string queueLabel, std::vector addresses) @@ -22,3 +24,5 @@ return backtrace; } + +#endif // SENTRY_TARGET_PROFILING_SUPPORTED From 6679a960323842ef752b079e13c332acd74079a0 Mon Sep 17 00:00:00 2001 From: Andrew McKnight Date: Thu, 6 Jul 2023 22:43:24 -0800 Subject: [PATCH 12/15] fix tvos build again --- .../SentryProfilerMocksSwiftCompatible.h | 5 +++++ .../SentryProfilerMocksSwiftCompatible.mm | 13 +++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/SentryTestUtils/SentryProfilerMocksSwiftCompatible.h b/SentryTestUtils/SentryProfilerMocksSwiftCompatible.h index 022f01404a5..bc038ac73d2 100644 --- a/SentryTestUtils/SentryProfilerMocksSwiftCompatible.h +++ b/SentryTestUtils/SentryProfilerMocksSwiftCompatible.h @@ -1,5 +1,8 @@ +#import "SentryProfilingConditionals.h" #import +#if SENTRY_TARGET_PROFILING_SUPPORTED + @class SentryProfilerState; NS_ASSUME_NONNULL_BEGIN @@ -21,3 +24,5 @@ NS_ASSUME_NONNULL_BEGIN @end NS_ASSUME_NONNULL_END + +#endif // SENTRY_TARGET_PROFILING_SUPPORTED diff --git a/SentryTestUtils/SentryProfilerMocksSwiftCompatible.mm b/SentryTestUtils/SentryProfilerMocksSwiftCompatible.mm index b9d4fe29165..8a7687349e3 100644 --- a/SentryTestUtils/SentryProfilerMocksSwiftCompatible.mm +++ b/SentryTestUtils/SentryProfilerMocksSwiftCompatible.mm @@ -1,8 +1,11 @@ #import "SentryProfilerMocksSwiftCompatible.h" -#import "SentryCurrentDate.h" -#import "SentryProfilerMocks.h" -#import "SentryProfilerState+ObjCpp.h" -#include + +#if SENTRY_TARGET_PROFILING_SUPPORTED + +# import "SentryCurrentDate.h" +# import "SentryProfilerMocks.h" +# import "SentryProfilerState+ObjCpp.h" +# include using namespace std; @@ -30,3 +33,5 @@ + (void)appendMockBacktraceToState:(SentryProfilerState *)state } @end + +#endif // SENTRY_TARGET_PROFILING_SUPPORTED From afa102744f6a37750984d795ca3eed23b4635cf4 Mon Sep 17 00:00:00 2001 From: Andrew McKnight Date: Thu, 6 Jul 2023 22:57:13 -0800 Subject: [PATCH 13/15] for real though, fix tvos builds --- Sources/Sentry/include/SentryProfilerState+ObjCpp.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Sources/Sentry/include/SentryProfilerState+ObjCpp.h b/Sources/Sentry/include/SentryProfilerState+ObjCpp.h index 20f8f6f0ce6..6fadeaee19b 100644 --- a/Sources/Sentry/include/SentryProfilerState+ObjCpp.h +++ b/Sources/Sentry/include/SentryProfilerState+ObjCpp.h @@ -1,5 +1,9 @@ -#import "SentryBacktrace.hpp" -#import "SentryProfilerState.h" +#import "SentryProfilingConditionals.h" + +#if SENTRY_TARGET_PROFILING_SUPPORTED + +# import "SentryBacktrace.hpp" +# import "SentryProfilerState.h" /* * This extension defines C++ interface on SentryProfilerState that is not able to be imported into @@ -12,3 +16,5 @@ SentryProfilerState () - (void)appendBacktrace:(const sentry::profiling::Backtrace &)backtrace; @end + +#endif // SENTRY_TARGET_PROFILING_SUPPORTED From 905cf129a124edd31d1fe427d849bfcd4ccca8ff Mon Sep 17 00:00:00 2001 From: Andrew McKnight Date: Fri, 7 Jul 2023 11:42:13 -0800 Subject: [PATCH 14/15] split test and private header --- Sentry.xcodeproj/project.pbxproj | 10 ++++++---- Sources/Sentry/SentryProfiler.mm | 4 +++- ...Profiler+Test.h => SentryProfiler+Private.h} | 6 +----- .../SentryProfilerTests/SentryProfilerTests.mm | 1 + Tests/SentryTests/SentryProfiler+Test.h | 17 +++++++++++++++++ Tests/SentryTests/SentryTests-Bridging-Header.h | 1 + 6 files changed, 29 insertions(+), 10 deletions(-) rename Sources/Sentry/include/{SentryProfiler+Test.h => SentryProfiler+Private.h} (83%) create mode 100644 Tests/SentryTests/SentryProfiler+Test.h diff --git a/Sentry.xcodeproj/project.pbxproj b/Sentry.xcodeproj/project.pbxproj index f27e0aadb9f..4847f1f29ff 100644 --- a/Sentry.xcodeproj/project.pbxproj +++ b/Sentry.xcodeproj/project.pbxproj @@ -641,7 +641,7 @@ 8454CF8D293EAF9A006AC140 /* SentryMetricProfiler.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8454CF8B293EAF9A006AC140 /* SentryMetricProfiler.mm */; }; 849AC40029E0C1FF00889C16 /* SentryFormatterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849AC3FF29E0C1FF00889C16 /* SentryFormatterTests.swift */; }; 84A5D75B29D5170700388BFA /* TimeInterval+Sentry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84A5D75A29D5170700388BFA /* TimeInterval+Sentry.swift */; }; - 84A888FD28D9B11700C51DFD /* SentryProfiler+Test.h in Headers */ = {isa = PBXBuildFile; fileRef = 84A888FC28D9B11700C51DFD /* SentryProfiler+Test.h */; }; + 84A888FD28D9B11700C51DFD /* SentryProfiler+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 84A888FC28D9B11700C51DFD /* SentryProfiler+Private.h */; }; 84A8891C28DBD28900C51DFD /* SentryDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = 84A8891A28DBD28900C51DFD /* SentryDevice.h */; }; 84A8891D28DBD28900C51DFD /* SentryDevice.mm in Sources */ = {isa = PBXBuildFile; fileRef = 84A8891B28DBD28900C51DFD /* SentryDevice.mm */; }; 84A8892128DBD8D600C51DFD /* SentryDeviceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 84A8892028DBD8D600C51DFD /* SentryDeviceTests.mm */; }; @@ -1534,6 +1534,7 @@ 84281C4C2A579A0C00EE88F2 /* SentryProfilerMocksSwiftCompatible.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SentryProfilerMocksSwiftCompatible.h; sourceTree = ""; }; 84281C4D2A579A0C00EE88F2 /* SentryProfilerMocksSwiftCompatible.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SentryProfilerMocksSwiftCompatible.mm; sourceTree = ""; }; 84281C642A57D36100EE88F2 /* SentryProfilerState+ObjCpp.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "SentryProfilerState+ObjCpp.h"; path = "../include/SentryProfilerState+ObjCpp.h"; sourceTree = ""; }; + 84281C652A58A16500EE88F2 /* SentryProfiler+Test.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SentryProfiler+Test.h"; sourceTree = ""; }; 8431EE5A29ADB8EA00D8DC56 /* SentryTimeTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryTimeTests.m; sourceTree = ""; }; 8431EFD929B27B1100D8DC56 /* SentryProfilerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SentryProfilerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 8431EFDA29B27B1200D8DC56 /* SentryTests copy-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "SentryTests copy-Info.plist"; path = "/Users/andrewmcknight/Code/organization/getsentry/repos/public/sentry-cocoa/SentryTests copy-Info.plist"; sourceTree = ""; }; @@ -1581,7 +1582,7 @@ 849472842971C41A002603DE /* SentryNSTimerFactoryTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryNSTimerFactoryTest.swift; sourceTree = ""; }; 849AC3FF29E0C1FF00889C16 /* SentryFormatterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SentryFormatterTests.swift; sourceTree = ""; }; 84A5D75A29D5170700388BFA /* TimeInterval+Sentry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TimeInterval+Sentry.swift"; sourceTree = ""; }; - 84A888FC28D9B11700C51DFD /* SentryProfiler+Test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "SentryProfiler+Test.h"; path = "Sources/Sentry/include/SentryProfiler+Test.h"; sourceTree = SOURCE_ROOT; }; + 84A888FC28D9B11700C51DFD /* SentryProfiler+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "SentryProfiler+Private.h"; path = "Sources/Sentry/include/SentryProfiler+Private.h"; sourceTree = SOURCE_ROOT; }; 84A8891A28DBD28900C51DFD /* SentryDevice.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryDevice.h; path = include/SentryDevice.h; sourceTree = ""; }; 84A8891B28DBD28900C51DFD /* SentryDevice.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SentryDevice.mm; sourceTree = ""; }; 84A8892028DBD8D600C51DFD /* SentryDeviceTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SentryDeviceTests.mm; sourceTree = ""; }; @@ -2223,6 +2224,7 @@ isa = PBXGroup; children = ( 7B3878E92490D90400EBDEA2 /* SentryClient+TestInit.h */, + 84281C652A58A16500EE88F2 /* SentryProfiler+Test.h */, 7B569DFE2590EEF600B653FC /* SentryScope+Equality.h */, 7B569E052590F04700B653FC /* SentryScope+Properties.h */, 7B9421C4260CA393001F9349 /* SentrySDK+Tests.h */, @@ -3110,7 +3112,7 @@ 8454CF8B293EAF9A006AC140 /* SentryMetricProfiler.mm */, 03F84D1127DD414C008FE43F /* SentryProfiler.h */, 03F84D2B27DD4191008FE43F /* SentryProfiler.mm */, - 84A888FC28D9B11700C51DFD /* SentryProfiler+Test.h */, + 84A888FC28D9B11700C51DFD /* SentryProfiler+Private.h */, 0354A22A2A134D9C003C3A04 /* SentryProfilerState.h */, 84281C642A57D36100EE88F2 /* SentryProfilerState+ObjCpp.h */, 84281C422A578E5600EE88F2 /* SentryProfilerState.mm */, @@ -3489,7 +3491,7 @@ 7BAF3DD92440AEC8008A5414 /* SentryRequestManager.h in Headers */, 627E7589299F6FE40085504D /* SentryInternalDefines.h in Headers */, 7BE3C77B2446111500A38442 /* SentryRateLimitParser.h in Headers */, - 84A888FD28D9B11700C51DFD /* SentryProfiler+Test.h in Headers */, + 84A888FD28D9B11700C51DFD /* SentryProfiler+Private.h in Headers */, 7D0637032382B34300B30749 /* SentryScope.h in Headers */, 03F84D2727DD414C008FE43F /* SentryMachLogging.hpp in Headers */, 0356A570288B4612008BF593 /* SentryProfilesSampler.h in Headers */, diff --git a/Sources/Sentry/SentryProfiler.mm b/Sources/Sentry/SentryProfiler.mm index bdecb656292..344197b6c47 100644 --- a/Sources/Sentry/SentryProfiler.mm +++ b/Sources/Sentry/SentryProfiler.mm @@ -1,4 +1,4 @@ -#import "SentryProfiler+Test.h" +#import "SentryProfiler+Private.h" #if SENTRY_TARGET_PROFILING_SUPPORTED # import "NSDate+SentryExtras.h" @@ -528,6 +528,8 @@ - (BOOL)isRunning return _profiler->isSampling(); } +# pragma mark - Testing helpers + # if defined(TEST) || defined(TESTCI) + (SentryProfiler *)getCurrentProfiler { diff --git a/Sources/Sentry/include/SentryProfiler+Test.h b/Sources/Sentry/include/SentryProfiler+Private.h similarity index 83% rename from Sources/Sentry/include/SentryProfiler+Test.h rename to Sources/Sentry/include/SentryProfiler+Private.h index b8b778eb500..6f402ca75e4 100644 --- a/Sources/Sentry/include/SentryProfiler+Test.h +++ b/Sources/Sentry/include/SentryProfiler+Private.h @@ -21,12 +21,8 @@ SentryProfiler () @property (strong, nonatomic) SentryProfilerState *_state; -# if defined(TEST) || defined(TESTCI) -+ (SentryProfiler *)getCurrentProfiler; -# endif // defined(TEST) || defined(TESTCI) - @end NS_ASSUME_NONNULL_END -#endif +#endif // SENTRY_TARGET_PROFILING_SUPPORTED diff --git a/Tests/SentryProfilerTests/SentryProfilerTests.mm b/Tests/SentryProfilerTests/SentryProfilerTests.mm index 35b56021a08..bbad9da551b 100644 --- a/Tests/SentryProfilerTests/SentryProfilerTests.mm +++ b/Tests/SentryProfilerTests/SentryProfilerTests.mm @@ -2,6 +2,7 @@ #import "SentryHub+TestInit.h" #import "SentryId.h" #import "SentryProfileTimeseries.h" +#import "SentryProfiler+Private.h" #import "SentryProfiler+Test.h" #import "SentryProfilerMocks.h" #import "SentryProfilerState+ObjCpp.h" diff --git a/Tests/SentryTests/SentryProfiler+Test.h b/Tests/SentryTests/SentryProfiler+Test.h new file mode 100644 index 00000000000..83a7655ed1b --- /dev/null +++ b/Tests/SentryTests/SentryProfiler+Test.h @@ -0,0 +1,17 @@ +#import "SentryProfiler.h" +#import "SentryProfilingConditionals.h" + +#if SENTRY_TARGET_PROFILING_SUPPORTED + +NS_ASSUME_NONNULL_BEGIN + +@interface +SentryProfiler () + ++ (SentryProfiler *)getCurrentProfiler; + +@end + +NS_ASSUME_NONNULL_END + +#endif // SENTRY_TARGET_PROFILING_SUPPORTED diff --git a/Tests/SentryTests/SentryTests-Bridging-Header.h b/Tests/SentryTests/SentryTests-Bridging-Header.h index 6b37ed9c57a..a8e7460efa0 100644 --- a/Tests/SentryTests/SentryTests-Bridging-Header.h +++ b/Tests/SentryTests/SentryTests-Bridging-Header.h @@ -124,6 +124,7 @@ #import "SentryPerformanceTracker.h" #import "SentryPerformanceTrackingIntegration.h" #import "SentryPredicateDescriptor.h" +#import "SentryProfiler+Private.h" #import "SentryProfiler+Test.h" #import "SentryProfilerMocksSwiftCompatible.h" #import "SentryProfilerState.h" From dea16768aa585bbd546b765309e8bfaae0cf04ce Mon Sep 17 00:00:00 2001 From: Andrew McKnight Date: Fri, 7 Jul 2023 16:00:36 -0800 Subject: [PATCH 15/15] remove unnecessary stall loop --- Tests/SentryTests/Transaction/SentryTracerObjCTests.m | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Tests/SentryTests/Transaction/SentryTracerObjCTests.m b/Tests/SentryTests/Transaction/SentryTracerObjCTests.m index 8378315cfb6..1f04fb2a853 100644 --- a/Tests/SentryTests/Transaction/SentryTracerObjCTests.m +++ b/Tests/SentryTests/Transaction/SentryTracerObjCTests.m @@ -76,12 +76,6 @@ - (void)testConcurrentTracerProfiling configuration.waitForChildren = YES; }]]; - // force some samples to be taken by the profiler - NSMutableString *string = [NSMutableString string]; - for (int i = 0; i < 100000; i++) { - [string appendString:@"a"]; - } - XCTestExpectation *exp = [self expectationWithDescription:@"finishes tracers"]; dispatch_after( dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{