From f23238084d1d9e04032f02cea216b8211a747795 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Wed, 27 Apr 2022 15:16:22 +0200 Subject: [PATCH 01/28] feat: build macOS SDK as a dylib from sentry-cocoa --- .github/workflows/ci.yml | 12 ++ .github/workflows/sdk.yml | 2 +- .gitignore | 2 + Directory.Build.targets | 26 ++- .../Plugins/macOS/Sentry/Sentry.dylib.meta | 81 ++++++++ .../Plugins/macOS/SentryNativeBridge.m | 173 ++++++++++++++++++ .../Plugins/macOS/SentryNativeBridge.m.meta | 86 +++++++++ 7 files changed, 378 insertions(+), 4 deletions(-) create mode 100644 package-dev/Plugins/macOS/Sentry/Sentry.dylib.meta create mode 100644 package-dev/Plugins/macOS/SentryNativeBridge.m create mode 100644 package-dev/Plugins/macOS/SentryNativeBridge.m.meta diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8de87f4a8..cc5593e93 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,6 +33,12 @@ jobs: target: iOS runsOn: macos-latest + macos-sdk: + uses: ./.github/workflows/sdk.yml + with: + target: macOS + runsOn: macos-latest + windows-sdk: uses: ./.github/workflows/sdk.yml with: @@ -90,6 +96,12 @@ jobs: path: package-dev/Plugins/iOS wait-timeout: 3600 + - uses: vaind/download-artifact@989a39a417730897d098ab11c34e49ac4e13ed70 + with: + name: macOS-sdk + path: package-dev/Plugins/macOS + wait-timeout: 3600 + - uses: vaind/download-artifact@989a39a417730897d098ab11c34e49ac4e13ed70 with: name: Windows-sdk diff --git a/.github/workflows/sdk.yml b/.github/workflows/sdk.yml index a27f15247..6cd9c37a1 100644 --- a/.github/workflows/sdk.yml +++ b/.github/workflows/sdk.yml @@ -40,7 +40,7 @@ jobs: run: | if [[ "${{ inputs.target }}" == "Android" ]]; then submodules="modules/sentry-java" - elif [[ "${{ inputs.target }}" == "iOS" ]]; then + elif [[ "${{ inputs.target }}" == "iOS" || "${{ inputs.target }}" == "macOS" ]]; then submodules="modules/sentry-cocoa" else submodules="modules/sentry-native" diff --git a/.gitignore b/.gitignore index 00c8bf92c..0102b4cd4 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,8 @@ package-dev/**/*.meta package-dev/**/*.framework package-dev/**/*.pdb package-dev/**/*.xml +package-dev/**/*.dylib +package-dev/**/*.dSYM package-dev/**/TestSentryOptions.json package-dev/Tests/Editor/TestFiles/ package-dev/Plugins/*/Sentry/crashpad_handler* diff --git a/Directory.Build.targets b/Directory.Build.targets index 5d0932555..d8af15681 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -15,10 +15,11 @@ ../../artifacts/test/playmode/results.xml ../../artifacts/test/editmode/results.xml $(RepoRoot)package-dev/Plugins/ - + $(RepoRoot)modules/sentry-cocoa/ $(SentryArtifactsDestination)/iOS/Device/Sentry.framework/ $(SentryArtifactsDestination)/iOS/Simulator/Sentry.framework/ + $(SentryArtifactsDestination)/macOS/Sentry/ $(RepoRoot)modules/sentry-java/ $(SentryArtifactsDestination)/Android/Sentry/ @@ -107,6 +108,7 @@ Expected to exist: + @@ -117,6 +119,12 @@ Expected to exist: + + + + + + @@ -143,8 +151,20 @@ Expected to exist: - - + + + + + + + + + + + + diff --git a/package-dev/Plugins/macOS/Sentry/Sentry.dylib.meta b/package-dev/Plugins/macOS/Sentry/Sentry.dylib.meta new file mode 100644 index 000000000..5ee8d51df --- /dev/null +++ b/package-dev/Plugins/macOS/Sentry/Sentry.dylib.meta @@ -0,0 +1,81 @@ +fileFormatVersion: 2 +guid: 23251176c1b104465a1b141cf7aabd2b +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 0 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + Exclude iOS: 1 + - first: + Android: Android + second: + enabled: 0 + settings: + CPU: ARMv7 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: x86_64 + - first: + Standalone: OSXUniversal + second: + enabled: 1 + settings: + CPU: AnyCPU + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: x86 + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: x86_64 + - first: + iPhone: iOS + second: + enabled: 0 + settings: + AddToEmbeddedBinaries: false + CPU: AnyCPU + CompileFlags: + FrameworkDependencies: + userData: + assetBundleName: + assetBundleVariant: diff --git a/package-dev/Plugins/macOS/SentryNativeBridge.m b/package-dev/Plugins/macOS/SentryNativeBridge.m new file mode 100644 index 000000000..ffbec3520 --- /dev/null +++ b/package-dev/Plugins/macOS/SentryNativeBridge.m @@ -0,0 +1,173 @@ +#import +#include + +@class SentryOptions; +@class SentrySDK; + +static int loadStatus = 0; // 0 = unitialized; 1 = dylib loaded successfully; -1 = dylib load error +static void *dylib; +static Class sdkClass; +static Class optionsClass; + +int +LoadSentryDylib() +{ + if (!loadStatus) { + loadStatus = -1; // init to "error" + do { + dylib = dlopen("@executable_path/../PlugIns/Sentry.dylib", RTLD_LAZY); + if (!dylib) { + NSLog(@"Couldn't load Sentry.dylib - dlopen() failed"); + break; + } + + sdkClass = dlsym(dylib, "OBJC_CLASS_$_SentrySDK"); + if (!sdkClass) { + NSLog(@"Couldn't load SentrySDK class from the dynamic library"); + break; + } + + optionsClass = dlsym(dylib, "OBJC_CLASS_$_SentryOptions"); + if (!optionsClass) { + NSLog(@"Couldn't load SentryOptions class from the dynamic library"); + break; + } + + // everything above passed succesfully - mark as loaded + loadStatus = 1; + } while (false); + } + return loadStatus; +} + +// TODO expose options setup & init +// SentryOptions *options = [[optionsClass alloc] init]; +// [options +// setValue: +// @"https://94677106febe46b88b9b9ae5efd18a00@o447951.ingest.sentry.io/5439417" +// forKey:@"dsn"]; +// [options setValue:[NSNumber numberWithBool:YES] forKey:@"debug"]; + +// [sdkClass startWithOptionsObject:options]; + +// int CrashedLastRun() { +// return [SentrySDK crashedLastRun] ? 1 : 0; +// } + +// void Close() { +// [SentrySDK close]; +// } + +// void SentryNativeBridgeAddBreadcrumb(const char* timestamp, const char* message, const char* +// type, const char* category, int level) { +// if (timestamp == NULL && message == NULL && type == NULL && category == NULL) { +// return; +// } + +// [SentrySDK configureScope:^(SentryScope * scope) { +// SentryBreadcrumb *breadcrumb = [[SentryBreadcrumb alloc] init]; + +// if (timestamp != NULL) { +// NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; +// [dateFormatter setDateFormat:NSCalendarIdentifierISO8601]; +// breadcrumb.timestamp = [dateFormatter dateFromString:[NSString +// stringWithCString:timestamp encoding:NSUTF8StringEncoding]]; +// } + +// if (message != NULL) { +// breadcrumb.message = [NSString stringWithCString:message +// encoding:NSUTF8StringEncoding]; +// } + +// if (type != NULL) { +// breadcrumb.type = [NSString stringWithCString:type +// encoding:NSUTF8StringEncoding]; +// } + +// if (category != NULL) { +// breadcrumb.category = [NSString stringWithCString:category +// encoding:NSUTF8StringEncoding]; +// } + +// breadcrumb.level = level; + +// [scope addBreadcrumb:breadcrumb]; +// }]; +// } + +// void SentryNativeBridgeSetExtra(const char* key, const char* value) { +// if (key == NULL) { +// return; +// } + +// [SentrySDK configureScope:^(SentryScope * scope) { +// if (value != NULL) { +// [scope setExtraValue:[NSString stringWithUTF8String:value] forKey:[NSString +// stringWithUTF8String:key]]; +// } else { +// [scope removeExtraForKey:[NSString stringWithUTF8String:key]]; +// } +// }]; +// } + +// void SentryNativeBridgeSetTag(const char* key, const char* value) { +// if (key == NULL) { +// return; +// } + +// [SentrySDK configureScope:^(SentryScope * scope) { +// if (value != NULL) { +// [scope setTagValue:[NSString stringWithUTF8String:value] forKey:[NSString +// stringWithUTF8String:key]]; +// } else { +// [scope removeTagForKey:[NSString stringWithUTF8String:key]]; +// } +// }]; +// } + +// void SentryNativeBridgeUnsetTag(const char* key) { +// if (key == NULL) { +// return; +// } + +// [SentrySDK configureScope:^(SentryScope * scope) { +// [scope removeTagForKey:[NSString stringWithUTF8String:key]]; +// }]; +// } + +// void SentryNativeBridgeSetUser(const char* email, const char* userId, const char* ipAddress, +// const char* username) { +// if (email == NULL && userId == NULL && ipAddress == NULL && username == NULL) { +// return; +// } + +// [SentrySDK configureScope:^(SentryScope * scope) { +// SentryUser *user = [[SentryUser alloc] init]; + +// if (email != NULL) { +// user.email = [NSString stringWithCString:email encoding:NSUTF8StringEncoding]; +// } + +// if (userId != NULL) { +// user.userId = [NSString stringWithCString:userId encoding:NSUTF8StringEncoding]; +// } + +// if (ipAddress != NULL) { +// user.ipAddress = [NSString stringWithCString:ipAddress +// encoding:NSUTF8StringEncoding]; +// } + +// if (username != NULL) { +// user.username = [NSString stringWithCString:username +// encoding:NSUTF8StringEncoding]; +// } + +// [scope setUser:user]; +// }]; +// } + +// void SentryNativeBridgeUnsetUser() { +// [SentrySDK configureScope:^(SentryScope * scope) { +// [scope setUser:nil]; +// }]; +// } diff --git a/package-dev/Plugins/macOS/SentryNativeBridge.m.meta b/package-dev/Plugins/macOS/SentryNativeBridge.m.meta new file mode 100644 index 000000000..43069dbda --- /dev/null +++ b/package-dev/Plugins/macOS/SentryNativeBridge.m.meta @@ -0,0 +1,86 @@ +fileFormatVersion: 2 +guid: 17913db96b84047a990f83b3c11b3117 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 0 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + Exclude iOS: 1 + - first: + Android: Android + second: + enabled: 0 + settings: + CPU: ARMv7 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 1 + settings: + CPU: AnyCPU + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + - first: + iPhone: iOS + second: + enabled: 0 + settings: + AddToEmbeddedBinaries: false + CPU: AnyCPU + CompileFlags: + FrameworkDependencies: + - first: + tvOS: tvOS + second: + enabled: 1 + settings: {} + userData: + assetBundleName: + assetBundleVariant: From 1bb39ca1613dfbaf9d20f1bae143a48cb40adec5 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Wed, 27 Apr 2022 16:03:23 +0200 Subject: [PATCH 02/28] refactor: reuse iOS native bridge for macOS --- .../Plugins/macOS/SentryNativeBridge.m | 36 +++++++++---------- package-dev/Runtime/Sentry.Unity.iOS.dll.meta | 19 +++++----- package-dev/Runtime/SentryInitialization.cs | 10 +++--- ...copeObserver.cs => NativeScopeObserver.cs} | 4 +-- .../{SentryNativeIos.cs => SentryNative.cs} | 8 ++--- ...erTests.cs => NativeScopeObserverTests.cs} | 4 +-- .../SentryNativeIosTests.cs | 14 ++++---- 7 files changed, 48 insertions(+), 47 deletions(-) rename src/Sentry.Unity.iOS/{IosNativeScopeObserver.cs => NativeScopeObserver.cs} (92%) rename src/Sentry.Unity.iOS/{SentryNativeIos.cs => SentryNative.cs} (82%) rename test/Sentry.Unity.iOS.Tests/{IosNativeScopeObserverTests.cs => NativeScopeObserverTests.cs} (84%) diff --git a/package-dev/Plugins/macOS/SentryNativeBridge.m b/package-dev/Plugins/macOS/SentryNativeBridge.m index ffbec3520..7055f04f1 100644 --- a/package-dev/Plugins/macOS/SentryNativeBridge.m +++ b/package-dev/Plugins/macOS/SentryNativeBridge.m @@ -50,16 +50,16 @@ // [sdkClass startWithOptionsObject:options]; -// int CrashedLastRun() { +int CrashedLastRun() { // return [SentrySDK crashedLastRun] ? 1 : 0; -// } +} -// void Close() { +void Close() { // [SentrySDK close]; -// } +} -// void SentryNativeBridgeAddBreadcrumb(const char* timestamp, const char* message, const char* -// type, const char* category, int level) { +void SentryNativeBridgeAddBreadcrumb(const char* timestamp, const char* message, const char* +type, const char* category, int level) { // if (timestamp == NULL && message == NULL && type == NULL && category == NULL) { // return; // } @@ -93,9 +93,9 @@ // [scope addBreadcrumb:breadcrumb]; // }]; -// } +} -// void SentryNativeBridgeSetExtra(const char* key, const char* value) { +void SentryNativeBridgeSetExtra(const char* key, const char* value) { // if (key == NULL) { // return; // } @@ -108,9 +108,9 @@ // [scope removeExtraForKey:[NSString stringWithUTF8String:key]]; // } // }]; -// } +} -// void SentryNativeBridgeSetTag(const char* key, const char* value) { +void SentryNativeBridgeSetTag(const char* key, const char* value) { // if (key == NULL) { // return; // } @@ -123,9 +123,9 @@ // [scope removeTagForKey:[NSString stringWithUTF8String:key]]; // } // }]; -// } +} -// void SentryNativeBridgeUnsetTag(const char* key) { +void SentryNativeBridgeUnsetTag(const char* key) { // if (key == NULL) { // return; // } @@ -133,10 +133,10 @@ // [SentrySDK configureScope:^(SentryScope * scope) { // [scope removeTagForKey:[NSString stringWithUTF8String:key]]; // }]; -// } +} -// void SentryNativeBridgeSetUser(const char* email, const char* userId, const char* ipAddress, -// const char* username) { +void SentryNativeBridgeSetUser(const char* email, const char* userId, const char* ipAddress, +const char* username) { // if (email == NULL && userId == NULL && ipAddress == NULL && username == NULL) { // return; // } @@ -164,10 +164,10 @@ // [scope setUser:user]; // }]; -// } +} -// void SentryNativeBridgeUnsetUser() { +void SentryNativeBridgeUnsetUser() { // [SentrySDK configureScope:^(SentryScope * scope) { // [scope setUser:nil]; // }]; -// } +} diff --git a/package-dev/Runtime/Sentry.Unity.iOS.dll.meta b/package-dev/Runtime/Sentry.Unity.iOS.dll.meta index 9ecb7dc94..a279b2b75 100644 --- a/package-dev/Runtime/Sentry.Unity.iOS.dll.meta +++ b/package-dev/Runtime/Sentry.Unity.iOS.dll.meta @@ -18,10 +18,11 @@ PluginImporter: settings: Exclude Android: 1 Exclude Editor: 0 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude Win: 1 - Exclude Win64: 1 + Exclude Linux64: 0 + Exclude OSXUniversal: 0 + Exclude WebGL: 1 + Exclude Win: 0 + Exclude Win64: 0 Exclude iOS: 0 - first: Android: Android @@ -45,25 +46,25 @@ PluginImporter: - first: Standalone: Linux64 second: - enabled: 0 + enabled: 1 settings: CPU: None - first: Standalone: OSXUniversal second: - enabled: 0 + enabled: 1 settings: - CPU: None + CPU: AnyCPU - first: Standalone: Win second: - enabled: 0 + enabled: 1 settings: CPU: None - first: Standalone: Win64 second: - enabled: 0 + enabled: 1 settings: CPU: None - first: diff --git a/package-dev/Runtime/SentryInitialization.cs b/package-dev/Runtime/SentryInitialization.cs index 2c6381e62..6e1662560 100644 --- a/package-dev/Runtime/SentryInitialization.cs +++ b/package-dev/Runtime/SentryInitialization.cs @@ -1,6 +1,6 @@ #if !UNITY_EDITOR -#if UNITY_IOS -#define SENTRY_NATIVE_IOS +#if UNITY_IOS || (UNITY_STANDALONE_OSX && ENABLE_IL2CPP) +#define SENTRY_NATIVE_COCOA #elif UNITY_ANDROID #define SENTRY_NATIVE_ANDROID #elif UNITY_STANDALONE_WIN && ENABLE_IL2CPP @@ -13,7 +13,7 @@ using UnityEngine; using UnityEngine.Scripting; -#if SENTRY_NATIVE_IOS +#if SENTRY_NATIVE_COCOA using Sentry.Unity.iOS; #elif UNITY_ANDROID using Sentry.Unity.Android; @@ -37,8 +37,8 @@ public static void Init() { var sentryUnityInfo = new SentryUnityInfo(); -#if SENTRY_NATIVE_IOS - SentryNativeIos.Configure(options); +#if SENTRY_NATIVE_COCOA + SentryNativeCocoa.Configure(options); #elif SENTRY_NATIVE_ANDROID SentryNativeAndroid.Configure(options, sentryUnityInfo); #elif SENTRY_NATIVE_WINDOWS diff --git a/src/Sentry.Unity.iOS/IosNativeScopeObserver.cs b/src/Sentry.Unity.iOS/NativeScopeObserver.cs similarity index 92% rename from src/Sentry.Unity.iOS/IosNativeScopeObserver.cs rename to src/Sentry.Unity.iOS/NativeScopeObserver.cs index 87a5a5824..0c82a5661 100644 --- a/src/Sentry.Unity.iOS/IosNativeScopeObserver.cs +++ b/src/Sentry.Unity.iOS/NativeScopeObserver.cs @@ -2,9 +2,9 @@ namespace Sentry.Unity.iOS { - public class IosNativeScopeObserver : ScopeObserver + public class NativeScopeObserver : ScopeObserver { - public IosNativeScopeObserver(SentryOptions options) : base("iOS", options) { } + public NativeScopeObserver(string name, SentryOptions options) : base(name, options) { } public override void AddBreadcrumbImpl(Breadcrumb breadcrumb) { diff --git a/src/Sentry.Unity.iOS/SentryNativeIos.cs b/src/Sentry.Unity.iOS/SentryNative.cs similarity index 82% rename from src/Sentry.Unity.iOS/SentryNativeIos.cs rename to src/Sentry.Unity.iOS/SentryNative.cs index e9780262d..d37969582 100644 --- a/src/Sentry.Unity.iOS/SentryNativeIos.cs +++ b/src/Sentry.Unity.iOS/SentryNative.cs @@ -4,19 +4,19 @@ namespace Sentry.Unity.iOS { /// - /// Access to the Sentry native support on iOS. + /// Access to the Sentry native support on iOS/macOS. /// - public static class SentryNativeIos + public static class SentryNativeCocoa { /// - /// Configures the native Android support. + /// Configures the native support. /// /// The Sentry Unity options to use. public static void Configure(SentryUnityOptions options) { if (options.IosNativeSupportEnabled) { - options.ScopeObserver = new IosNativeScopeObserver(options); + options.ScopeObserver = new NativeScopeObserver("iOS", options); options.EnableScopeSync = true; options.CrashedLastRun = () => { diff --git a/test/Sentry.Unity.iOS.Tests/IosNativeScopeObserverTests.cs b/test/Sentry.Unity.iOS.Tests/NativeScopeObserverTests.cs similarity index 84% rename from test/Sentry.Unity.iOS.Tests/IosNativeScopeObserverTests.cs rename to test/Sentry.Unity.iOS.Tests/NativeScopeObserverTests.cs index 6ee396381..55e038905 100644 --- a/test/Sentry.Unity.iOS.Tests/IosNativeScopeObserverTests.cs +++ b/test/Sentry.Unity.iOS.Tests/NativeScopeObserverTests.cs @@ -11,7 +11,7 @@ public void GetTimestamp_ReturnStringConformsToISO8601() { var timestamp = DateTimeOffset.UtcNow; - var timestampString = IosNativeScopeObserver.GetTimestamp(timestamp); + var timestampString = NativeScopeObserver.GetTimestamp(timestamp); var actualTimestamp = DateTimeOffset.ParseExact(timestampString, "o", CultureInfo.InvariantCulture); Assert.AreEqual(timestamp, actualTimestamp); @@ -25,7 +25,7 @@ public void GetTimestamp_ReturnStringConformsToISO8601() [TestCase(BreadcrumbLevel.Critical, 5)] public void GetBreadcrumbLevel_TestCases(BreadcrumbLevel level, int expectedNativeLevel) { - var actualLevel = IosNativeScopeObserver.GetBreadcrumbLevel(level); + var actualLevel = NativeScopeObserver.GetBreadcrumbLevel(level); Assert.AreEqual(actualLevel, expectedNativeLevel); } diff --git a/test/Sentry.Unity.iOS.Tests/SentryNativeIosTests.cs b/test/Sentry.Unity.iOS.Tests/SentryNativeIosTests.cs index 8ad47b9c8..841071d5b 100644 --- a/test/Sentry.Unity.iOS.Tests/SentryNativeIosTests.cs +++ b/test/Sentry.Unity.iOS.Tests/SentryNativeIosTests.cs @@ -2,21 +2,21 @@ namespace Sentry.Unity.iOS.Tests { - public class SentryNativeIosTests + public class SentryNativeCocoaTests { [Test] public void Configure_DefaultConfiguration_SetsScopeObserver() { var options = new SentryUnityOptions(); - SentryNativeIos.Configure(options); - Assert.IsAssignableFrom(options.ScopeObserver); + SentryNativeCocoa.Configure(options); + Assert.IsAssignableFrom(options.ScopeObserver); } [Test] public void Configure_DefaultConfiguration_SetsCrashedLastRun() { var options = new SentryUnityOptions(); - SentryNativeIos.Configure(options); + SentryNativeCocoa.Configure(options); Assert.IsNotNull(options.CrashedLastRun); } @@ -24,7 +24,7 @@ public void Configure_DefaultConfiguration_SetsCrashedLastRun() public void Configure_NativeIosSupportDisabled_ObserverIsNull() { var options = new SentryUnityOptions { IosNativeSupportEnabled = false }; - SentryNativeIos.Configure(options); + SentryNativeCocoa.Configure(options); Assert.Null(options.ScopeObserver); } @@ -32,7 +32,7 @@ public void Configure_NativeIosSupportDisabled_ObserverIsNull() public void Configure_DefaultConfiguration_EnablesScopeSync() { var options = new SentryUnityOptions(); - SentryNativeIos.Configure(options); + SentryNativeCocoa.Configure(options); Assert.True(options.EnableScopeSync); } @@ -40,7 +40,7 @@ public void Configure_DefaultConfiguration_EnablesScopeSync() public void Configure_NativeIosSupportDisabled_DisabledScopeSync() { var options = new SentryUnityOptions { IosNativeSupportEnabled = false }; - SentryNativeIos.Configure(options); + SentryNativeCocoa.Configure(options); Assert.False(options.EnableScopeSync); } } From 08880576f19cf652fa2576b58f233774e053f1f8 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Wed, 27 Apr 2022 16:05:36 +0200 Subject: [PATCH 03/28] feat: add MacosNativeSupportEnabled option --- .../Assets/Resources/Sentry/SentryOptions.asset | 1 + src/Sentry.Unity.Editor/ConfigurationWindow/AdvancedTab.cs | 7 +++++-- .../ScriptableSentryUnityOptionsEditor.cs | 1 + src/Sentry.Unity/ScriptableSentryUnityOptions.cs | 2 ++ src/Sentry.Unity/SentryUnityOptions.cs | 5 +++++ .../ScriptableSentryUnityOptionsTests.cs | 1 + 6 files changed, 15 insertions(+), 2 deletions(-) diff --git a/samples/unity-of-bugs/Assets/Resources/Sentry/SentryOptions.asset b/samples/unity-of-bugs/Assets/Resources/Sentry/SentryOptions.asset index 89d937fc4..00c2aba65 100644 --- a/samples/unity-of-bugs/Assets/Resources/Sentry/SentryOptions.asset +++ b/samples/unity-of-bugs/Assets/Resources/Sentry/SentryOptions.asset @@ -38,6 +38,7 @@ MonoBehaviour: k__BackingField: 1 k__BackingField: 1 k__BackingField: 1 + k__BackingField: 1 k__BackingField: {fileID: 11400000, guid: bda1a724b0875436aade31393b0129c0, type: 2} k__BackingField: 1 diff --git a/src/Sentry.Unity.Editor/ConfigurationWindow/AdvancedTab.cs b/src/Sentry.Unity.Editor/ConfigurationWindow/AdvancedTab.cs index 5d79da5bf..425a34c89 100644 --- a/src/Sentry.Unity.Editor/ConfigurationWindow/AdvancedTab.cs +++ b/src/Sentry.Unity.Editor/ConfigurationWindow/AdvancedTab.cs @@ -59,9 +59,12 @@ internal static void Display(ScriptableSentryUnityOptions options) options.AndroidNativeSupportEnabled); options.WindowsNativeSupportEnabled = EditorGUILayout.Toggle( - new GUIContent("Windows Native Support", "Whether to enable Native Windows support to " + - "capture errors written in languages such as C and C++."), + new GUIContent("Windows Native Support", "Whether to enable native crashes support on Windows."), options.WindowsNativeSupportEnabled); + + options.MacosNativeSupportEnabled = EditorGUILayout.Toggle( + new GUIContent("macOS Native Support", "Whether to enable native crashes support on macOS."), + options.MacosNativeSupportEnabled); } diff --git a/src/Sentry.Unity.Editor/ScriptableSentryUnityOptionsEditor.cs b/src/Sentry.Unity.Editor/ScriptableSentryUnityOptionsEditor.cs index 9d10afee7..d8979336e 100644 --- a/src/Sentry.Unity.Editor/ScriptableSentryUnityOptionsEditor.cs +++ b/src/Sentry.Unity.Editor/ScriptableSentryUnityOptionsEditor.cs @@ -65,6 +65,7 @@ public override void OnInspectorGUI() EditorGUILayout.Toggle("iOS Native Support", options.IosNativeSupportEnabled); EditorGUILayout.Toggle("Android Native Support", options.AndroidNativeSupportEnabled); EditorGUILayout.Toggle("Windows Native Support", options.WindowsNativeSupportEnabled); + EditorGUILayout.Toggle("macOS Native Support", options.MacosNativeSupportEnabled); EditorGUILayout.Space(); EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); diff --git a/src/Sentry.Unity/ScriptableSentryUnityOptions.cs b/src/Sentry.Unity/ScriptableSentryUnityOptions.cs index 2e8146b48..514230d5f 100644 --- a/src/Sentry.Unity/ScriptableSentryUnityOptions.cs +++ b/src/Sentry.Unity/ScriptableSentryUnityOptions.cs @@ -61,6 +61,7 @@ public static string GetConfigPath(string? notDefaultConfigName = null) [field: SerializeField] public bool IosNativeSupportEnabled { get; set; } = true; [field: SerializeField] public bool AndroidNativeSupportEnabled { get; set; } = true; [field: SerializeField] public bool WindowsNativeSupportEnabled { get; set; } = true; + [field: SerializeField] public bool MacosNativeSupportEnabled { get; set; } = true; [field: SerializeField] public ScriptableOptionsConfiguration? OptionsConfiguration { get; set; } @@ -133,6 +134,7 @@ internal SentryUnityOptions ToSentryUnityOptions(bool isBuilding, IApplication? options.IosNativeSupportEnabled = IosNativeSupportEnabled; options.AndroidNativeSupportEnabled = AndroidNativeSupportEnabled; options.WindowsNativeSupportEnabled = WindowsNativeSupportEnabled; + options.MacosNativeSupportEnabled = MacosNativeSupportEnabled; // Because SentryOptions.Debug is used inside the .NET SDK to setup the ConsoleLogger we // need to set it here directly. diff --git a/src/Sentry.Unity/SentryUnityOptions.cs b/src/Sentry.Unity/SentryUnityOptions.cs index 564835f68..434844d10 100644 --- a/src/Sentry.Unity/SentryUnityOptions.cs +++ b/src/Sentry.Unity/SentryUnityOptions.cs @@ -101,6 +101,11 @@ public sealed class SentryUnityOptions : SentryOptions /// public bool WindowsNativeSupportEnabled { get; set; } = true; + /// + /// Whether the SDK should add native support for MacOS + /// + public bool MacosNativeSupportEnabled { get; set; } = true; + public SentryUnityOptions() : this(ApplicationAdapter.Instance, false) { } diff --git a/test/Sentry.Unity.Editor.Tests/ScriptableSentryUnityOptionsTests.cs b/test/Sentry.Unity.Editor.Tests/ScriptableSentryUnityOptionsTests.cs index 153915c08..b01d0882f 100644 --- a/test/Sentry.Unity.Editor.Tests/ScriptableSentryUnityOptionsTests.cs +++ b/test/Sentry.Unity.Editor.Tests/ScriptableSentryUnityOptionsTests.cs @@ -44,6 +44,7 @@ public void ScriptableSentryUnityOptions_Creation_AllPropertiesPresent() StringAssert.Contains("IosNativeSupportEnabled", optionsAsString); StringAssert.Contains("AndroidNativeSupportEnabled", optionsAsString); StringAssert.Contains("WindowsNativeSupportEnabled", optionsAsString); + StringAssert.Contains("MacosNativeSupportEnabled", optionsAsString); StringAssert.Contains("OptionsConfiguration", optionsAsString); StringAssert.Contains("Debug", optionsAsString); StringAssert.Contains("DebugOnlyInEditor", optionsAsString); From 25b0ea41b4efdb815a1d036a29e3e0cfbc175b6d Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Mon, 25 Apr 2022 12:32:30 +0200 Subject: [PATCH 04/28] fix: SmokeTester.cs compilation for iOS --- samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs b/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs index 8a6d936b3..abb641699 100644 --- a/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs +++ b/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs @@ -41,7 +41,7 @@ public void Start() } } -#if UNITY_IOS +#if UNITY_IOS && !UNITY_EDITOR // .NET `Environment.GetCommandLineArgs()` doesn't seem to work on iOS so we get the test arg in Objective-C [DllImport("__Internal", EntryPoint="getTestArgObjectiveC")] private static extern string GetTestArg(); From 7aac1805a3a16ea299cbe112fcd686e06900c55e Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Wed, 27 Apr 2022 17:11:03 +0200 Subject: [PATCH 05/28] feat: extend SentryNativeBridge with macOS support --- package-dev/Plugins/iOS/SentryNativeBridge.m | 9 +- .../Plugins/macOS/SentryNativeBridge.m | 244 ++++++++++-------- .../SentryCocoaBridgeProxy.cs | 7 +- src/Sentry.Unity.iOS/SentryNative.cs | 52 ++-- 4 files changed, 183 insertions(+), 129 deletions(-) diff --git a/package-dev/Plugins/iOS/SentryNativeBridge.m b/package-dev/Plugins/iOS/SentryNativeBridge.m index 4de55d26e..620b94764 100644 --- a/package-dev/Plugins/iOS/SentryNativeBridge.m +++ b/package-dev/Plugins/iOS/SentryNativeBridge.m @@ -2,11 +2,16 @@ NS_ASSUME_NONNULL_BEGIN -int CrashedLastRun() { +// must be here to match the native bridge API for macOS +int SentryNativeBridgeInit() { + return 0; // this shouldn't be used so return "false" +} + +int SentryNativeBridgeCrashedLastRun() { return [SentrySDK crashedLastRun] ? 1 : 0; } -void Close() { +void SentryNativeBridgeClose() { [SentrySDK close]; } diff --git a/package-dev/Plugins/macOS/SentryNativeBridge.m b/package-dev/Plugins/macOS/SentryNativeBridge.m index 7055f04f1..c323cfdb7 100644 --- a/package-dev/Plugins/macOS/SentryNativeBridge.m +++ b/package-dev/Plugins/macOS/SentryNativeBridge.m @@ -40,134 +40,160 @@ return loadStatus; } -// TODO expose options setup & init -// SentryOptions *options = [[optionsClass alloc] init]; -// [options -// setValue: -// @"https://94677106febe46b88b9b9ae5efd18a00@o447951.ingest.sentry.io/5439417" -// forKey:@"dsn"]; -// [options setValue:[NSNumber numberWithBool:YES] forKey:@"debug"]; - -// [sdkClass startWithOptionsObject:options]; - -int CrashedLastRun() { -// return [SentrySDK crashedLastRun] ? 1 : 0; +// Initialize Sentry SDK with the given options +// returns (bool): 0 on failure, 1 on success, -1 on error +// WARNING: you may only call other Sentry* functions AFTER calling this AND only if it returned "1" +int +SentryNativeBridgeInit() +{ + if (LoadSentryDylib() != 1) { + return loadStatus; + } + + SentryOptions *options = [[optionsClass alloc] init]; + [options setValue:@"https://94677106febe46b88b9b9ae5efd18a00@o447951.ingest.sentry.io/5439417" + forKey:@"dsn"]; + [options setValue:[NSNumber numberWithBool:YES] forKey:@"debug"]; + + [sdkClass startWithOptionsObject:options]; + + return 1; } -void Close() { -// [SentrySDK close]; +int +SentryNativeBridgeCrashedLastRun() +{ + // return [sdkClass crashedLastRun] ? 1 : 0; } -void SentryNativeBridgeAddBreadcrumb(const char* timestamp, const char* message, const char* -type, const char* category, int level) { -// if (timestamp == NULL && message == NULL && type == NULL && category == NULL) { -// return; -// } - -// [SentrySDK configureScope:^(SentryScope * scope) { -// SentryBreadcrumb *breadcrumb = [[SentryBreadcrumb alloc] init]; - -// if (timestamp != NULL) { -// NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; -// [dateFormatter setDateFormat:NSCalendarIdentifierISO8601]; -// breadcrumb.timestamp = [dateFormatter dateFromString:[NSString -// stringWithCString:timestamp encoding:NSUTF8StringEncoding]]; -// } - -// if (message != NULL) { -// breadcrumb.message = [NSString stringWithCString:message -// encoding:NSUTF8StringEncoding]; -// } - -// if (type != NULL) { -// breadcrumb.type = [NSString stringWithCString:type -// encoding:NSUTF8StringEncoding]; -// } - -// if (category != NULL) { -// breadcrumb.category = [NSString stringWithCString:category -// encoding:NSUTF8StringEncoding]; -// } - -// breadcrumb.level = level; - -// [scope addBreadcrumb:breadcrumb]; -// }]; +void +SentryNativeBridgeClose() +{ + // [sdkClass close]; } -void SentryNativeBridgeSetExtra(const char* key, const char* value) { -// if (key == NULL) { -// return; -// } - -// [SentrySDK configureScope:^(SentryScope * scope) { -// if (value != NULL) { -// [scope setExtraValue:[NSString stringWithUTF8String:value] forKey:[NSString -// stringWithUTF8String:key]]; -// } else { -// [scope removeExtraForKey:[NSString stringWithUTF8String:key]]; -// } -// }]; +void +SentryNativeBridgeAddBreadcrumb( + const char *timestamp, const char *message, const char *type, const char *category, int level) +{ + // if (timestamp == NULL && message == NULL && type == NULL && category == NULL) { + // return; + // } + + // [SentrySDK configureScope:^(SentryScope * scope) { + // SentryBreadcrumb *breadcrumb = [[SentryBreadcrumb alloc] init]; + + // if (timestamp != NULL) { + // NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + // [dateFormatter setDateFormat:NSCalendarIdentifierISO8601]; + // breadcrumb.timestamp = [dateFormatter dateFromString:[NSString + // stringWithCString:timestamp encoding:NSUTF8StringEncoding]]; + // } + + // if (message != NULL) { + // breadcrumb.message = [NSString stringWithCString:message + // encoding:NSUTF8StringEncoding]; + // } + + // if (type != NULL) { + // breadcrumb.type = [NSString stringWithCString:type + // encoding:NSUTF8StringEncoding]; + // } + + // if (category != NULL) { + // breadcrumb.category = [NSString stringWithCString:category + // encoding:NSUTF8StringEncoding]; + // } + + // breadcrumb.level = level; + + // [scope addBreadcrumb:breadcrumb]; + // }]; +} + +void +SentryNativeBridgeSetExtra(const char *key, const char *value) +{ + // if (key == NULL) { + // return; + // } + + // [SentrySDK configureScope:^(SentryScope * scope) { + // if (value != NULL) { + // [scope setExtraValue:[NSString stringWithUTF8String:value] forKey:[NSString + // stringWithUTF8String:key]]; + // } else { + // [scope removeExtraForKey:[NSString stringWithUTF8String:key]]; + // } + // }]; } -void SentryNativeBridgeSetTag(const char* key, const char* value) { -// if (key == NULL) { -// return; -// } - -// [SentrySDK configureScope:^(SentryScope * scope) { -// if (value != NULL) { -// [scope setTagValue:[NSString stringWithUTF8String:value] forKey:[NSString -// stringWithUTF8String:key]]; -// } else { -// [scope removeTagForKey:[NSString stringWithUTF8String:key]]; -// } -// }]; +void +SentryNativeBridgeSetTag(const char *key, const char *value) +{ + // if (key == NULL) { + // return; + // } + + // [SentrySDK configureScope:^(SentryScope * scope) { + // if (value != NULL) { + // [scope setTagValue:[NSString stringWithUTF8String:value] forKey:[NSString + // stringWithUTF8String:key]]; + // } else { + // [scope removeTagForKey:[NSString stringWithUTF8String:key]]; + // } + // }]; } -void SentryNativeBridgeUnsetTag(const char* key) { -// if (key == NULL) { -// return; -// } +void +SentryNativeBridgeUnsetTag(const char *key) +{ + // if (key == NULL) { + // return; + // } -// [SentrySDK configureScope:^(SentryScope * scope) { -// [scope removeTagForKey:[NSString stringWithUTF8String:key]]; -// }]; + // [SentrySDK configureScope:^(SentryScope * scope) { + // [scope removeTagForKey:[NSString stringWithUTF8String:key]]; + // }]; } -void SentryNativeBridgeSetUser(const char* email, const char* userId, const char* ipAddress, -const char* username) { -// if (email == NULL && userId == NULL && ipAddress == NULL && username == NULL) { -// return; -// } +void +SentryNativeBridgeSetUser( + const char *email, const char *userId, const char *ipAddress, const char *username) +{ + // if (email == NULL && userId == NULL && ipAddress == NULL && username == NULL) { + // return; + // } -// [SentrySDK configureScope:^(SentryScope * scope) { -// SentryUser *user = [[SentryUser alloc] init]; + // [SentrySDK configureScope:^(SentryScope * scope) { + // SentryUser *user = [[SentryUser alloc] init]; -// if (email != NULL) { -// user.email = [NSString stringWithCString:email encoding:NSUTF8StringEncoding]; -// } + // if (email != NULL) { + // user.email = [NSString stringWithCString:email encoding:NSUTF8StringEncoding]; + // } -// if (userId != NULL) { -// user.userId = [NSString stringWithCString:userId encoding:NSUTF8StringEncoding]; -// } + // if (userId != NULL) { + // user.userId = [NSString stringWithCString:userId encoding:NSUTF8StringEncoding]; + // } -// if (ipAddress != NULL) { -// user.ipAddress = [NSString stringWithCString:ipAddress -// encoding:NSUTF8StringEncoding]; -// } + // if (ipAddress != NULL) { + // user.ipAddress = [NSString stringWithCString:ipAddress + // encoding:NSUTF8StringEncoding]; + // } -// if (username != NULL) { -// user.username = [NSString stringWithCString:username -// encoding:NSUTF8StringEncoding]; -// } + // if (username != NULL) { + // user.username = [NSString stringWithCString:username + // encoding:NSUTF8StringEncoding]; + // } -// [scope setUser:user]; -// }]; + // [scope setUser:user]; + // }]; } -void SentryNativeBridgeUnsetUser() { -// [SentrySDK configureScope:^(SentryScope * scope) { -// [scope setUser:nil]; -// }]; +void +SentryNativeBridgeUnsetUser() +{ + // [SentrySDK configureScope:^(SentryScope * scope) { + // [scope setUser:nil]; + // }]; } diff --git a/src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs b/src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs index 0bc68e37a..17a8ad524 100644 --- a/src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs +++ b/src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs @@ -11,10 +11,13 @@ namespace Sentry.Unity.iOS /// internal static class SentryCocoaBridgeProxy { - [DllImport("__Internal")] + [DllImport("__Internal", EntryPoint = "SentryNativeBridgeInit")] + public static extern int Init(); + + [DllImport("__Internal", EntryPoint = "SentryNativeBridgeCrashedLastRun")] public static extern int CrashedLastRun(); - [DllImport("__Internal")] + [DllImport("__Internal", EntryPoint = "SentryNativeBridgeClose")] public static extern void Close(); [DllImport("__Internal")] diff --git a/src/Sentry.Unity.iOS/SentryNative.cs b/src/Sentry.Unity.iOS/SentryNative.cs index d37969582..35bec1813 100644 --- a/src/Sentry.Unity.iOS/SentryNative.cs +++ b/src/Sentry.Unity.iOS/SentryNative.cs @@ -1,5 +1,6 @@ using Sentry.Extensibility; using Sentry.Unity.Integrations; +using UnityEngine; namespace Sentry.Unity.iOS { @@ -14,24 +15,43 @@ public static class SentryNativeCocoa /// The Sentry Unity options to use. public static void Configure(SentryUnityOptions options) { - if (options.IosNativeSupportEnabled) + switch (ApplicationAdapter.Instance.Platform) { - options.ScopeObserver = new NativeScopeObserver("iOS", options); - options.EnableScopeSync = true; - options.CrashedLastRun = () => - { - var crashedLastRun = SentryCocoaBridgeProxy.CrashedLastRun() == 1; - options.DiagnosticLogger? - .LogDebug("Native iOS SDK reported: 'crashedLastRun': '{0}'", crashedLastRun); - - return crashedLastRun; - }; - ApplicationAdapter.Instance.Quitting += () => - { - options.DiagnosticLogger?.LogDebug("Closing the sentry-cocoa SDK"); - SentryCocoaBridgeProxy.Close(); - }; + case RuntimePlatform.IPhonePlayer: + if (!options.IosNativeSupportEnabled) + { + return; + } + options.ScopeObserver = new NativeScopeObserver("iOS", options); + break; + case RuntimePlatform.OSXPlayer: + if (!options.MacosNativeSupportEnabled) + { + return; + } + options.ScopeObserver = new NativeScopeObserver("macOS", options); + if (SentryCocoaBridgeProxy.Init() != 1) + { + options.DiagnosticLogger?.LogWarning("Failed to initialzie the native SDK"); + return; + } + break; } + + options.EnableScopeSync = true; + options.CrashedLastRun = () => + { + var crashedLastRun = SentryCocoaBridgeProxy.CrashedLastRun() == 1; + options.DiagnosticLogger? + .LogDebug("Native SDK reported: 'crashedLastRun': '{0}'", crashedLastRun); + + return crashedLastRun; + }; + ApplicationAdapter.Instance.Quitting += () => + { + options.DiagnosticLogger?.LogDebug("Closing the sentry-cocoa SDK"); + SentryCocoaBridgeProxy.Close(); + }; } } } From 347cf5d2bd63c26702da5db042a8bb166ef0ece8 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Wed, 27 Apr 2022 20:42:44 +0200 Subject: [PATCH 06/28] feat: implement macOS cocoa SDK start with options --- .clang-format | 1 - package-dev/Plugins/iOS/SentryNativeBridge.m | 80 ++++-- .../Plugins/macOS/Sentry/Sentry.dylib.meta | 14 +- .../Plugins/macOS/SentryNativeBridge.m | 257 +++++++++--------- .../SentryCocoaBridgeProxy.cs | 67 ++++- src/Sentry.Unity.iOS/SentryNative.cs | 4 +- 6 files changed, 248 insertions(+), 175 deletions(-) diff --git a/.clang-format b/.clang-format index d07822c8b..5fa895e9e 100644 --- a/.clang-format +++ b/.clang-format @@ -3,6 +3,5 @@ BasedOnStyle: WebKit IndentWidth: 4 IndentPPDirectives: AfterHash ColumnLimit: 100 -AlwaysBreakAfterDefinitionReturnType: All PointerAlignment: Right ForEachMacros: ['SENTRY_WITH_SCOPE', 'SENTRY_WITH_SCOPE_MUT'] diff --git a/package-dev/Plugins/iOS/SentryNativeBridge.m b/package-dev/Plugins/iOS/SentryNativeBridge.m index 620b94764..85fc43787 100644 --- a/package-dev/Plugins/iOS/SentryNativeBridge.m +++ b/package-dev/Plugins/iOS/SentryNativeBridge.m @@ -2,31 +2,51 @@ NS_ASSUME_NONNULL_BEGIN -// must be here to match the native bridge API for macOS -int SentryNativeBridgeInit() { - return 0; // this shouldn't be used so return "false" +int SentryNativeBridgeLoadLibrary() +{ + // macOS only } -int SentryNativeBridgeCrashedLastRun() { - return [SentrySDK crashedLastRun] ? 1 : 0; +void *SentryNativeBridgeOptionsNew() +{ + // macOS only } -void SentryNativeBridgeClose() { - [SentrySDK close]; +void SentryNativeBridgeOptionsSetString(void *options, const char *name, const char *value) +{ + // macOS only } -void SentryNativeBridgeAddBreadcrumb(const char* timestamp, const char* message, const char* type, const char* category, int level) { +void SentryNativeBridgeOptionsSetInt(void *options, const char *name, int32_t value) +{ + // macOS only +} + +void SentryNativeBridgeStartWithOptions(void *options) +{ + // macOS only +} + +int SentryNativeBridgeCrashedLastRun() { return [SentrySDK crashedLastRun] ? 1 : 0; } + +void SentryNativeBridgeClose() { [SentrySDK close]; } + +void SentryNativeBridgeAddBreadcrumb( + const char *timestamp, const char *message, const char *type, const char *category, int level) +{ if (timestamp == NULL && message == NULL && type == NULL && category == NULL) { return; } - [SentrySDK configureScope:^(SentryScope * scope) { + [SentrySDK configureScope:^(SentryScope *scope) { SentryBreadcrumb *breadcrumb = [[SentryBreadcrumb alloc] init]; if (timestamp != NULL) { NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; [dateFormatter setDateFormat:NSCalendarIdentifierISO8601]; - breadcrumb.timestamp = [dateFormatter dateFromString:[NSString stringWithCString:timestamp encoding:NSUTF8StringEncoding]]; + breadcrumb.timestamp = + [dateFormatter dateFromString:[NSString stringWithCString:timestamp + encoding:NSUTF8StringEncoding]]; } if (message != NULL) { @@ -38,7 +58,8 @@ void SentryNativeBridgeAddBreadcrumb(const char* timestamp, const char* message, } if (category != NULL) { - breadcrumb.category = [NSString stringWithCString:category encoding:NSUTF8StringEncoding]; + breadcrumb.category = [NSString stringWithCString:category + encoding:NSUTF8StringEncoding]; } breadcrumb.level = level; @@ -47,50 +68,56 @@ void SentryNativeBridgeAddBreadcrumb(const char* timestamp, const char* message, }]; } -void SentryNativeBridgeSetExtra(const char* key, const char* value) { +void SentryNativeBridgeSetExtra(const char *key, const char *value) +{ if (key == NULL) { return; } - [SentrySDK configureScope:^(SentryScope * scope) { + [SentrySDK configureScope:^(SentryScope *scope) { if (value != NULL) { - [scope setExtraValue:[NSString stringWithUTF8String:value] forKey:[NSString stringWithUTF8String:key]]; + [scope setExtraValue:[NSString stringWithUTF8String:value] + forKey:[NSString stringWithUTF8String:key]]; } else { [scope removeExtraForKey:[NSString stringWithUTF8String:key]]; } }]; } -void SentryNativeBridgeSetTag(const char* key, const char* value) { +void SentryNativeBridgeSetTag(const char *key, const char *value) +{ if (key == NULL) { return; } - [SentrySDK configureScope:^(SentryScope * scope) { + [SentrySDK configureScope:^(SentryScope *scope) { if (value != NULL) { - [scope setTagValue:[NSString stringWithUTF8String:value] forKey:[NSString stringWithUTF8String:key]]; + [scope setTagValue:[NSString stringWithUTF8String:value] + forKey:[NSString stringWithUTF8String:key]]; } else { [scope removeTagForKey:[NSString stringWithUTF8String:key]]; } }]; } -void SentryNativeBridgeUnsetTag(const char* key) { +void SentryNativeBridgeUnsetTag(const char *key) +{ if (key == NULL) { return; } - [SentrySDK configureScope:^(SentryScope * scope) { - [scope removeTagForKey:[NSString stringWithUTF8String:key]]; - }]; + [SentrySDK configureScope:^( + SentryScope *scope) { [scope removeTagForKey:[NSString stringWithUTF8String:key]]; }]; } -void SentryNativeBridgeSetUser(const char* email, const char* userId, const char* ipAddress, const char* username) { +void SentryNativeBridgeSetUser( + const char *email, const char *userId, const char *ipAddress, const char *username) +{ if (email == NULL && userId == NULL && ipAddress == NULL && username == NULL) { return; } - [SentrySDK configureScope:^(SentryScope * scope) { + [SentrySDK configureScope:^(SentryScope *scope) { SentryUser *user = [[SentryUser alloc] init]; if (email != NULL) { @@ -113,10 +140,9 @@ void SentryNativeBridgeSetUser(const char* email, const char* userId, const char }]; } -void SentryNativeBridgeUnsetUser() { - [SentrySDK configureScope:^(SentryScope * scope) { - [scope setUser:nil]; - }]; +void SentryNativeBridgeUnsetUser() +{ + [SentrySDK configureScope:^(SentryScope *scope) { [scope setUser:nil]; }]; } NS_ASSUME_NONNULL_END diff --git a/package-dev/Plugins/macOS/Sentry/Sentry.dylib.meta b/package-dev/Plugins/macOS/Sentry/Sentry.dylib.meta index 5ee8d51df..2ff22fed8 100644 --- a/package-dev/Plugins/macOS/Sentry/Sentry.dylib.meta +++ b/package-dev/Plugins/macOS/Sentry/Sentry.dylib.meta @@ -31,7 +31,7 @@ PluginImporter: settings: CPU: ARMv7 - first: - Any: + Any: second: enabled: 0 settings: {} @@ -48,7 +48,7 @@ PluginImporter: second: enabled: 0 settings: - CPU: x86_64 + CPU: AnyCPU - first: Standalone: OSXUniversal second: @@ -74,8 +74,8 @@ PluginImporter: settings: AddToEmbeddedBinaries: false CPU: AnyCPU - CompileFlags: - FrameworkDependencies: - userData: - assetBundleName: - assetBundleVariant: + CompileFlags: + FrameworkDependencies: + userData: + assetBundleName: + assetBundleVariant: diff --git a/package-dev/Plugins/macOS/SentryNativeBridge.m b/package-dev/Plugins/macOS/SentryNativeBridge.m index c323cfdb7..535539fe8 100644 --- a/package-dev/Plugins/macOS/SentryNativeBridge.m +++ b/package-dev/Plugins/macOS/SentryNativeBridge.m @@ -1,199 +1,184 @@ #import #include -@class SentryOptions; -@class SentrySDK; +static int loadStatus = -1; // unitialized -static int loadStatus = 0; // 0 = unitialized; 1 = dylib loaded successfully; -1 = dylib load error -static void *dylib; -static Class sdkClass; -static Class optionsClass; +static Class SentrySDK; +static Class SentryScope; +static Class SentryBreadcrumb; -int -LoadSentryDylib() +#define LOAD_CLASS_OR_BREAK(name) \ + name = dlsym(dylib, "OBJC_CLASS_$_" #name); \ + if (!name) { \ + NSLog(@("Couldn't load " #name " class from the dynamic library")); \ + break; \ + } + +// Returns (bool): 0 on failure, 1 on success +// WARNING: you may only call other Sentry* functions AFTER calling this AND only if it returned "1" +int SentryNativeBridgeLoadLibrary() { - if (!loadStatus) { - loadStatus = -1; // init to "error" + if (loadStatus == -1) { + loadStatus = 0; // init to "error" do { - dylib = dlopen("@executable_path/../PlugIns/Sentry.dylib", RTLD_LAZY); + void *dylib = dlopen("@executable_path/../PlugIns/Sentry.dylib", RTLD_LAZY); if (!dylib) { NSLog(@"Couldn't load Sentry.dylib - dlopen() failed"); break; } - sdkClass = dlsym(dylib, "OBJC_CLASS_$_SentrySDK"); - if (!sdkClass) { - NSLog(@"Couldn't load SentrySDK class from the dynamic library"); - break; - } - - optionsClass = dlsym(dylib, "OBJC_CLASS_$_SentryOptions"); - if (!optionsClass) { - NSLog(@"Couldn't load SentryOptions class from the dynamic library"); - break; - } + LOAD_CLASS_OR_BREAK(SentrySDK) + LOAD_CLASS_OR_BREAK(SentryScope) + LOAD_CLASS_OR_BREAK(SentryBreadcrumb) - // everything above passed succesfully - mark as loaded + // everything above passed - mark as successfully loaded loadStatus = 1; } while (false); } return loadStatus; } -// Initialize Sentry SDK with the given options -// returns (bool): 0 on failure, 1 on success, -1 on error -// WARNING: you may only call other Sentry* functions AFTER calling this AND only if it returned "1" -int -SentryNativeBridgeInit() -{ - if (LoadSentryDylib() != 1) { - return loadStatus; - } - - SentryOptions *options = [[optionsClass alloc] init]; - [options setValue:@"https://94677106febe46b88b9b9ae5efd18a00@o447951.ingest.sentry.io/5439417" - forKey:@"dsn"]; - [options setValue:[NSNumber numberWithBool:YES] forKey:@"debug"]; - - [sdkClass startWithOptionsObject:options]; +void *SentryNativeBridgeOptionsNew() { return (void *)[[NSMutableDictionary alloc] init]; } - return 1; +void SentryNativeBridgeOptionsSetString(void *options, const char *name, const char *value) +{ + NSMutableDictionary *dictOptions = (NSMutableDictionary *)options; + dictOptions[[NSString stringWithUTF8String:name]] = [NSString stringWithUTF8String:value]; } -int -SentryNativeBridgeCrashedLastRun() +void SentryNativeBridgeOptionsSetInt(void *options, const char *name, int32_t value) { - // return [sdkClass crashedLastRun] ? 1 : 0; + NSMutableDictionary *dictOptions = (NSMutableDictionary *)options; + dictOptions[[NSString stringWithUTF8String:name]] = [NSNumber numberWithInt:value]; } -void -SentryNativeBridgeClose() +void SentryNativeBridgeStartWithOptions(void *options) { - // [sdkClass close]; + [SentrySDK startWithOptions:((NSMutableDictionary *)options)]; + [((NSMutableDictionary *)options) release]; } -void -SentryNativeBridgeAddBreadcrumb( +/******************************************************************************************/ +/* THE REMAINING CODE IS A LITERAL COPY OF THE iOS/SentryNativeBridge.m */ +/* Note: maybe we could avoid the code copy, e.g. by doing the dlopen(__internal) on iOS? */ +/******************************************************************************************/ + +int SentryNativeBridgeCrashedLastRun() { return [SentrySDK crashedLastRun] ? 1 : 0; } + +void SentryNativeBridgeClose() { [SentrySDK close]; } + +void SentryNativeBridgeAddBreadcrumb( const char *timestamp, const char *message, const char *type, const char *category, int level) { - // if (timestamp == NULL && message == NULL && type == NULL && category == NULL) { - // return; - // } + if (timestamp == NULL && message == NULL && type == NULL && category == NULL) { + return; + } - // [SentrySDK configureScope:^(SentryScope * scope) { - // SentryBreadcrumb *breadcrumb = [[SentryBreadcrumb alloc] init]; + // [SentrySDK configureScope:^(SentryScope *scope) { + // SentryBreadcrumb *breadcrumb = [[SentryBreadcrumb alloc] init]; - // if (timestamp != NULL) { - // NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; - // [dateFormatter setDateFormat:NSCalendarIdentifierISO8601]; - // breadcrumb.timestamp = [dateFormatter dateFromString:[NSString - // stringWithCString:timestamp encoding:NSUTF8StringEncoding]]; - // } + // if (timestamp != NULL) { + // NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + // [dateFormatter setDateFormat:NSCalendarIdentifierISO8601]; + // breadcrumb.timestamp = + // [dateFormatter dateFromString:[NSString stringWithCString:timestamp + // encoding:NSUTF8StringEncoding]]; + // } - // if (message != NULL) { - // breadcrumb.message = [NSString stringWithCString:message - // encoding:NSUTF8StringEncoding]; - // } + // if (message != NULL) { + // breadcrumb.message = [NSString stringWithCString:message + // encoding:NSUTF8StringEncoding]; + // } - // if (type != NULL) { - // breadcrumb.type = [NSString stringWithCString:type - // encoding:NSUTF8StringEncoding]; - // } + // if (type != NULL) { + // breadcrumb.type = [NSString stringWithCString:type encoding:NSUTF8StringEncoding]; + // } - // if (category != NULL) { - // breadcrumb.category = [NSString stringWithCString:category - // encoding:NSUTF8StringEncoding]; - // } + // if (category != NULL) { + // breadcrumb.category = [NSString stringWithCString:category + // encoding:NSUTF8StringEncoding]; + // } - // breadcrumb.level = level; + // breadcrumb.level = level; - // [scope addBreadcrumb:breadcrumb]; - // }]; + // [scope addBreadcrumb:breadcrumb]; + // }]; } -void -SentryNativeBridgeSetExtra(const char *key, const char *value) +void SentryNativeBridgeSetExtra(const char *key, const char *value) { - // if (key == NULL) { - // return; - // } + if (key == NULL) { + return; + } - // [SentrySDK configureScope:^(SentryScope * scope) { - // if (value != NULL) { - // [scope setExtraValue:[NSString stringWithUTF8String:value] forKey:[NSString - // stringWithUTF8String:key]]; - // } else { - // [scope removeExtraForKey:[NSString stringWithUTF8String:key]]; - // } - // }]; + // [SentrySDK configureScope:^(SentryScope *scope) { + // if (value != NULL) { + // [scope setExtraValue:[NSString stringWithUTF8String:value] + // forKey:[NSString stringWithUTF8String:key]]; + // } else { + // [scope removeExtraForKey:[NSString stringWithUTF8String:key]]; + // } + // }]; } -void -SentryNativeBridgeSetTag(const char *key, const char *value) +void SentryNativeBridgeSetTag(const char *key, const char *value) { - // if (key == NULL) { - // return; - // } + if (key == NULL) { + return; + } - // [SentrySDK configureScope:^(SentryScope * scope) { - // if (value != NULL) { - // [scope setTagValue:[NSString stringWithUTF8String:value] forKey:[NSString - // stringWithUTF8String:key]]; - // } else { - // [scope removeTagForKey:[NSString stringWithUTF8String:key]]; - // } - // }]; + // [SentrySDK configureScope:^(SentryScope *scope) { + // if (value != NULL) { + // [scope setTagValue:[NSString stringWithUTF8String:value] + // forKey:[NSString stringWithUTF8String:key]]; + // } else { + // [scope removeTagForKey:[NSString stringWithUTF8String:key]]; + // } + // }]; } -void -SentryNativeBridgeUnsetTag(const char *key) +void SentryNativeBridgeUnsetTag(const char *key) { - // if (key == NULL) { - // return; - // } + if (key == NULL) { + return; + } - // [SentrySDK configureScope:^(SentryScope * scope) { - // [scope removeTagForKey:[NSString stringWithUTF8String:key]]; - // }]; + // [SentrySDK configureScope:^( + // SentryScope *scope) { [scope removeTagForKey:[NSString stringWithUTF8String:key]]; }]; } -void -SentryNativeBridgeSetUser( +void SentryNativeBridgeSetUser( const char *email, const char *userId, const char *ipAddress, const char *username) { - // if (email == NULL && userId == NULL && ipAddress == NULL && username == NULL) { - // return; - // } + if (email == NULL && userId == NULL && ipAddress == NULL && username == NULL) { + return; + } - // [SentrySDK configureScope:^(SentryScope * scope) { - // SentryUser *user = [[SentryUser alloc] init]; + // [SentrySDK configureScope:^(SentryScope *scope) { + // SentryUser *user = [[SentryUser alloc] init]; - // if (email != NULL) { - // user.email = [NSString stringWithCString:email encoding:NSUTF8StringEncoding]; - // } + // if (email != NULL) { + // user.email = [NSString stringWithCString:email encoding:NSUTF8StringEncoding]; + // } - // if (userId != NULL) { - // user.userId = [NSString stringWithCString:userId encoding:NSUTF8StringEncoding]; - // } + // if (userId != NULL) { + // user.userId = [NSString stringWithCString:userId encoding:NSUTF8StringEncoding]; + // } - // if (ipAddress != NULL) { - // user.ipAddress = [NSString stringWithCString:ipAddress - // encoding:NSUTF8StringEncoding]; - // } + // if (ipAddress != NULL) { + // user.ipAddress = [NSString stringWithCString:ipAddress + // encoding:NSUTF8StringEncoding]; + // } - // if (username != NULL) { - // user.username = [NSString stringWithCString:username - // encoding:NSUTF8StringEncoding]; - // } + // if (username != NULL) { + // user.username = [NSString stringWithCString:username encoding:NSUTF8StringEncoding]; + // } - // [scope setUser:user]; - // }]; + // [scope setUser:user]; + // }]; } -void -SentryNativeBridgeUnsetUser() +void SentryNativeBridgeUnsetUser() { - // [SentrySDK configureScope:^(SentryScope * scope) { - // [scope setUser:nil]; - // }]; + // [SentrySDK configureScope:^(SentryScope *scope) { [scope setUser:nil]; }]; } diff --git a/src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs b/src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs index 17a8ad524..0b91089db 100644 --- a/src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs +++ b/src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs @@ -1,4 +1,6 @@ +using System; using System.Runtime.InteropServices; +using Sentry.Extensibility; namespace Sentry.Unity.iOS { @@ -11,8 +13,69 @@ namespace Sentry.Unity.iOS /// internal static class SentryCocoaBridgeProxy { - [DllImport("__Internal", EntryPoint = "SentryNativeBridgeInit")] - public static extern int Init(); + public static bool Init(SentryUnityOptions options) + { + // Note: used on macOS only + if (LoadLibrary() != 1) + { + return false; + } + + var cOptions = OptionsNew(); + + // Note: DSN is not null because options.IsValid() must have returned true for this to be called. + OptionsSetString(cOptions, "dsn", options.Dsn!); + + if (options.Release is not null) + { + options.DiagnosticLogger?.LogDebug("Setting Release: {0}", options.Release); + OptionsSetString(cOptions, "release", options.Release); + } + + if (options.Environment is not null) + { + options.DiagnosticLogger?.LogDebug("Setting Environment: {0}", options.Environment); + OptionsSetString(cOptions, "environment", options.Environment); + } + + options.DiagnosticLogger?.LogDebug("Setting Debug: {0}", options.Debug); + OptionsSetInt(cOptions, "debug", options.Debug ? 1 : 0); + + var diagnosticLevel = options.DiagnosticLevel.ToString().ToLowerInvariant(); + options.DiagnosticLogger?.LogDebug("Setting DiagnosticLevel: {0}", diagnosticLevel); + OptionsSetString(cOptions, "environment", diagnosticLevel); + + // Disabling the native in favor of the C# layer for now + options.DiagnosticLogger?.LogDebug("Disabling native auto session tracking"); + OptionsSetInt(cOptions, "enableAutoSessionTracking", 0); + + options.DiagnosticLogger?.LogDebug("Setting SendDefaultPii: {0}", options.SendDefaultPii); + OptionsSetInt(cOptions, "sendDefaultPii", options.SendDefaultPii ? 1 : 0); + + options.DiagnosticLogger?.LogDebug("Setting MaxBreadcrumbs: {0}", options.MaxBreadcrumbs); + OptionsSetInt(cOptions, "maxBreadcrumbs", options.MaxBreadcrumbs); + + options.DiagnosticLogger?.LogDebug("Setting MaxCacheItems: {0}", options.MaxCacheItems); + OptionsSetInt(cOptions, "maxCacheItems", options.MaxCacheItems); + + StartWithOptions(cOptions); + return true; + } + + [DllImport("__Internal", EntryPoint = "SentryNativeBridgeLoadLibrary")] + private static extern int LoadLibrary(); + + [DllImport("__Internal", EntryPoint = "SentryNativeBridgeOptionsNew")] + private static extern IntPtr OptionsNew(); + + [DllImport("__Internal", EntryPoint = "SentryNativeBridgeOptionsSetString")] + private static extern void OptionsSetString(IntPtr options, string name, string value); + + [DllImport("__Internal", EntryPoint = "SentryNativeBridgeOptionsSetInt")] + private static extern void OptionsSetInt(IntPtr options, string name, int value); + + [DllImport("__Internal", EntryPoint = "SentryNativeBridgeStartWithOptions")] + private static extern void StartWithOptions(IntPtr options); [DllImport("__Internal", EntryPoint = "SentryNativeBridgeCrashedLastRun")] public static extern int CrashedLastRun(); diff --git a/src/Sentry.Unity.iOS/SentryNative.cs b/src/Sentry.Unity.iOS/SentryNative.cs index 35bec1813..bf6c4453c 100644 --- a/src/Sentry.Unity.iOS/SentryNative.cs +++ b/src/Sentry.Unity.iOS/SentryNative.cs @@ -29,12 +29,12 @@ public static void Configure(SentryUnityOptions options) { return; } - options.ScopeObserver = new NativeScopeObserver("macOS", options); - if (SentryCocoaBridgeProxy.Init() != 1) + if (!SentryCocoaBridgeProxy.Init(options)) { options.DiagnosticLogger?.LogWarning("Failed to initialzie the native SDK"); return; } + options.ScopeObserver = new NativeScopeObserver("macOS", options); break; } From b4fceb034c8619b0953f001cc01d3569ad7c7de4 Mon Sep 17 00:00:00 2001 From: Sentry Github Bot Date: Wed, 27 Apr 2022 18:45:31 +0000 Subject: [PATCH 07/28] Format code --- .../Assets/Scripts/NativeSupport/CppPlugin.cpp | 6 ++---- .../Assets/Scripts/NativeSupport/ObjectiveCPlugin.m | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/samples/unity-of-bugs/Assets/Scripts/NativeSupport/CppPlugin.cpp b/samples/unity-of-bugs/Assets/Scripts/NativeSupport/CppPlugin.cpp index a44e38fc3..3b24c6510 100644 --- a/samples/unity-of-bugs/Assets/Scripts/NativeSupport/CppPlugin.cpp +++ b/samples/unity-of-bugs/Assets/Scripts/NativeSupport/CppPlugin.cpp @@ -3,8 +3,7 @@ #include extern "C" { -void -crash_in_cpp() +void crash_in_cpp() { char *ptr = 0; *ptr += 1; @@ -12,8 +11,7 @@ crash_in_cpp() } extern "C" { -void -throw_cpp() +void throw_cpp() { try { // throws std::length_error diff --git a/samples/unity-of-bugs/Assets/Scripts/NativeSupport/ObjectiveCPlugin.m b/samples/unity-of-bugs/Assets/Scripts/NativeSupport/ObjectiveCPlugin.m index ba8b1c776..3773cf7b2 100644 --- a/samples/unity-of-bugs/Assets/Scripts/NativeSupport/ObjectiveCPlugin.m +++ b/samples/unity-of-bugs/Assets/Scripts/NativeSupport/ObjectiveCPlugin.m @@ -2,8 +2,7 @@ NS_ASSUME_NONNULL_BEGIN -void -throwObjectiveC() +void throwObjectiveC() { #ifdef __EXCEPTIONS NSLog(@"Throwing an Objective-C Exception"); @@ -17,8 +16,7 @@ #endif } -char * -getTestArgObjectiveC() +char *getTestArgObjectiveC() { NSArray *args = NSProcessInfo.processInfo.arguments; From 0eaacee58a60a1ee6cc4c4462e75fa41024425c2 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 28 Apr 2022 12:43:30 +0200 Subject: [PATCH 08/28] feat: finish macOS native bridge & update iOS copy --- package-dev/Plugins/iOS/SentryNativeBridge.m | 164 +++++++++------- .../Plugins/macOS/SentryNativeBridge.m | 184 +++++++++++------- 2 files changed, 213 insertions(+), 135 deletions(-) diff --git a/package-dev/Plugins/iOS/SentryNativeBridge.m b/package-dev/Plugins/iOS/SentryNativeBridge.m index 85fc43787..45cb60dc7 100644 --- a/package-dev/Plugins/iOS/SentryNativeBridge.m +++ b/package-dev/Plugins/iOS/SentryNativeBridge.m @@ -27,9 +27,24 @@ void SentryNativeBridgeStartWithOptions(void *options) // macOS only } -int SentryNativeBridgeCrashedLastRun() { return [SentrySDK crashedLastRun] ? 1 : 0; } +int SentryNativeBridgeCrashedLastRun() +{ + @try { + return [SentrySDK crashedLastRun] ? 1 : 0; + } @catch (NSException *exception) { + NSLog(@"Sentry (bridge): failed to get crashedLastRun: %@", exception.reason); + } + return -1; +} -void SentryNativeBridgeClose() { [SentrySDK close]; } +void SentryNativeBridgeClose() +{ + @try { + [SentrySDK close]; + } @catch (NSException *exception) { + NSLog(@"Sentry (bridge): failed to close: %@", exception.reason); + } +} void SentryNativeBridgeAddBreadcrumb( const char *timestamp, const char *message, const char *type, const char *category, int level) @@ -38,34 +53,33 @@ void SentryNativeBridgeAddBreadcrumb( return; } - [SentrySDK configureScope:^(SentryScope *scope) { - SentryBreadcrumb *breadcrumb = [[SentryBreadcrumb alloc] init]; - - if (timestamp != NULL) { - NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; - [dateFormatter setDateFormat:NSCalendarIdentifierISO8601]; - breadcrumb.timestamp = - [dateFormatter dateFromString:[NSString stringWithCString:timestamp - encoding:NSUTF8StringEncoding]]; - } - - if (message != NULL) { - breadcrumb.message = [NSString stringWithCString:message encoding:NSUTF8StringEncoding]; - } - - if (type != NULL) { - breadcrumb.type = [NSString stringWithCString:type encoding:NSUTF8StringEncoding]; - } - - if (category != NULL) { - breadcrumb.category = [NSString stringWithCString:category - encoding:NSUTF8StringEncoding]; - } - - breadcrumb.level = level; - - [scope addBreadcrumb:breadcrumb]; - }]; + @try { + [SentrySDK configureScope:^(SentryScope *scope) { + SentryBreadcrumb *breadcrumb = [[SentryBreadcrumb alloc] + initWithLevel:level + category:(category ? [NSString stringWithUTF8String:category] : nil)]; + + if (timestamp != NULL) { + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + [dateFormatter setDateFormat:NSCalendarIdentifierISO8601]; + breadcrumb.timestamp = + [dateFormatter dateFromString:[NSString stringWithUTF8String:timestamp]]; + [dateFormatter release]; + } + + if (message != NULL) { + breadcrumb.message = [NSString stringWithUTF8String:message]; + } + + if (type != NULL) { + breadcrumb.type = [NSString stringWithUTF8String:type]; + } + + [scope addBreadcrumb:breadcrumb]; + }]; + } @catch (NSException *exception) { + NSLog(@"Sentry (bridge): failed to add breadcrumb: %@", exception.reason); + } } void SentryNativeBridgeSetExtra(const char *key, const char *value) @@ -74,14 +88,18 @@ void SentryNativeBridgeSetExtra(const char *key, const char *value) return; } - [SentrySDK configureScope:^(SentryScope *scope) { - if (value != NULL) { - [scope setExtraValue:[NSString stringWithUTF8String:value] - forKey:[NSString stringWithUTF8String:key]]; - } else { - [scope removeExtraForKey:[NSString stringWithUTF8String:key]]; - } - }]; + @try { + [SentrySDK configureScope:^(SentryScope *scope) { + if (value != NULL) { + [scope setExtraValue:[NSString stringWithUTF8String:value] + forKey:[NSString stringWithUTF8String:key]]; + } else { + [scope removeExtraForKey:[NSString stringWithUTF8String:key]]; + } + }]; + } @catch (NSException *exception) { + NSLog(@"Sentry (bridge): failed to set extra: %@", exception.reason); + } } void SentryNativeBridgeSetTag(const char *key, const char *value) @@ -90,14 +108,18 @@ void SentryNativeBridgeSetTag(const char *key, const char *value) return; } - [SentrySDK configureScope:^(SentryScope *scope) { - if (value != NULL) { - [scope setTagValue:[NSString stringWithUTF8String:value] - forKey:[NSString stringWithUTF8String:key]]; - } else { - [scope removeTagForKey:[NSString stringWithUTF8String:key]]; - } - }]; + @try { + [SentrySDK configureScope:^(SentryScope *scope) { + if (value != NULL) { + [scope setTagValue:[NSString stringWithUTF8String:value] + forKey:[NSString stringWithUTF8String:key]]; + } else { + [scope removeTagForKey:[NSString stringWithUTF8String:key]]; + } + }]; + } @catch (NSException *exception) { + NSLog(@"Sentry (bridge): failed to set tag: %@", exception.reason); + } } void SentryNativeBridgeUnsetTag(const char *key) @@ -106,8 +128,12 @@ void SentryNativeBridgeUnsetTag(const char *key) return; } - [SentrySDK configureScope:^( - SentryScope *scope) { [scope removeTagForKey:[NSString stringWithUTF8String:key]]; }]; + @try { + [SentrySDK configureScope:^( + SentryScope *scope) { [scope removeTagForKey:[NSString stringWithUTF8String:key]]; }]; + } @catch (NSException *exception) { + NSLog(@"Sentry (bridge): failed to unset tag: %@", exception.reason); + } } void SentryNativeBridgeSetUser( @@ -117,32 +143,40 @@ void SentryNativeBridgeSetUser( return; } - [SentrySDK configureScope:^(SentryScope *scope) { - SentryUser *user = [[SentryUser alloc] init]; + @try { + [SentrySDK configureScope:^(SentryScope *scope) { + SentryUser *user = [[SentryUser alloc] init]; - if (email != NULL) { - user.email = [NSString stringWithCString:email encoding:NSUTF8StringEncoding]; - } + if (email != NULL) { + user.email = [NSString stringWithUTF8String:email]; + } - if (userId != NULL) { - user.userId = [NSString stringWithCString:userId encoding:NSUTF8StringEncoding]; - } + if (userId != NULL) { + user.userId = [NSString stringWithUTF8String:userId]; + } - if (ipAddress != NULL) { - user.ipAddress = [NSString stringWithCString:ipAddress encoding:NSUTF8StringEncoding]; - } + if (ipAddress != NULL) { + user.ipAddress = [NSString stringWithUTF8String:ipAddress]; + } - if (username != NULL) { - user.username = [NSString stringWithCString:username encoding:NSUTF8StringEncoding]; - } + if (username != NULL) { + user.username = [NSString stringWithUTF8String:username]; + } - [scope setUser:user]; - }]; + [scope setUser:user]; + }]; + } @catch (NSException *exception) { + NSLog(@"Sentry (bridge): failed to set user: %@", exception.reason); + } } void SentryNativeBridgeUnsetUser() { - [SentrySDK configureScope:^(SentryScope *scope) { [scope setUser:nil]; }]; + @try { + [SentrySDK configureScope:^(SentryScope *scope) { [scope setUser:nil]; }]; + } @catch (NSException *exception) { + NSLog(@"Sentry (bridge): failed to unset user: %@", exception.reason); + } } NS_ASSUME_NONNULL_END diff --git a/package-dev/Plugins/macOS/SentryNativeBridge.m b/package-dev/Plugins/macOS/SentryNativeBridge.m index 535539fe8..b557a17b5 100644 --- a/package-dev/Plugins/macOS/SentryNativeBridge.m +++ b/package-dev/Plugins/macOS/SentryNativeBridge.m @@ -6,11 +6,12 @@ static Class SentrySDK; static Class SentryScope; static Class SentryBreadcrumb; +static Class SentryUser; #define LOAD_CLASS_OR_BREAK(name) \ name = dlsym(dylib, "OBJC_CLASS_$_" #name); \ if (!name) { \ - NSLog(@("Couldn't load " #name " class from the dynamic library")); \ + NSLog(@"Sentry (native bridge): Couldn't load %@ class from the dynamic library", name); \ break; \ } @@ -23,13 +24,14 @@ int SentryNativeBridgeLoadLibrary() do { void *dylib = dlopen("@executable_path/../PlugIns/Sentry.dylib", RTLD_LAZY); if (!dylib) { - NSLog(@"Couldn't load Sentry.dylib - dlopen() failed"); + NSLog(@"Sentry (native bridge): Couldn't load Sentry.dylib - dlopen() failed"); break; } LOAD_CLASS_OR_BREAK(SentrySDK) LOAD_CLASS_OR_BREAK(SentryScope) LOAD_CLASS_OR_BREAK(SentryBreadcrumb) + LOAD_CLASS_OR_BREAK(SentryUser) // everything above passed - mark as successfully loaded loadStatus = 1; @@ -38,6 +40,10 @@ int SentryNativeBridgeLoadLibrary() return loadStatus; } +// We're doing dynamic access so we're getting warnings about missing class/instance methods... +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wobjc-method-access" + void *SentryNativeBridgeOptionsNew() { return (void *)[[NSMutableDictionary alloc] init]; } void SentryNativeBridgeOptionsSetString(void *options, const char *name, const char *value) @@ -58,14 +64,32 @@ void SentryNativeBridgeStartWithOptions(void *options) [((NSMutableDictionary *)options) release]; } -/******************************************************************************************/ -/* THE REMAINING CODE IS A LITERAL COPY OF THE iOS/SentryNativeBridge.m */ -/* Note: maybe we could avoid the code copy, e.g. by doing the dlopen(__internal) on iOS? */ -/******************************************************************************************/ +/*****************************************************************************/ +/* The remaining code is a copy of iOS/SentryNativeBridge.m */ +/* with minor changes to make it work with dynamically loaded classes. */ +/* Specifically: */ +/* - use `id` as variable types */ +/* - use [obj setValue:value forKey:@"prop"] instead of `obj.prop = value` */ +/*****************************************************************************/ -int SentryNativeBridgeCrashedLastRun() { return [SentrySDK crashedLastRun] ? 1 : 0; } +int SentryNativeBridgeCrashedLastRun() +{ + @try { + return [SentrySDK crashedLastRun] ? 1 : 0; + } @catch (NSException *exception) { + NSLog(@"Sentry (bridge): failed to get crashedLastRun: %@", exception.reason); + } + return -1; +} -void SentryNativeBridgeClose() { [SentrySDK close]; } +void SentryNativeBridgeClose() +{ + @try { + [SentrySDK close]; + } @catch (NSException *exception) { + NSLog(@"Sentry (bridge): failed to close: %@", exception.reason); + } +} void SentryNativeBridgeAddBreadcrumb( const char *timestamp, const char *message, const char *type, const char *category, int level) @@ -74,35 +98,34 @@ void SentryNativeBridgeAddBreadcrumb( return; } - // [SentrySDK configureScope:^(SentryScope *scope) { - // SentryBreadcrumb *breadcrumb = [[SentryBreadcrumb alloc] init]; - - // if (timestamp != NULL) { - // NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; - // [dateFormatter setDateFormat:NSCalendarIdentifierISO8601]; - // breadcrumb.timestamp = - // [dateFormatter dateFromString:[NSString stringWithCString:timestamp - // encoding:NSUTF8StringEncoding]]; - // } - - // if (message != NULL) { - // breadcrumb.message = [NSString stringWithCString:message - // encoding:NSUTF8StringEncoding]; - // } - - // if (type != NULL) { - // breadcrumb.type = [NSString stringWithCString:type encoding:NSUTF8StringEncoding]; - // } + @try { + [SentrySDK configureScope:^(id scope) { + id breadcrumb = [[SentryBreadcrumb alloc] + initWithLevel:level + category:(category ? [NSString stringWithUTF8String:category] : nil)]; + + if (timestamp != NULL) { + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + [dateFormatter setDateFormat:NSCalendarIdentifierISO8601]; + [breadcrumb setValue:[dateFormatter + dateFromString:[NSString stringWithUTF8String:timestamp]] + forKey:@"timestamp"]; + [dateFormatter release]; + } - // if (category != NULL) { - // breadcrumb.category = [NSString stringWithCString:category - // encoding:NSUTF8StringEncoding]; - // } + if (message != NULL) { + [breadcrumb setValue:[NSString stringWithUTF8String:message] forKey:@"message"]; + } - // breadcrumb.level = level; + if (type != NULL) { + [breadcrumb setValue:[NSString stringWithUTF8String:type] forKey:@"type"]; + } - // [scope addBreadcrumb:breadcrumb]; - // }]; + [scope addBreadcrumb:breadcrumb]; + }]; + } @catch (NSException *exception) { + NSLog(@"Sentry (bridge): failed to add breadcrumb: %@", exception.reason); + } } void SentryNativeBridgeSetExtra(const char *key, const char *value) @@ -111,14 +134,18 @@ void SentryNativeBridgeSetExtra(const char *key, const char *value) return; } - // [SentrySDK configureScope:^(SentryScope *scope) { - // if (value != NULL) { - // [scope setExtraValue:[NSString stringWithUTF8String:value] - // forKey:[NSString stringWithUTF8String:key]]; - // } else { - // [scope removeExtraForKey:[NSString stringWithUTF8String:key]]; - // } - // }]; + @try { + [SentrySDK configureScope:^(id scope) { + if (value != NULL) { + [scope setExtraValue:[NSString stringWithUTF8String:value] + forKey:[NSString stringWithUTF8String:key]]; + } else { + [scope removeExtraForKey:[NSString stringWithUTF8String:key]]; + } + }]; + } @catch (NSException *exception) { + NSLog(@"Sentry (bridge): failed to set extra: %@", exception.reason); + } } void SentryNativeBridgeSetTag(const char *key, const char *value) @@ -127,14 +154,18 @@ void SentryNativeBridgeSetTag(const char *key, const char *value) return; } - // [SentrySDK configureScope:^(SentryScope *scope) { - // if (value != NULL) { - // [scope setTagValue:[NSString stringWithUTF8String:value] - // forKey:[NSString stringWithUTF8String:key]]; - // } else { - // [scope removeTagForKey:[NSString stringWithUTF8String:key]]; - // } - // }]; + @try { + [SentrySDK configureScope:^(id scope) { + if (value != NULL) { + [scope setTagValue:[NSString stringWithUTF8String:value] + forKey:[NSString stringWithUTF8String:key]]; + } else { + [scope removeTagForKey:[NSString stringWithUTF8String:key]]; + } + }]; + } @catch (NSException *exception) { + NSLog(@"Sentry (bridge): failed to set tag: %@", exception.reason); + } } void SentryNativeBridgeUnsetTag(const char *key) @@ -143,8 +174,12 @@ void SentryNativeBridgeUnsetTag(const char *key) return; } - // [SentrySDK configureScope:^( - // SentryScope *scope) { [scope removeTagForKey:[NSString stringWithUTF8String:key]]; }]; + @try { + [SentrySDK configureScope:^( + id scope) { [scope removeTagForKey:[NSString stringWithUTF8String:key]]; }]; + } @catch (NSException *exception) { + NSLog(@"Sentry (bridge): failed to unset tag: %@", exception.reason); + } } void SentryNativeBridgeSetUser( @@ -154,31 +189,40 @@ void SentryNativeBridgeSetUser( return; } - // [SentrySDK configureScope:^(SentryScope *scope) { - // SentryUser *user = [[SentryUser alloc] init]; + @try { + [SentrySDK configureScope:^(id scope) { + id user = [[SentryUser alloc] init]; - // if (email != NULL) { - // user.email = [NSString stringWithCString:email encoding:NSUTF8StringEncoding]; - // } + if (email != NULL) { + [user setValue:[NSString stringWithUTF8String:email] forKey:@"email"]; + } - // if (userId != NULL) { - // user.userId = [NSString stringWithCString:userId encoding:NSUTF8StringEncoding]; - // } + if (userId != NULL) { + [user setValue:[NSString stringWithUTF8String:userId] forKey:@"userId"]; + } - // if (ipAddress != NULL) { - // user.ipAddress = [NSString stringWithCString:ipAddress - // encoding:NSUTF8StringEncoding]; - // } + if (ipAddress != NULL) { + [user setValue:[NSString stringWithUTF8String:ipAddress] forKey:@"ipAddress"]; + } - // if (username != NULL) { - // user.username = [NSString stringWithCString:username encoding:NSUTF8StringEncoding]; - // } + if (username != NULL) { + [user setValue:[NSString stringWithUTF8String:username] forKey:@"username"]; + } - // [scope setUser:user]; - // }]; + [scope setUser:user]; + }]; + } @catch (NSException *exception) { + NSLog(@"Sentry (bridge): failed to set user: %@", exception.reason); + } } void SentryNativeBridgeUnsetUser() { - // [SentrySDK configureScope:^(SentryScope *scope) { [scope setUser:nil]; }]; + @try { + [SentrySDK configureScope:^(id scope) { [scope setUser:nil]; }]; + } @catch (NSException *exception) { + NSLog(@"Sentry (bridge): failed to unset user: %@", exception.reason); + } } + +#pragma clang diagnostic pop From 9f8b77126451311ab3bafa75be311c5362c650d9 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 28 Apr 2022 14:14:18 +0200 Subject: [PATCH 09/28] fix: cocoa unit tests --- src/Sentry.Unity.iOS/SentryNative.cs | 10 ++++-- .../SentryNativeIosTests.cs | 36 ++++++++++--------- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/src/Sentry.Unity.iOS/SentryNative.cs b/src/Sentry.Unity.iOS/SentryNative.cs index bf6c4453c..ea2ec06c2 100644 --- a/src/Sentry.Unity.iOS/SentryNative.cs +++ b/src/Sentry.Unity.iOS/SentryNative.cs @@ -13,9 +13,11 @@ public static class SentryNativeCocoa /// Configures the native support. /// /// The Sentry Unity options to use. - public static void Configure(SentryUnityOptions options) + public static void Configure(SentryUnityOptions options) => Configure(options, ApplicationAdapter.Instance.Platform); + + internal static void Configure(SentryUnityOptions options, RuntimePlatform platform) { - switch (ApplicationAdapter.Instance.Platform) + switch (platform) { case RuntimePlatform.IPhonePlayer: if (!options.IosNativeSupportEnabled) @@ -36,6 +38,10 @@ public static void Configure(SentryUnityOptions options) } options.ScopeObserver = new NativeScopeObserver("macOS", options); break; + default: + options.DiagnosticLogger? + .LogWarning("Cocoa SentryNative.Configure() called for unsupported platform: '{0}'", platform); + return; } options.EnableScopeSync = true; diff --git a/test/Sentry.Unity.iOS.Tests/SentryNativeIosTests.cs b/test/Sentry.Unity.iOS.Tests/SentryNativeIosTests.cs index 841071d5b..7585b176f 100644 --- a/test/Sentry.Unity.iOS.Tests/SentryNativeIosTests.cs +++ b/test/Sentry.Unity.iOS.Tests/SentryNativeIosTests.cs @@ -1,46 +1,48 @@ +using System; using NUnit.Framework; +using UnityEngine; namespace Sentry.Unity.iOS.Tests { public class SentryNativeCocoaTests { [Test] - public void Configure_DefaultConfiguration_SetsScopeObserver() + public void Configure_DefaultConfiguration_iOS() { var options = new SentryUnityOptions(); - SentryNativeCocoa.Configure(options); + SentryNativeCocoa.Configure(options, RuntimePlatform.IPhonePlayer); Assert.IsAssignableFrom(options.ScopeObserver); - } - - [Test] - public void Configure_DefaultConfiguration_SetsCrashedLastRun() - { - var options = new SentryUnityOptions(); - SentryNativeCocoa.Configure(options); Assert.IsNotNull(options.CrashedLastRun); + Assert.True(options.EnableScopeSync); } [Test] - public void Configure_NativeIosSupportDisabled_ObserverIsNull() + public void Configure_NativeSupportDisabled_iOS() { var options = new SentryUnityOptions { IosNativeSupportEnabled = false }; - SentryNativeCocoa.Configure(options); + SentryNativeCocoa.Configure(options, RuntimePlatform.IPhonePlayer); Assert.Null(options.ScopeObserver); + Assert.Null(options.CrashedLastRun); + Assert.False(options.EnableScopeSync); } [Test] - public void Configure_DefaultConfiguration_EnablesScopeSync() + public void Configure_DefaultConfiguration_macOS() { var options = new SentryUnityOptions(); - SentryNativeCocoa.Configure(options); - Assert.True(options.EnableScopeSync); + // Note: can't test macOS - throws because it tries to call SentryCocoaBridgeProxy.Init() + // but the bridge isn't loaded now... + Assert.Throws(() => + SentryNativeCocoa.Configure(options, RuntimePlatform.OSXPlayer)); } [Test] - public void Configure_NativeIosSupportDisabled_DisabledScopeSync() + public void Configure_NativeSupportDisabled_macOS() { - var options = new SentryUnityOptions { IosNativeSupportEnabled = false }; - SentryNativeCocoa.Configure(options); + var options = new SentryUnityOptions { MacosNativeSupportEnabled = false }; + SentryNativeCocoa.Configure(options, RuntimePlatform.OSXPlayer); + Assert.Null(options.ScopeObserver); + Assert.Null(options.CrashedLastRun); Assert.False(options.EnableScopeSync); } } From c7891d3e64caa5f0fa44a6db7e59afe74164dfc0 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 28 Apr 2022 14:22:55 +0200 Subject: [PATCH 10/28] chore: add macOS to DownloadNativeSDKs --- Directory.Build.targets | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Directory.Build.targets b/Directory.Build.targets index d8af15681..348ffbea5 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -15,7 +15,7 @@ ../../artifacts/test/playmode/results.xml ../../artifacts/test/editmode/results.xml $(RepoRoot)package-dev/Plugins/ - + $(RepoRoot)modules/sentry-cocoa/ $(SentryArtifactsDestination)/iOS/Device/Sentry.framework/ $(SentryArtifactsDestination)/iOS/Simulator/Sentry.framework/ @@ -521,6 +521,7 @@ void PrintFailedTests(XElement element) + From 74b68b73d5f7d5f73d689d208a7a8c61eb4300a4 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 28 Apr 2022 17:21:27 +0200 Subject: [PATCH 11/28] fix: macOS SentryNativeBridge.m for Unity 2019 --- .../Plugins/macOS/SentryNativeBridge.m | 188 ++++++++++-------- 1 file changed, 103 insertions(+), 85 deletions(-) diff --git a/package-dev/Plugins/macOS/SentryNativeBridge.m b/package-dev/Plugins/macOS/SentryNativeBridge.m index b557a17b5..ecb30b2d5 100644 --- a/package-dev/Plugins/macOS/SentryNativeBridge.m +++ b/package-dev/Plugins/macOS/SentryNativeBridge.m @@ -9,7 +9,7 @@ static Class SentryUser; #define LOAD_CLASS_OR_BREAK(name) \ - name = dlsym(dylib, "OBJC_CLASS_$_" #name); \ + name = (__bridge Class)dlsym(dylib, "OBJC_CLASS_$_" #name); \ if (!name) { \ NSLog(@"Sentry (native bridge): Couldn't load %@ class from the dynamic library", name); \ break; \ @@ -40,42 +40,42 @@ int SentryNativeBridgeLoadLibrary() return loadStatus; } -// We're doing dynamic access so we're getting warnings about missing class/instance methods... -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wobjc-method-access" - -void *SentryNativeBridgeOptionsNew() { return (void *)[[NSMutableDictionary alloc] init]; } +const void *SentryNativeBridgeOptionsNew() +{ + return CFBridgingRetain([[NSMutableDictionary alloc] init]); +} -void SentryNativeBridgeOptionsSetString(void *options, const char *name, const char *value) +void SentryNativeBridgeOptionsSetString(const void *options, const char *name, const char *value) { - NSMutableDictionary *dictOptions = (NSMutableDictionary *)options; + NSMutableDictionary *dictOptions = (__bridge NSMutableDictionary *)options; dictOptions[[NSString stringWithUTF8String:name]] = [NSString stringWithUTF8String:value]; } -void SentryNativeBridgeOptionsSetInt(void *options, const char *name, int32_t value) +void SentryNativeBridgeOptionsSetInt(const void *options, const char *name, int32_t value) { - NSMutableDictionary *dictOptions = (NSMutableDictionary *)options; + NSMutableDictionary *dictOptions = (__bridge NSMutableDictionary *)options; dictOptions[[NSString stringWithUTF8String:name]] = [NSNumber numberWithInt:value]; } -void SentryNativeBridgeStartWithOptions(void *options) +void SentryNativeBridgeStartWithOptions(const void *options) { - [SentrySDK startWithOptions:((NSMutableDictionary *)options)]; - [((NSMutableDictionary *)options) release]; + NSMutableDictionary *dictOptions = (__bridge_transfer NSMutableDictionary *)options; + [SentrySDK performSelector:@selector(startWithOptions:) withObject:dictOptions]; } -/*****************************************************************************/ -/* The remaining code is a copy of iOS/SentryNativeBridge.m */ -/* with minor changes to make it work with dynamically loaded classes. */ -/* Specifically: */ -/* - use `id` as variable types */ -/* - use [obj setValue:value forKey:@"prop"] instead of `obj.prop = value` */ -/*****************************************************************************/ +/*******************************************************************************/ +/* The remaining code is a copy of iOS/SentryNativeBridge.m with changes to */ +/* make it work with dynamically loaded classes. Mainly: */ +/* - call: [class performSelector:@selector(arg1:arg2:) */ +/* withObject:arg1Value withObject:arg2Value]; */ +/* - use `id` as variable types */ +/* - use [obj setValue:value forKey:@"prop"] instead of `obj.prop = value` */ +/*******************************************************************************/ int SentryNativeBridgeCrashedLastRun() { @try { - return [SentrySDK crashedLastRun] ? 1 : 0; + return [SentrySDK performSelector:@selector(crashedLastRun)] ? 1 : 0; } @catch (NSException *exception) { NSLog(@"Sentry (bridge): failed to get crashedLastRun: %@", exception.reason); } @@ -98,31 +98,37 @@ void SentryNativeBridgeAddBreadcrumb( return; } - @try { - [SentrySDK configureScope:^(id scope) { - id breadcrumb = [[SentryBreadcrumb alloc] - initWithLevel:level - category:(category ? [NSString stringWithUTF8String:category] : nil)]; - - if (timestamp != NULL) { - NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; - [dateFormatter setDateFormat:NSCalendarIdentifierISO8601]; - [breadcrumb setValue:[dateFormatter - dateFromString:[NSString stringWithUTF8String:timestamp]] - forKey:@"timestamp"]; - [dateFormatter release]; - } + // declaring the (block) callback as a variable to avoid too much editor blank space on the left + void (^scopeUpdateBlock)(id) = ^void(id scope) { + id breadcrumb = [[SentryBreadcrumb alloc] init]; - if (message != NULL) { - [breadcrumb setValue:[NSString stringWithUTF8String:message] forKey:@"message"]; - } + if (timestamp != NULL) { + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + [dateFormatter setDateFormat:NSCalendarIdentifierISO8601]; + [breadcrumb + setValue:[dateFormatter dateFromString:[NSString stringWithUTF8String:timestamp]] + forKey:@"timestamp"]; + } - if (type != NULL) { - [breadcrumb setValue:[NSString stringWithUTF8String:type] forKey:@"type"]; - } + if (message != NULL) { + [breadcrumb setValue:[NSString stringWithUTF8String:message] forKey:@"message"]; + } + + if (type != NULL) { + [breadcrumb setValue:[NSString stringWithUTF8String:type] forKey:@"type"]; + } + + if (category != NULL) { + [breadcrumb setValue:[NSString stringWithUTF8String:category] forKey:@"category"]; + } - [scope addBreadcrumb:breadcrumb]; - }]; + [breadcrumb setValue:[NSNumber numberWithInt:level] forKey:@"level"]; + + [scope performSelector:@selector(addBreadcrumb:) withObject:breadcrumb]; + }; + + @try { + [SentrySDK performSelector:@selector(configureScope:) withObject:scopeUpdateBlock]; } @catch (NSException *exception) { NSLog(@"Sentry (bridge): failed to add breadcrumb: %@", exception.reason); } @@ -135,14 +141,17 @@ void SentryNativeBridgeSetExtra(const char *key, const char *value) } @try { - [SentrySDK configureScope:^(id scope) { - if (value != NULL) { - [scope setExtraValue:[NSString stringWithUTF8String:value] - forKey:[NSString stringWithUTF8String:key]]; - } else { - [scope removeExtraForKey:[NSString stringWithUTF8String:key]]; - } - }]; + [SentrySDK performSelector:@selector(configureScope:) + withObject:^(id scope) { + if (value != NULL) { + [scope performSelector:@selector(setExtraValue:forKey:) + withObject:[NSString stringWithUTF8String:value] + withObject:[NSString stringWithUTF8String:key]]; + } else { + [scope performSelector:@selector(removeExtraForKey:) + withObject:[NSString stringWithUTF8String:key]]; + } + }]; } @catch (NSException *exception) { NSLog(@"Sentry (bridge): failed to set extra: %@", exception.reason); } @@ -155,14 +164,17 @@ void SentryNativeBridgeSetTag(const char *key, const char *value) } @try { - [SentrySDK configureScope:^(id scope) { - if (value != NULL) { - [scope setTagValue:[NSString stringWithUTF8String:value] - forKey:[NSString stringWithUTF8String:key]]; - } else { - [scope removeTagForKey:[NSString stringWithUTF8String:key]]; - } - }]; + [SentrySDK performSelector:@selector(configureScope:) + withObject:^(id scope) { + if (value != NULL) { + [scope performSelector:@selector(setTagValue:forKey:) + withObject:[NSString stringWithUTF8String:value] + withObject:[NSString stringWithUTF8String:key]]; + } else { + [scope performSelector:@selector(removeTagForKey:) + withObject:[NSString stringWithUTF8String:key]]; + } + }]; } @catch (NSException *exception) { NSLog(@"Sentry (bridge): failed to set tag: %@", exception.reason); } @@ -175,8 +187,11 @@ void SentryNativeBridgeUnsetTag(const char *key) } @try { - [SentrySDK configureScope:^( - id scope) { [scope removeTagForKey:[NSString stringWithUTF8String:key]]; }]; + [SentrySDK performSelector:@selector(configureScope:) + withObject:^(id scope) { + [scope performSelector:@selector(removeTagForKey:) + withObject:[NSString stringWithUTF8String:key]]; + }]; } @catch (NSException *exception) { NSLog(@"Sentry (bridge): failed to unset tag: %@", exception.reason); } @@ -190,27 +205,31 @@ void SentryNativeBridgeSetUser( } @try { - [SentrySDK configureScope:^(id scope) { - id user = [[SentryUser alloc] init]; - - if (email != NULL) { - [user setValue:[NSString stringWithUTF8String:email] forKey:@"email"]; - } - - if (userId != NULL) { - [user setValue:[NSString stringWithUTF8String:userId] forKey:@"userId"]; - } - - if (ipAddress != NULL) { - [user setValue:[NSString stringWithUTF8String:ipAddress] forKey:@"ipAddress"]; - } - - if (username != NULL) { - [user setValue:[NSString stringWithUTF8String:username] forKey:@"username"]; - } - - [scope setUser:user]; - }]; + [SentrySDK + performSelector:@selector(configureScope:) + withObject:^(id scope) { + id user = [[SentryUser alloc] init]; + + if (email != NULL) { + [user setValue:[NSString stringWithUTF8String:email] forKey:@"email"]; + } + + if (userId != NULL) { + [user setValue:[NSString stringWithUTF8String:userId] forKey:@"userId"]; + } + + if (ipAddress != NULL) { + [user setValue:[NSString stringWithUTF8String:ipAddress] + forKey:@"ipAddress"]; + } + + if (username != NULL) { + [user setValue:[NSString stringWithUTF8String:username] + forKey:@"username"]; + } + + [scope setUser:user]; + }]; } @catch (NSException *exception) { NSLog(@"Sentry (bridge): failed to set user: %@", exception.reason); } @@ -219,10 +238,9 @@ void SentryNativeBridgeSetUser( void SentryNativeBridgeUnsetUser() { @try { - [SentrySDK configureScope:^(id scope) { [scope setUser:nil]; }]; + [SentrySDK performSelector:@selector(configureScope:) + withObject:^(id scope) { [scope setUser:nil]; }]; } @catch (NSException *exception) { NSLog(@"Sentry (bridge): failed to unset user: %@", exception.reason); } } - -#pragma clang diagnostic pop From db92e048b7a1260fe075545dfc1aa00a0f2c4ecf Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 28 Apr 2022 18:46:52 +0200 Subject: [PATCH 12/28] fix: SentryNativeBridge.m iOS compilation --- package-dev/Plugins/iOS/SentryNativeBridge.m | 188 +++++++------------ 1 file changed, 65 insertions(+), 123 deletions(-) diff --git a/package-dev/Plugins/iOS/SentryNativeBridge.m b/package-dev/Plugins/iOS/SentryNativeBridge.m index 45cb60dc7..4fbf9afd3 100644 --- a/package-dev/Plugins/iOS/SentryNativeBridge.m +++ b/package-dev/Plugins/iOS/SentryNativeBridge.m @@ -2,49 +2,16 @@ NS_ASSUME_NONNULL_BEGIN -int SentryNativeBridgeLoadLibrary() -{ - // macOS only -} - -void *SentryNativeBridgeOptionsNew() -{ - // macOS only -} - -void SentryNativeBridgeOptionsSetString(void *options, const char *name, const char *value) -{ - // macOS only -} +// macOS only +int SentryNativeBridgeLoadLibrary() { return 0; } +void *SentryNativeBridgeOptionsNew() { return nil; } +void SentryNativeBridgeOptionsSetString(void *options, const char *name, const char *value) { } +void SentryNativeBridgeOptionsSetInt(void *options, const char *name, int32_t value) { } +void SentryNativeBridgeStartWithOptions(void *options) { } -void SentryNativeBridgeOptionsSetInt(void *options, const char *name, int32_t value) -{ - // macOS only -} - -void SentryNativeBridgeStartWithOptions(void *options) -{ - // macOS only -} - -int SentryNativeBridgeCrashedLastRun() -{ - @try { - return [SentrySDK crashedLastRun] ? 1 : 0; - } @catch (NSException *exception) { - NSLog(@"Sentry (bridge): failed to get crashedLastRun: %@", exception.reason); - } - return -1; -} +int SentryNativeBridgeCrashedLastRun() { return [SentrySDK crashedLastRun] ? 1 : 0; } -void SentryNativeBridgeClose() -{ - @try { - [SentrySDK close]; - } @catch (NSException *exception) { - NSLog(@"Sentry (bridge): failed to close: %@", exception.reason); - } -} +void SentryNativeBridgeClose() { [SentrySDK close]; } void SentryNativeBridgeAddBreadcrumb( const char *timestamp, const char *message, const char *type, const char *category, int level) @@ -53,33 +20,28 @@ void SentryNativeBridgeAddBreadcrumb( return; } - @try { - [SentrySDK configureScope:^(SentryScope *scope) { - SentryBreadcrumb *breadcrumb = [[SentryBreadcrumb alloc] - initWithLevel:level - category:(category ? [NSString stringWithUTF8String:category] : nil)]; - - if (timestamp != NULL) { - NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; - [dateFormatter setDateFormat:NSCalendarIdentifierISO8601]; - breadcrumb.timestamp = - [dateFormatter dateFromString:[NSString stringWithUTF8String:timestamp]]; - [dateFormatter release]; - } - - if (message != NULL) { - breadcrumb.message = [NSString stringWithUTF8String:message]; - } - - if (type != NULL) { - breadcrumb.type = [NSString stringWithUTF8String:type]; - } - - [scope addBreadcrumb:breadcrumb]; - }]; - } @catch (NSException *exception) { - NSLog(@"Sentry (bridge): failed to add breadcrumb: %@", exception.reason); - } + [SentrySDK configureScope:^(SentryScope *scope) { + SentryBreadcrumb *breadcrumb = [[SentryBreadcrumb alloc] + initWithLevel:level + category:(category ? [NSString stringWithUTF8String:category] : nil)]; + + if (timestamp != NULL) { + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + [dateFormatter setDateFormat:NSCalendarIdentifierISO8601]; + breadcrumb.timestamp = + [dateFormatter dateFromString:[NSString stringWithUTF8String:timestamp]]; + } + + if (message != NULL) { + breadcrumb.message = [NSString stringWithUTF8String:message]; + } + + if (type != NULL) { + breadcrumb.type = [NSString stringWithUTF8String:type]; + } + + [scope addBreadcrumb:breadcrumb]; + }]; } void SentryNativeBridgeSetExtra(const char *key, const char *value) @@ -88,18 +50,14 @@ void SentryNativeBridgeSetExtra(const char *key, const char *value) return; } - @try { - [SentrySDK configureScope:^(SentryScope *scope) { - if (value != NULL) { - [scope setExtraValue:[NSString stringWithUTF8String:value] - forKey:[NSString stringWithUTF8String:key]]; - } else { - [scope removeExtraForKey:[NSString stringWithUTF8String:key]]; - } - }]; - } @catch (NSException *exception) { - NSLog(@"Sentry (bridge): failed to set extra: %@", exception.reason); - } + [SentrySDK configureScope:^(SentryScope *scope) { + if (value != NULL) { + [scope setExtraValue:[NSString stringWithUTF8String:value] + forKey:[NSString stringWithUTF8String:key]]; + } else { + [scope removeExtraForKey:[NSString stringWithUTF8String:key]]; + } + }]; } void SentryNativeBridgeSetTag(const char *key, const char *value) @@ -108,18 +66,14 @@ void SentryNativeBridgeSetTag(const char *key, const char *value) return; } - @try { - [SentrySDK configureScope:^(SentryScope *scope) { - if (value != NULL) { - [scope setTagValue:[NSString stringWithUTF8String:value] - forKey:[NSString stringWithUTF8String:key]]; - } else { - [scope removeTagForKey:[NSString stringWithUTF8String:key]]; - } - }]; - } @catch (NSException *exception) { - NSLog(@"Sentry (bridge): failed to set tag: %@", exception.reason); - } + [SentrySDK configureScope:^(SentryScope *scope) { + if (value != NULL) { + [scope setTagValue:[NSString stringWithUTF8String:value] + forKey:[NSString stringWithUTF8String:key]]; + } else { + [scope removeTagForKey:[NSString stringWithUTF8String:key]]; + } + }]; } void SentryNativeBridgeUnsetTag(const char *key) @@ -128,12 +82,8 @@ void SentryNativeBridgeUnsetTag(const char *key) return; } - @try { - [SentrySDK configureScope:^( - SentryScope *scope) { [scope removeTagForKey:[NSString stringWithUTF8String:key]]; }]; - } @catch (NSException *exception) { - NSLog(@"Sentry (bridge): failed to unset tag: %@", exception.reason); - } + [SentrySDK configureScope:^( + SentryScope *scope) { [scope removeTagForKey:[NSString stringWithUTF8String:key]]; }]; } void SentryNativeBridgeSetUser( @@ -143,40 +93,32 @@ void SentryNativeBridgeSetUser( return; } - @try { - [SentrySDK configureScope:^(SentryScope *scope) { - SentryUser *user = [[SentryUser alloc] init]; + [SentrySDK configureScope:^(SentryScope *scope) { + SentryUser *user = [[SentryUser alloc] init]; - if (email != NULL) { - user.email = [NSString stringWithUTF8String:email]; - } + if (email != NULL) { + user.email = [NSString stringWithUTF8String:email]; + } - if (userId != NULL) { - user.userId = [NSString stringWithUTF8String:userId]; - } + if (userId != NULL) { + user.userId = [NSString stringWithUTF8String:userId]; + } - if (ipAddress != NULL) { - user.ipAddress = [NSString stringWithUTF8String:ipAddress]; - } + if (ipAddress != NULL) { + user.ipAddress = [NSString stringWithUTF8String:ipAddress]; + } - if (username != NULL) { - user.username = [NSString stringWithUTF8String:username]; - } + if (username != NULL) { + user.username = [NSString stringWithUTF8String:username]; + } - [scope setUser:user]; - }]; - } @catch (NSException *exception) { - NSLog(@"Sentry (bridge): failed to set user: %@", exception.reason); - } + [scope setUser:user]; + }]; } void SentryNativeBridgeUnsetUser() { - @try { - [SentrySDK configureScope:^(SentryScope *scope) { [scope setUser:nil]; }]; - } @catch (NSException *exception) { - NSLog(@"Sentry (bridge): failed to unset user: %@", exception.reason); - } + [SentrySDK configureScope:^(SentryScope *scope) { [scope setUser:nil]; }]; } NS_ASSUME_NONNULL_END From fbc48df74e809396158b838ad047440923373c50 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 28 Apr 2022 19:24:27 +0200 Subject: [PATCH 13/28] ci: sdk cache key must include static files in package-dev/Plugins/Platform --- .github/workflows/sdk.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sdk.yml b/.github/workflows/sdk.yml index 6cd9c37a1..8de8e0789 100644 --- a/.github/workflows/sdk.yml +++ b/.github/workflows/sdk.yml @@ -32,7 +32,7 @@ jobs: path: | package-dev/Plugins modules/sentry-java/sentry-android-ndk/build/intermediates/merged_native_libs/release/out/lib - key: sdk=${{ inputs.target }}-${{ hashFiles('submodules-status', 'package/package.json', 'Directory.Build.targets') }} + key: sdk=${{ inputs.target }}-${{ hashFiles('submodules-status', 'package/package.json', 'Directory.Build.targets', 'package-dev/Plugins/${{ inputs.target }}/**') }} - name: Checkout submodules if: steps.cache.outputs.cache-hit != 'true' From 733a82ff715d7437a91039db15a8d4701bb53b37 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Fri, 29 Apr 2022 09:33:28 +0200 Subject: [PATCH 14/28] ci: sdk cache key must include static files --- .github/workflows/sdk.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/sdk.yml b/.github/workflows/sdk.yml index 8de8e0789..867da0338 100644 --- a/.github/workflows/sdk.yml +++ b/.github/workflows/sdk.yml @@ -22,6 +22,9 @@ jobs: - name: Get submodules status run: git submodule status | tee submodules-status + - run: cp -r package-dev/Plugins/${{ inputs.target }} sdk-static || echo "never mind, no files checked in..." + shell: bash + - name: Restore from cache uses: actions/cache@v2 id: cache @@ -32,7 +35,7 @@ jobs: path: | package-dev/Plugins modules/sentry-java/sentry-android-ndk/build/intermediates/merged_native_libs/release/out/lib - key: sdk=${{ inputs.target }}-${{ hashFiles('submodules-status', 'package/package.json', 'Directory.Build.targets', 'package-dev/Plugins/${{ inputs.target }}/**') }} + key: sdk=${{ inputs.target }}-${{ hashFiles('submodules-status', 'package/package.json', 'Directory.Build.targets', 'sdk-static/**') }} - name: Checkout submodules if: steps.cache.outputs.cache-hit != 'true' From b50ab18802b1fada4947792b3e5cd01882895a0a Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Fri, 29 Apr 2022 10:54:34 +0200 Subject: [PATCH 15/28] chore: update package-release snapshot --- test/Scripts.Tests/package-release.zip.snapshot | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/Scripts.Tests/package-release.zip.snapshot b/test/Scripts.Tests/package-release.zip.snapshot index fffd52285..cd5041aa1 100644 --- a/test/Scripts.Tests/package-release.zip.snapshot +++ b/test/Scripts.Tests/package-release.zip.snapshot @@ -13,7 +13,15 @@ Editor/sentry-cli/sentry-cli-Windows-x86_64.exe Editor/sentry-cli/sentry-cli-Windows-x86_64.exe.meta Plugins/Android.meta Plugins/iOS.meta +Plugins/macOS.meta Plugins/Windows.meta +Plugins/macOS/Sentry.meta +Plugins/macOS/SentryNativeBridge.m +Plugins/macOS/SentryNativeBridge.m.meta +Plugins/macOS/Sentry/Sentry.dylib +Plugins/macOS/Sentry/Sentry.dylib.dSYM +Plugins/macOS/Sentry/Sentry.dylib.dSYM.meta +Plugins/macOS/Sentry/Sentry.dylib.meta Plugins/iOS/Device.meta Plugins/iOS/SentryNativeBridge.m Plugins/iOS/SentryNativeBridge.m.meta From 86afcf94ee063e3f4c11d397f2fe452488ee7a10 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Fri, 29 Apr 2022 12:09:22 +0200 Subject: [PATCH 16/28] fix: macOS environment vs diagnosticLevel options init --- src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs b/src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs index 0b91089db..06049b58f 100644 --- a/src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs +++ b/src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs @@ -43,7 +43,7 @@ public static bool Init(SentryUnityOptions options) var diagnosticLevel = options.DiagnosticLevel.ToString().ToLowerInvariant(); options.DiagnosticLogger?.LogDebug("Setting DiagnosticLevel: {0}", diagnosticLevel); - OptionsSetString(cOptions, "environment", diagnosticLevel); + OptionsSetString(cOptions, "diagnosticLevel", diagnosticLevel); // Disabling the native in favor of the C# layer for now options.DiagnosticLogger?.LogDebug("Disabling native auto session tracking"); From 7fd94e97bfe3268663fad63459ae46f0c7e5cb14 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Fri, 29 Apr 2022 12:10:16 +0200 Subject: [PATCH 17/28] feat: macOS debug symbol upload --- .../Native/BuildPostProcess.cs | 62 ++++++++++++++----- 1 file changed, 45 insertions(+), 17 deletions(-) diff --git a/src/Sentry.Unity.Editor/Native/BuildPostProcess.cs b/src/Sentry.Unity.Editor/Native/BuildPostProcess.cs index 2ac319bbd..f0a57cb7a 100644 --- a/src/Sentry.Unity.Editor/Native/BuildPostProcess.cs +++ b/src/Sentry.Unity.Editor/Native/BuildPostProcess.cs @@ -13,7 +13,7 @@ public static class BuildPostProcess [PostProcessBuild(1)] public static void OnPostProcessBuild(BuildTarget target, string executablePath) { - if (target is not (BuildTarget.StandaloneWindows or BuildTarget.StandaloneWindows64)) + if (EditorUserBuildSettings.selectedBuildTargetGroup is not BuildTargetGroup.Standalone) { return; } @@ -36,15 +36,16 @@ public static void OnPostProcessBuild(BuildTarget target, string executablePath) return; } - if (!options.WindowsNativeSupportEnabled) + if (!IsEnabledForPlatform(target, options)) { - logger.LogDebug("Windows Native support disabled through the options."); + logger.LogDebug("Native support for the current platform is disabled in the configuration."); return; } var projectDir = Path.GetDirectoryName(executablePath); - AddCrashHandler(logger, projectDir); - UploadDebugSymbols(logger, projectDir, Path.GetFileName(executablePath)); + var executableName = Path.GetFileName(executablePath); + AddCrashHandler(logger, target, projectDir, executableName); + UploadDebugSymbols(logger, target, projectDir, executableName); } catch (Exception e) @@ -54,16 +55,38 @@ public static void OnPostProcessBuild(BuildTarget target, string executablePath) } } - private static void AddCrashHandler(IDiagnosticLogger logger, string projectDir) + private static bool IsEnabledForPlatform(BuildTarget target, SentryUnityOptions options) => target switch { - var crashpadPath = Path.GetFullPath(Path.Combine("Packages", SentryPackageInfo.GetName(), "Plugins", - "Windows", "Sentry", "crashpad_handler.exe")); - var targetPath = Path.Combine(projectDir, Path.GetFileName(crashpadPath)); - logger.LogInfo("Copying the native crash handler '{0}' to the output directory", Path.GetFileName(crashpadPath)); + BuildTarget.StandaloneWindows64 => options.WindowsNativeSupportEnabled, + BuildTarget.StandaloneOSX => options.MacosNativeSupportEnabled, + _ => false, + }; + + private static void AddCrashHandler(IDiagnosticLogger logger, BuildTarget target, string projectDir, string executableName) + { + string crashpadPath; + string targetPath; + if (target is BuildTarget.StandaloneWindows64) + { + crashpadPath = Path.Combine("Windows", "Sentry", "crashpad_handler.exe"); + targetPath = Path.Combine(projectDir, Path.GetFileName(crashpadPath)); + } + else if (target is BuildTarget.StandaloneOSX) + { + // No standalone crash handler for macOS - uses built-in handler in sentry-cocoa. + return; + } + else + { + throw new ArgumentException($"Unsupported build target: {target}"); + } + + crashpadPath = Path.GetFullPath(Path.Combine("Packages", SentryPackageInfo.GetName(), "Plugins", crashpadPath)); + logger.LogInfo("Copying the native crash handler '{0}' to {1}", Path.GetFileName(crashpadPath), targetPath); File.Copy(crashpadPath, targetPath, true); } - private static void UploadDebugSymbols(IDiagnosticLogger logger, string projectDir, string executableName) + private static void UploadDebugSymbols(IDiagnosticLogger logger, BuildTarget target, string projectDir, string executableName) { var cliOptions = SentryScriptableObject.CreateOrLoad(SentryCliOptions.GetConfigPath()); if (!cliOptions.IsValid(logger)) @@ -91,13 +114,18 @@ private static void UploadDebugSymbols(IDiagnosticLogger logger, string projectD }; addPath(executableName); - addPath("GameAssembly.dll"); - addPath("UnityPlayer.dll"); addPath(Path.GetFileNameWithoutExtension(executableName) + "_BackUpThisFolder_ButDontShipItWithYourGame"); - addPath(Path.GetFileNameWithoutExtension(executableName) + "_Data/Plugins/x86_64/sentry.dll"); - - // Note: using Path.GetFullPath as suggested by https://docs.unity3d.com/Manual/upm-assets.html - addPath(Path.GetFullPath($"Packages/{SentryPackageInfo.GetName()}/Plugins/Windows/Sentry/sentry.pdb")); + if (target is BuildTarget.StandaloneWindows64) + { + addPath("GameAssembly.dll"); + addPath("UnityPlayer.dll"); + addPath(Path.GetFileNameWithoutExtension(executableName) + "_Data/Plugins/x86_64/sentry.dll"); + addPath(Path.GetFullPath($"Packages/{SentryPackageInfo.GetName()}/Plugins/Windows/Sentry/sentry.pdb")); + } + else if (target is BuildTarget.StandaloneOSX) + { + addPath(Path.GetFullPath($"Packages/{SentryPackageInfo.GetName()}/Plugins/macOS/Sentry/Sentry.dylib.dSYM")); + } // Configure the process using the StartInfo properties. var process = new Process From f7d1d49ff40a6c29c5cd20552a6972093ae50351 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Fri, 29 Apr 2022 15:16:39 +0200 Subject: [PATCH 18/28] ci: add macOS smoke test --- .github/workflows/ci.yml | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cc5593e93..13f2a29bd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -272,16 +272,21 @@ jobs: # pwsh ./test/Scripts.Tests/test-pack-contents.ps1 accept run: ./test/Scripts.Tests/test-pack-contents.ps1 - windows-smoke-test: - name: Run Windows Unity ${{ matrix.unity-version }} Smoke Test - runs-on: windows-latest + desktop-smoke-test: + name: Run ${{ matrix.os }} Unity ${{ matrix.unity-version }} Smoke Test + runs-on: ${{ matrix.os }}-latest strategy: fail-fast: false matrix: unity-version: ['2019', '2020', '2021'] + os: ['windows', 'macos'] include: - - os: windows-latest + - os: windows + unity-modules: windows-il2cpp unity-config-path: C:/ProgramData/Unity/config/ + - os: macos + unity-modules: mac-il2cpp + unity-config-path: /Library/Application Support/Unity/config/ defaults: run: shell: pwsh @@ -293,20 +298,11 @@ jobs: id: env run: echo "::set-output name=unityVersion::$(./scripts/ci-env.ps1 "unity${{ matrix.unity-version }}")" - # Caching the whole Unity installation would overflow the 10 GiB limit repositories have but we can cache the Hub - # to achieve some speedup here at a low size cost. - # See https://github.com/kuler90/setup-unity/blob/797ebd0cc9d0e0ca5bcd6cc280ec1a6e685fbf83/src/setup.js#L36 - - name: Cache Unity Hub - uses: actions/cache@v2 - with: - path: 'C:/Program Files/Unity Hub' - key: unity-hub|windows - - name: Setup Unity uses: getsentry/setup-unity@46c2e082d98cc3a825a5b59038cb31705fe9ff56 with: unity-version: ${{ steps.env.outputs.unityVersion }} - unity-modules: windows-il2cpp + unity-modules: ${{ matrix.unity-modules }} - run: echo "::add-mask::${{ secrets.LICENSE_SERVER_URL }}" @@ -317,6 +313,7 @@ jobs: # Step used to reduce the Unity path to avoid long path errors on Windows. - name: Make symbolic link for Unity (Windows) + if: matrix.os == 'windows' run: New-Item -ItemType SymbolicLink -Path "C:\${{ steps.env.outputs.unityVersion }}" -Target "C:\Program Files\Unity\Hub\Editor\${{ steps.env.outputs.unityVersion }}" - name: Create new Project From bd504dffaebe10da4ec90acc14b1ba109c4db6ee Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Mon, 2 May 2022 20:15:51 +0200 Subject: [PATCH 19/28] chore: fix integration test scripts for macOS CI --- .github/workflows/ci.yml | 10 ++--- scripts/unity-utils.ps1 | 44 +++++++------------ scripts/unity.ps1 | 2 +- .../IntegrationGlobals.ps1 | 4 +- .../integration-build-project.ps1 | 4 +- .../integration-create-project.ps1 | 4 +- .../integration-update-sentry.ps1 | 2 +- 7 files changed, 27 insertions(+), 43 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 13f2a29bd..59c9d0805 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -273,7 +273,7 @@ jobs: run: ./test/Scripts.Tests/test-pack-contents.ps1 desktop-smoke-test: - name: Run ${{ matrix.os }} Unity ${{ matrix.unity-version }} Smoke Test + name: Run ${{ matrix.os }} Smoke Test - ${{ matrix.unity-version }} runs-on: ${{ matrix.os }}-latest strategy: fail-fast: false @@ -317,10 +317,10 @@ jobs: run: New-Item -ItemType SymbolicLink -Path "C:\${{ steps.env.outputs.unityVersion }}" -Target "C:\Program Files\Unity\Hub\Editor\${{ steps.env.outputs.unityVersion }}" - name: Create new Project - run: ./test/Scripts.Integration.Test/integration-create-project.ps1 "${{ env.UNITY_PATH }}" + run: ./test/Scripts.Integration.Test/integration-create-project.ps1 -UnityPath "${{ env.UNITY_PATH }}" - name: Build Standalone Player without Sentry SDK - run: ./test/Scripts.Integration.Test/integration-build-project.ps1 "${{ env.UNITY_PATH }}" + run: ./test/Scripts.Integration.Test/integration-build-project.ps1 -UnityPath "${{ env.UNITY_PATH }}" - name: Download UPM package uses: vaind/download-artifact@989a39a417730897d098ab11c34e49ac4e13ed70 @@ -333,10 +333,10 @@ jobs: run: ./test/Scripts.Integration.Test/integration-extract-package.ps1 - name: Add Sentry to test project - run: ./test/Scripts.Integration.Test/integration-update-sentry.ps1 "${{ env.UNITY_PATH }}" + run: ./test/Scripts.Integration.Test/integration-update-sentry.ps1 -UnityPath "${{ env.UNITY_PATH }}" - name: Build Standalone Player Sentry SDK - run: ./test/Scripts.Integration.Test/integration-build-project.ps1 "${{ env.UNITY_PATH }}" + run: ./test/Scripts.Integration.Test/integration-build-project.ps1 -UnityPath "${{ env.UNITY_PATH }}" - name: Run Player - Smoke Test run: ./test/Scripts.Integration.Test/integration-run-smoke-test.ps1 -Smoke diff --git a/scripts/unity-utils.ps1 b/scripts/unity-utils.ps1 index 28ca8d288..bcedc0539 100644 --- a/scripts/unity-utils.ps1 +++ b/scripts/unity-utils.ps1 @@ -1,8 +1,7 @@ $RunUnityLicenseRetryTimeoutSeconds = 3600 $RunUnityLicenseRetryIntervalSeconds = 60 -$RunUnityLogFile = 'unity.log' -function RunUnity([string] $unityPath, [string[]] $arguments) +function RunUnity([string] $unityPath, [string[]] $arguments, [switch] $ReturnLogOutput) { If ($unityPath.StartsWith("docker ")) { @@ -24,30 +23,32 @@ function RunUnity([string] $unityPath, [string[]] $arguments) $unityPath = "xvfb-run" } + $logFilePath = "$pwd/unity.log" + foreach ($arg in $arguments) { if ($arg -ieq "logfile" -or $arg -ieq "-logfile") { - # Note: if we really needed to use a custom value: get the next arg and overwrite $RunUnityLogFile. + # Note: if we really needed to use a custom value: get the next arg and overwrite $logFilePath. # The only issue then would be if it was stdout ('-'). throw "You must not pass the 'logfile' argument - this script needs to set it to a custom value." } } - $arguments += @("-logfile", $RunUnityLogFile) + $arguments += @("-logfile", $logFilePath) $stopwatch = [System.Diagnostics.Stopwatch]::new() $stopwatch.Start() do { - ClearUnityLog + ClearUnityLog $logFilePath + New-Item $logFilePath + Write-Host "Running $unityPath $arguments" $process = Start-Process -FilePath $unityPath -ArgumentList $arguments -PassThru - Write-Host "Waiting for Unity to finish." - WaitForLogFile 30 - $stdout = SubscribeToUnityLogFile $process + $stdout = WaitForUnityExit $logFilePath $process if ($stdout -match "No valid Unity Editor license found. Please activate your license.") { @@ -66,38 +67,25 @@ function RunUnity([string] $unityPath, [string[]] $arguments) { Throw "Unity exited with code $($process.ExitCode)" } - return $stdout + return $ReturnLogOutput ? $stdout : $null } } while ($stopwatch.Elapsed.TotalSeconds -lt $RunUnityLicenseRetryTimeoutSeconds) } -function ClearUnityLog +function ClearUnityLog([string] $logFilePath) { Write-Host -NoNewline "Removing Unity log:" - If (Test-Path -Path "$RunUnityLogFile") + If (Test-Path -Path $logFilePath) { #Force is required if it's opened by another process. - Remove-Item -Path "$RunUnityLogFile" -Force + Remove-Item -Path $logFilePath -Force } Write-Host " OK" } -function WaitForLogFile -{ - for ($i = 0; $i -lt 30; $i++) - { - if (Test-Path -Path "$RunUnityLogFile") - { - return - } - Write-Host "Waiting for log file to appear: $RunUnityLogFile ..." - Start-Sleep -Seconds 1 - } - Throw "Timeout while waiting for the log file to appear: $RunUnityLogFile" -} - -function SubscribeToUnityLogFile([System.Diagnostics.Process] $unityProcess) +function WaitForUnityExit([string] $RunUnityLogFile, [System.Diagnostics.Process] $unityProcess) { + Write-Host "Waiting for Unity to finish." $unityClosedDelay = 0 If ($unityProcess -eq $null) @@ -105,7 +93,7 @@ function SubscribeToUnityLogFile([System.Diagnostics.Process] $unityProcess) Throw "Unity process not received" } - $logFileStream = New-Object System.IO.FileStream("$RunUnityLogFile", [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite) + $logFileStream = New-Object System.IO.FileStream($RunUnityLogFile, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite) If (-not $logFileStream) { Throw "Failed to open logfile on $RunUnityLogFile" diff --git a/scripts/unity.ps1 b/scripts/unity.ps1 index e3c1b4a55..fa6307aac 100644 --- a/scripts/unity.ps1 +++ b/scripts/unity.ps1 @@ -4,4 +4,4 @@ $ErrorActionPreference = "Stop" . $PSScriptRoot/unity-utils.ps1 # Redirecting the output to $null because RunUnity prints logs using Write-Host AND Write-Output - we don't need both here. -RunUnity $args[0] ($args | Select-Object -Skip 1) > $null \ No newline at end of file +RunUnity $args[0] ($args | Select-Object -Skip 1) \ No newline at end of file diff --git a/test/Scripts.Integration.Test/IntegrationGlobals.ps1 b/test/Scripts.Integration.Test/IntegrationGlobals.ps1 index 061baa8bf..266ab9924 100644 --- a/test/Scripts.Integration.Test/IntegrationGlobals.ps1 +++ b/test/Scripts.Integration.Test/IntegrationGlobals.ps1 @@ -63,9 +63,9 @@ function FormatUnityPath If ($path) { #Ajust path on MacOS - If ($path -match "Unity.app/$") + If ($path -match "Unity.app/?$") { - $path = $path + "Contents/MacOS" + $path = $path + "/Contents/MacOS" } $unityPath = $path } diff --git a/test/Scripts.Integration.Test/integration-build-project.ps1 b/test/Scripts.Integration.Test/integration-build-project.ps1 index 8aad16983..8a3f9c00c 100644 --- a/test/Scripts.Integration.Test/integration-build-project.ps1 +++ b/test/Scripts.Integration.Test/integration-build-project.ps1 @@ -9,10 +9,8 @@ $unityPath = FormatUnityPath $UnityPath $buildMethod = BuildMethodFor $Platform $outputPath = "$NewProjectBuildPath/$(GetTestAppName $buildMethod)" -ClearUnityLog - Write-Host -NoNewline "Executing ${buildMethod}:" RunUnityCustom $unityPath @("-batchmode", "-projectPath ", "$NewProjectPath", ` - "-executeMethod", $buildMethod , "-buildPath", $outputPath, "-quit") > $null + "-executeMethod", $buildMethod , "-buildPath", $outputPath, "-quit") Write-Host "Project built successfully" -ForegroundColor Green Get-ChildItem $NewProjectBuildPath diff --git a/test/Scripts.Integration.Test/integration-create-project.ps1 b/test/Scripts.Integration.Test/integration-create-project.ps1 index 42f49a28b..fa5cc2cb8 100644 --- a/test/Scripts.Integration.Test/integration-create-project.ps1 +++ b/test/Scripts.Integration.Test/integration-create-project.ps1 @@ -14,14 +14,12 @@ If (Test-Path -Path "$NewProjectPath" ) Write-Host " OK" } -ClearUnityLog - Write-Host -NoNewline "Creating directory for integration test:" New-Item -Path "$(ProjectRoot)/samples" -Name $NewProjectName -ItemType "directory" Write-Host " OK" Write-Host "Creating integration project:" -RunUnityCustom $UnityPath @("-batchmode", "-createProject", "$NewProjectPath", "-quit") > $null +RunUnityCustom $UnityPath @("-batchmode", "-createProject", "$NewProjectPath", "-quit") Write-Host -NoNewline "Copying Test scene" New-Item -Path "$NewProjectAssetsPath/Scenes" -Name $NewProjectName -ItemType "directory" diff --git a/test/Scripts.Integration.Test/integration-update-sentry.ps1 b/test/Scripts.Integration.Test/integration-update-sentry.ps1 index 2a0a39f3e..b72d200b9 100644 --- a/test/Scripts.Integration.Test/integration-update-sentry.ps1 +++ b/test/Scripts.Integration.Test/integration-update-sentry.ps1 @@ -9,7 +9,7 @@ $UnityPath = FormatUnityPath $UnityPath function RunUnityAndExpect([string] $name, [string] $successMessage, [string] $failMessage, [string[]] $arguments) { - $stdout = RunUnityCustom $UnityPath $arguments + $stdout = RunUnityCustom $UnityPath $arguments -ReturnLogOutput If ($null -ne ($stdout | Select-String $successMessage)) { Write-Host "`n$name | SUCCESS" -ForegroundColor Green From b92f1f40581b02f79fb666584edae60f12fb983e Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Mon, 2 May 2022 20:38:27 +0200 Subject: [PATCH 20/28] chore: remove unused Unity symlink for Windows (reverts #423) --- .github/workflows/ci.yml | 5 ----- Directory.Build.targets | 2 -- 2 files changed, 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 59c9d0805..e90081f68 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -311,11 +311,6 @@ jobs: New-Item -Path '${{ matrix.unity-config-path }}' -ItemType Directory Set-Content -Path '${{ matrix.unity-config-path }}services-config.json' -Value '${{ secrets.UNITY_LICENSE_SERVER_CONFIG }}' - # Step used to reduce the Unity path to avoid long path errors on Windows. - - name: Make symbolic link for Unity (Windows) - if: matrix.os == 'windows' - run: New-Item -ItemType SymbolicLink -Path "C:\${{ steps.env.outputs.unityVersion }}" -Target "C:\Program Files\Unity\Hub\Editor\${{ steps.env.outputs.unityVersion }}" - - name: Create new Project run: ./test/Scripts.Integration.Test/integration-create-project.ps1 -UnityPath "${{ env.UNITY_PATH }}" diff --git a/Directory.Build.targets b/Directory.Build.targets index 348ffbea5..d8371954c 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -40,8 +40,6 @@ C:\Program Files\Unity\Hub\Editor\$(UnityVersion)\Editor C:\Program Files\Unity\Editor - - C:\$(UnityVersion)\Editor $(UnityRoot)\Data\Managed "$(UnityRoot)\Unity.exe" Builder.BuildWindowsIl2CPPPlayer From c7b0a4eb3855c74a91a4f2e8c470a89636d76bd8 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Mon, 2 May 2022 20:40:34 +0200 Subject: [PATCH 21/28] chore: avoid full path for Unity logfile to support running in docker --- scripts/unity-utils.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/unity-utils.ps1 b/scripts/unity-utils.ps1 index bcedc0539..b5986e1ae 100644 --- a/scripts/unity-utils.ps1 +++ b/scripts/unity-utils.ps1 @@ -23,7 +23,7 @@ function RunUnity([string] $unityPath, [string[]] $arguments, [switch] $ReturnLo $unityPath = "xvfb-run" } - $logFilePath = "$pwd/unity.log" + $logFilePath = "unity.log" foreach ($arg in $arguments) { From 2f6b35d0c836409f0be022dcd2ac4a3b84eb15b5 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Tue, 3 May 2022 09:59:30 +0200 Subject: [PATCH 22/28] chore: fix RunUnityCustom CI script --- test/Scripts.Integration.Test/IntegrationGlobals.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Scripts.Integration.Test/IntegrationGlobals.ps1 b/test/Scripts.Integration.Test/IntegrationGlobals.ps1 index 266ab9924..edf4504a4 100644 --- a/test/Scripts.Integration.Test/IntegrationGlobals.ps1 +++ b/test/Scripts.Integration.Test/IntegrationGlobals.ps1 @@ -151,7 +151,7 @@ function TestDsnFor([string] $platform) return $dsn } -function RunUnityCustom([string] $unityPath, [string[]] $arguments) +function RunUnityCustom([string] $unityPath, [string[]] $arguments, [switch] $ReturnLogOutput) { If ($unityPath.StartsWith("docker ")) { @@ -161,5 +161,5 @@ function RunUnityCustom([string] $unityPath, [string[]] $arguments) } - return RunUnity $unityPath $arguments + return RunUnity $unityPath $arguments -ReturnLogOutput:$ReturnLogOutput } \ No newline at end of file From 9619a250a5577e6a78459b559f479954618ee87f Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Tue, 3 May 2022 10:34:36 +0200 Subject: [PATCH 23/28] chore: update smoke-test script for macOS --- scripts/unity-utils.ps1 | 12 +++++++----- .../integration-run-smoke-test.ps1 | 15 +++++++++++++-- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/scripts/unity-utils.ps1 b/scripts/unity-utils.ps1 index b5986e1ae..fa0cf96e8 100644 --- a/scripts/unity-utils.ps1 +++ b/scripts/unity-utils.ps1 @@ -63,13 +63,15 @@ function RunUnity([string] $unityPath, [string[]] $arguments, [switch] $ReturnLo } else { - if ($process.ExitCode -ne 0) - { - Throw "Unity exited with code $($process.ExitCode)" - } - return $ReturnLogOutput ? $stdout : $null + break } } while ($stopwatch.Elapsed.TotalSeconds -lt $RunUnityLicenseRetryTimeoutSeconds) + + if ($process.ExitCode -ne 0) + { + Throw "Unity exited with code $($process.ExitCode)" + } + return $ReturnLogOutput ? $stdout : $null } function ClearUnityLog([string] $logFilePath) diff --git a/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 b/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 index dd047d0f1..af9153d2a 100644 --- a/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 +++ b/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 @@ -100,6 +100,17 @@ if ($Smoke) { # Native crash test if ($Crash) { - CrashTestWithServer -SuccessString = "POST /api/12345/minidump/" -CrashTestCallback { RunTest "crash" } - RunTest "has-crashed" + if ($IsMacOS) + { + # Note: macOS apps post the crash on the second app launch, so we must run both as part of the "CrashTestWithServer" + CrashTestWithServer -SuccessString "POST /api/12345/envelope/ HTTP/1.1`" 200 -b'1f8b08000000000000" -CrashTestCallback { + RunTest "crash" "CRASH TEST: Issuing a native crash" + RunTest "has-crashed" + } + } + else + { + CrashTestWithServer -SuccessString = "POST /api/12345/minidump/" -CrashTestCallback { RunTest "crash" } + RunTest "has-crashed" + } } From 786b15db6403353148beaf2242b63ab378247ecb Mon Sep 17 00:00:00 2001 From: Ivan Dlugos <6349682+vaind@users.noreply.github.com> Date: Wed, 4 May 2022 14:34:35 +0200 Subject: [PATCH 24/28] Update scripts/unity.ps1 Co-authored-by: Stefan Jandl --- scripts/unity.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/unity.ps1 b/scripts/unity.ps1 index fa6307aac..5f70ef61c 100644 --- a/scripts/unity.ps1 +++ b/scripts/unity.ps1 @@ -3,5 +3,5 @@ $ErrorActionPreference = "Stop" . $PSScriptRoot/unity-utils.ps1 -# Redirecting the output to $null because RunUnity prints logs using Write-Host AND Write-Output - we don't need both here. +Log output is written to 'unity.log' RunUnity $args[0] ($args | Select-Object -Skip 1) \ No newline at end of file From 9e68701c19efbbcf7c8c7dde4c736ec68cf598cc Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Wed, 4 May 2022 18:01:13 +0200 Subject: [PATCH 25/28] refactor: macOS native bridge - add SentryConfigureScope() --- .../Plugins/macOS/SentryNativeBridge.m | 140 +++++++----------- 1 file changed, 57 insertions(+), 83 deletions(-) diff --git a/package-dev/Plugins/macOS/SentryNativeBridge.m b/package-dev/Plugins/macOS/SentryNativeBridge.m index ecb30b2d5..ba8e47e8d 100644 --- a/package-dev/Plugins/macOS/SentryNativeBridge.m +++ b/package-dev/Plugins/macOS/SentryNativeBridge.m @@ -63,6 +63,15 @@ void SentryNativeBridgeStartWithOptions(const void *options) [SentrySDK performSelector:@selector(startWithOptions:) withObject:dictOptions]; } +void SentryConfigureScope(void (^callback)(id)) +{ + @try { + [SentrySDK performSelector:@selector(configureScope:) withObject:callback]; + } @catch (NSException *exception) { + NSLog(@"Sentry (bridge): failed to configure scope: %@", exception.reason); + } +} + /*******************************************************************************/ /* The remaining code is a copy of iOS/SentryNativeBridge.m with changes to */ /* make it work with dynamically loaded classes. Mainly: */ @@ -98,8 +107,7 @@ void SentryNativeBridgeAddBreadcrumb( return; } - // declaring the (block) callback as a variable to avoid too much editor blank space on the left - void (^scopeUpdateBlock)(id) = ^void(id scope) { + SentryConfigureScope(^(id scope) { id breadcrumb = [[SentryBreadcrumb alloc] init]; if (timestamp != NULL) { @@ -125,13 +133,7 @@ void SentryNativeBridgeAddBreadcrumb( [breadcrumb setValue:[NSNumber numberWithInt:level] forKey:@"level"]; [scope performSelector:@selector(addBreadcrumb:) withObject:breadcrumb]; - }; - - @try { - [SentrySDK performSelector:@selector(configureScope:) withObject:scopeUpdateBlock]; - } @catch (NSException *exception) { - NSLog(@"Sentry (bridge): failed to add breadcrumb: %@", exception.reason); - } + }); } void SentryNativeBridgeSetExtra(const char *key, const char *value) @@ -140,21 +142,16 @@ void SentryNativeBridgeSetExtra(const char *key, const char *value) return; } - @try { - [SentrySDK performSelector:@selector(configureScope:) - withObject:^(id scope) { - if (value != NULL) { - [scope performSelector:@selector(setExtraValue:forKey:) - withObject:[NSString stringWithUTF8String:value] - withObject:[NSString stringWithUTF8String:key]]; - } else { - [scope performSelector:@selector(removeExtraForKey:) - withObject:[NSString stringWithUTF8String:key]]; - } - }]; - } @catch (NSException *exception) { - NSLog(@"Sentry (bridge): failed to set extra: %@", exception.reason); - } + SentryConfigureScope(^(id scope) { + if (value != NULL) { + [scope performSelector:@selector(setExtraValue:forKey:) + withObject:[NSString stringWithUTF8String:value] + withObject:[NSString stringWithUTF8String:key]]; + } else { + [scope performSelector:@selector(removeExtraForKey:) + withObject:[NSString stringWithUTF8String:key]]; + } + }); } void SentryNativeBridgeSetTag(const char *key, const char *value) @@ -163,21 +160,16 @@ void SentryNativeBridgeSetTag(const char *key, const char *value) return; } - @try { - [SentrySDK performSelector:@selector(configureScope:) - withObject:^(id scope) { - if (value != NULL) { - [scope performSelector:@selector(setTagValue:forKey:) - withObject:[NSString stringWithUTF8String:value] - withObject:[NSString stringWithUTF8String:key]]; - } else { - [scope performSelector:@selector(removeTagForKey:) - withObject:[NSString stringWithUTF8String:key]]; - } - }]; - } @catch (NSException *exception) { - NSLog(@"Sentry (bridge): failed to set tag: %@", exception.reason); - } + SentryConfigureScope(^(id scope) { + if (value != NULL) { + [scope performSelector:@selector(setTagValue:forKey:) + withObject:[NSString stringWithUTF8String:value] + withObject:[NSString stringWithUTF8String:key]]; + } else { + [scope performSelector:@selector(removeTagForKey:) + withObject:[NSString stringWithUTF8String:key]]; + } + }); } void SentryNativeBridgeUnsetTag(const char *key) @@ -186,15 +178,10 @@ void SentryNativeBridgeUnsetTag(const char *key) return; } - @try { - [SentrySDK performSelector:@selector(configureScope:) - withObject:^(id scope) { - [scope performSelector:@selector(removeTagForKey:) - withObject:[NSString stringWithUTF8String:key]]; - }]; - } @catch (NSException *exception) { - NSLog(@"Sentry (bridge): failed to unset tag: %@", exception.reason); - } + SentryConfigureScope(^(id scope) { + [scope performSelector:@selector(removeTagForKey:) + withObject:[NSString stringWithUTF8String:key]]; + }); } void SentryNativeBridgeSetUser( @@ -204,43 +191,30 @@ void SentryNativeBridgeSetUser( return; } - @try { - [SentrySDK - performSelector:@selector(configureScope:) - withObject:^(id scope) { - id user = [[SentryUser alloc] init]; - - if (email != NULL) { - [user setValue:[NSString stringWithUTF8String:email] forKey:@"email"]; - } - - if (userId != NULL) { - [user setValue:[NSString stringWithUTF8String:userId] forKey:@"userId"]; - } - - if (ipAddress != NULL) { - [user setValue:[NSString stringWithUTF8String:ipAddress] - forKey:@"ipAddress"]; - } - - if (username != NULL) { - [user setValue:[NSString stringWithUTF8String:username] - forKey:@"username"]; - } - - [scope setUser:user]; - }]; - } @catch (NSException *exception) { - NSLog(@"Sentry (bridge): failed to set user: %@", exception.reason); - } + SentryConfigureScope(^(id scope) { + id user = [[SentryUser alloc] init]; + + if (email != NULL) { + [user setValue:[NSString stringWithUTF8String:email] forKey:@"email"]; + } + + if (userId != NULL) { + [user setValue:[NSString stringWithUTF8String:userId] forKey:@"userId"]; + } + + if (ipAddress != NULL) { + [user setValue:[NSString stringWithUTF8String:ipAddress] forKey:@"ipAddress"]; + } + + if (username != NULL) { + [user setValue:[NSString stringWithUTF8String:username] forKey:@"username"]; + } + + [scope setUser:user]; + }); } void SentryNativeBridgeUnsetUser() { - @try { - [SentrySDK performSelector:@selector(configureScope:) - withObject:^(id scope) { [scope setUser:nil]; }]; - } @catch (NSException *exception) { - NSLog(@"Sentry (bridge): failed to unset user: %@", exception.reason); - } + SentryConfigureScope(^(id scope) { [scope setUser:nil]; }); } From e93e599f31d6495d1f536d0badaf57a0476d0bf4 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos <6349682+vaind@users.noreply.github.com> Date: Thu, 5 May 2022 13:49:09 +0200 Subject: [PATCH 26/28] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b24b64119..5259fa31b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Features +- macOS native crash support ([#710](https://github.com/getsentry/sentry-unity/pull/710)) - The SentryUnityOptions now provide a method to disable the UnityLoggingIntegration ([#724](https://github.com/getsentry/sentry-unity/pull/724)) ### Fixes From feb0b9ac08221903aa2cc3fe87a78cc58acb9c8e Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 5 May 2022 14:53:59 +0200 Subject: [PATCH 27/28] chore: fix CI --- scripts/unity.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/unity.ps1 b/scripts/unity.ps1 index 5f70ef61c..d3436912d 100644 --- a/scripts/unity.ps1 +++ b/scripts/unity.ps1 @@ -3,5 +3,5 @@ $ErrorActionPreference = "Stop" . $PSScriptRoot/unity-utils.ps1 -Log output is written to 'unity.log' +# Log output is written to 'unity.log' RunUnity $args[0] ($args | Select-Object -Skip 1) \ No newline at end of file From 3dd16f3eae4c5c5abd42420f5f388527cb2520d6 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 5 May 2022 17:35:15 +0200 Subject: [PATCH 28/28] chore: review changes --- package-dev/Plugins/macOS/SentryNativeBridge.m | 15 ++++++++++----- src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs | 2 +- src/Sentry.Unity.iOS/SentryNative.cs | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/package-dev/Plugins/macOS/SentryNativeBridge.m b/package-dev/Plugins/macOS/SentryNativeBridge.m index ba8e47e8d..b43e67e1d 100644 --- a/package-dev/Plugins/macOS/SentryNativeBridge.m +++ b/package-dev/Plugins/macOS/SentryNativeBridge.m @@ -11,7 +11,7 @@ #define LOAD_CLASS_OR_BREAK(name) \ name = (__bridge Class)dlsym(dylib, "OBJC_CLASS_$_" #name); \ if (!name) { \ - NSLog(@"Sentry (native bridge): Couldn't load %@ class from the dynamic library", name); \ + NSLog(@"Sentry (bridge): Couldn't load class '" #name "' from the dynamic library"); \ break; \ } @@ -24,7 +24,7 @@ int SentryNativeBridgeLoadLibrary() do { void *dylib = dlopen("@executable_path/../PlugIns/Sentry.dylib", RTLD_LAZY); if (!dylib) { - NSLog(@"Sentry (native bridge): Couldn't load Sentry.dylib - dlopen() failed"); + NSLog(@"Sentry (bridge): Couldn't load Sentry.dylib - dlopen() failed"); break; } @@ -65,6 +65,9 @@ void SentryNativeBridgeStartWithOptions(const void *options) void SentryConfigureScope(void (^callback)(id)) { + // setValue:forKey: may throw if the property is not found; same for performSelector. + // Even though this shouldn't happen, better not take the chance of letting an unhandled + // exception while setting error info - it would just crash the app immediately. @try { [SentrySDK performSelector:@selector(configureScope:) withObject:callback]; } @catch (NSException *exception) { @@ -77,6 +80,7 @@ void SentryConfigureScope(void (^callback)(id)) /* make it work with dynamically loaded classes. Mainly: */ /* - call: [class performSelector:@selector(arg1:arg2:) */ /* withObject:arg1Value withObject:arg2Value]; */ +/* or xCode warns of class/instance method not found */ /* - use `id` as variable types */ /* - use [obj setValue:value forKey:@"prop"] instead of `obj.prop = value` */ /*******************************************************************************/ @@ -94,7 +98,7 @@ int SentryNativeBridgeCrashedLastRun() void SentryNativeBridgeClose() { @try { - [SentrySDK close]; + [SentrySDK performSelector:@selector(close)]; } @catch (NSException *exception) { NSLog(@"Sentry (bridge): failed to close: %@", exception.reason); } @@ -210,11 +214,12 @@ void SentryNativeBridgeSetUser( [user setValue:[NSString stringWithUTF8String:username] forKey:@"username"]; } - [scope setUser:user]; + [scope performSelector:@selector(setUser:) withObject:user]; }); } void SentryNativeBridgeUnsetUser() { - SentryConfigureScope(^(id scope) { [scope setUser:nil]; }); + SentryConfigureScope( + ^(id scope) { [scope performSelector:@selector(setUser:) withObject:nil]; }); } diff --git a/src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs b/src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs index 06049b58f..9f3c7314a 100644 --- a/src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs +++ b/src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs @@ -13,9 +13,9 @@ namespace Sentry.Unity.iOS /// internal static class SentryCocoaBridgeProxy { + // Note: used on macOS only public static bool Init(SentryUnityOptions options) { - // Note: used on macOS only if (LoadLibrary() != 1) { return false; diff --git a/src/Sentry.Unity.iOS/SentryNative.cs b/src/Sentry.Unity.iOS/SentryNative.cs index ea2ec06c2..4375c2e93 100644 --- a/src/Sentry.Unity.iOS/SentryNative.cs +++ b/src/Sentry.Unity.iOS/SentryNative.cs @@ -33,7 +33,7 @@ internal static void Configure(SentryUnityOptions options, RuntimePlatform platf } if (!SentryCocoaBridgeProxy.Init(options)) { - options.DiagnosticLogger?.LogWarning("Failed to initialzie the native SDK"); + options.DiagnosticLogger?.LogWarning("Failed to initialize the native SDK"); return; } options.ScopeObserver = new NativeScopeObserver("macOS", options);