From 22448f1a266acf51c262c6eff0d673e5c4dfec1d Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Fri, 22 Apr 2022 18:55:19 +0200 Subject: [PATCH] feat: macOS native crashpad handler integration & debug symbol upload --- .../Runtime/Sentry.Unity.Native.dll.meta | 6 +-- package-dev/Runtime/SentryInitialization.cs | 13 +++-- .../Native/BuildPostProcess.cs | 49 +++++++++++++------ src/Sentry.Unity.Native/SentryNative.cs | 8 ++- src/Sentry.Unity.Native/SentryNativeBridge.cs | 26 +++++----- src/Sentry.Unity/SentryUnity.cs | 1 + 6 files changed, 66 insertions(+), 37 deletions(-) diff --git a/package-dev/Runtime/Sentry.Unity.Native.dll.meta b/package-dev/Runtime/Sentry.Unity.Native.dll.meta index 39b30bcb4..2735a9bc5 100644 --- a/package-dev/Runtime/Sentry.Unity.Native.dll.meta +++ b/package-dev/Runtime/Sentry.Unity.Native.dll.meta @@ -19,7 +19,7 @@ PluginImporter: Exclude Android: 1 Exclude Editor: 0 Exclude Linux64: 1 - Exclude OSXUniversal: 1 + Exclude OSXUniversal: 0 Exclude WebGL: 1 Exclude Win: 0 Exclude Win64: 0 @@ -53,9 +53,9 @@ PluginImporter: - first: Standalone: OSXUniversal second: - enabled: 0 + enabled: 1 settings: - CPU: None + CPU: AnyCPU - first: Standalone: Win second: diff --git a/package-dev/Runtime/SentryInitialization.cs b/package-dev/Runtime/SentryInitialization.cs index 2c6381e62..6fe534a41 100644 --- a/package-dev/Runtime/SentryInitialization.cs +++ b/package-dev/Runtime/SentryInitialization.cs @@ -3,8 +3,13 @@ #define SENTRY_NATIVE_IOS #elif UNITY_ANDROID #define SENTRY_NATIVE_ANDROID +// After enabling support for Linux, we can use UNITY_STANDALONE: +// #elif UNITY_STANDALONE && ENABLE_IL2CPP +// #define SENTRY_NATIVE_STANDALONE #elif UNITY_STANDALONE_WIN && ENABLE_IL2CPP -#define SENTRY_NATIVE_WINDOWS +#define SENTRY_NATIVE_STANDALONE +#elif UNITY_STANDALONE_OSX && ENABLE_IL2CPP +#define SENTRY_NATIVE_STANDALONE #elif UNITY_WEBGL #define SENTRY_WEBGL #endif @@ -15,9 +20,9 @@ #if SENTRY_NATIVE_IOS using Sentry.Unity.iOS; -#elif UNITY_ANDROID +#elif SENTRY_ANDROID using Sentry.Unity.Android; -#elif SENTRY_NATIVE_WINDOWS +#elif SENTRY_NATIVE_STANDALONE using Sentry.Unity.Native; #elif SENTRY_WEBGL using Sentry.Unity.WebGL; @@ -41,7 +46,7 @@ public static void Init() SentryNativeIos.Configure(options); #elif SENTRY_NATIVE_ANDROID SentryNativeAndroid.Configure(options, sentryUnityInfo); -#elif SENTRY_NATIVE_WINDOWS +#elif SENTRY_NATIVE_STANDALONE SentryNative.Configure(options); #elif SENTRY_WEBGL SentryWebGL.Configure(options); diff --git a/src/Sentry.Unity.Editor/Native/BuildPostProcess.cs b/src/Sentry.Unity.Editor/Native/BuildPostProcess.cs index b46cfe80f..df2966fc5 100644 --- a/src/Sentry.Unity.Editor/Native/BuildPostProcess.cs +++ b/src/Sentry.Unity.Editor/Native/BuildPostProcess.cs @@ -42,8 +42,9 @@ public static void OnPostProcessBuild(BuildTarget target, string executablePath) } 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) @@ -60,16 +61,31 @@ public static void OnPostProcessBuild(BuildTarget target, string executablePath) _ => false, }; - private static void AddCrashHandler(IDiagnosticLogger logger, string projectDir) + private static void AddCrashHandler(IDiagnosticLogger logger, BuildTarget target, string projectDir, string executableName) { - 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)); + string crashpadPath; + string targetPath; + if (target is (BuildTarget.StandaloneWindows or BuildTarget.StandaloneWindows64)) + { + crashpadPath = Path.Combine("Windows", "Sentry", "crashpad_handler.exe"); + targetPath = Path.Combine(projectDir, Path.GetFileName(crashpadPath)); + } + else if (target is BuildTarget.StandaloneOSX) + { + crashpadPath = Path.Combine("macOS", "Sentry", "crashpad_handler"); + targetPath = Path.Combine(projectDir, executableName, "Contents", "MacOS", Path.GetFileName(crashpadPath)); + } + 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 = SentryCliOptions.LoadCliOptions(); if (!cliOptions.IsValid(logger)) @@ -97,13 +113,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.StandaloneWindows or 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/libsentry.dylib.dSYM")); + } // Configure the process using the StartInfo properties. var process = new Process diff --git a/src/Sentry.Unity.Native/SentryNative.cs b/src/Sentry.Unity.Native/SentryNative.cs index 5c5624a6b..cd241aaf6 100644 --- a/src/Sentry.Unity.Native/SentryNative.cs +++ b/src/Sentry.Unity.Native/SentryNative.cs @@ -1,6 +1,7 @@ using Sentry.Extensibility; using Sentry.Unity.Integrations; using System.Collections.Generic; +using UnityEngine; namespace Sentry.Unity.Native { @@ -17,7 +18,12 @@ public static class SentryNative /// The Sentry Unity options to use. public static void Configure(SentryUnityOptions options) { - if (options.WindowsNativeSupportEnabled) + if (ApplicationAdapter.Instance.Platform switch + { + RuntimePlatform.WindowsPlayer => options.WindowsNativeSupportEnabled, + RuntimePlatform.OSXPlayer => options.MacosNativeSupportEnabled, + _ => false, + }) { SentryNativeBridge.Init(options); ApplicationAdapter.Instance.Quitting += () => diff --git a/src/Sentry.Unity.Native/SentryNativeBridge.cs b/src/Sentry.Unity.Native/SentryNativeBridge.cs index 32520f5b1..4fafe61c9 100644 --- a/src/Sentry.Unity.Native/SentryNativeBridge.cs +++ b/src/Sentry.Unity.Native/SentryNativeBridge.cs @@ -10,16 +10,9 @@ namespace Sentry.Unity /// /// P/Invoke to `sentry-native` functions. /// - /// - /// The `sentry-native` SDK on Android is brought in through the `sentry-android-ndk` - /// maven package. - /// On Standalone players on Windows and Linux it's build directly for those platforms. - /// - /// /// public static class SentryNativeBridge { - public static bool CrashedLastRun; public static void Init(SentryUnityOptions options) @@ -173,19 +166,22 @@ private static void nativeLog(int cLevel, string message, IntPtr args, IntPtr us // vsnprintf (to find out the length of the resulting buffer) & vsprintf (to actually format the message). if (message.Contains("%")) { - var formattedLength = vsnprintf(null, UIntPtr.Zero, message, args); - var buffer = new StringBuilder(formattedLength + 1); - vsprintf(buffer, message, args); - message = buffer.ToString(); + // TODO macOS & Linux support + // var formattedLength = vsnprintf(null, UIntPtr.Zero, message, args); + // var buffer = new StringBuilder(formattedLength + 1); + // vsprintf(buffer, message, args); + // message = buffer.ToString(); } logger.Log(level, $"Native: {message}"); } - [DllImport("msvcrt.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - private static extern int vsprintf(StringBuilder buffer, string format, IntPtr args); + // [DllImport("msvcrt.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + // [DllImport("__Internal", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + // private static extern int vsprintf(StringBuilder buffer, string format, IntPtr args); - [DllImport("msvcrt.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - private static extern int vsnprintf(string? buffer, UIntPtr bufferSize, string format, IntPtr args); + // [DllImport("msvcrt.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + // [DllImport("__Internal", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + // private static extern int vsnprintf(string? buffer, UIntPtr bufferSize, string format, IntPtr args); [DllImport("sentry")] private static extern void sentry_init(IntPtr options); diff --git a/src/Sentry.Unity/SentryUnity.cs b/src/Sentry.Unity/SentryUnity.cs index 5263ed4db..535dff8ba 100644 --- a/src/Sentry.Unity/SentryUnity.cs +++ b/src/Sentry.Unity/SentryUnity.cs @@ -38,6 +38,7 @@ public static void Init(SentryUnityOptions options) // On Standalone, we disable cache dir in case multiple app instances run over the same path. // Note: we cannot use a named Mutex, because Unit doesn't support it. Instead, we create a file with `FileShare.None`. // https://forum.unity.com/threads/unsupported-internal-call-for-il2cpp-mutex-createmutex_internal-named-mutexes-are-not-supported.387334/ + // TODO macOS & Linux implementation if (ApplicationAdapter.Instance.Platform is RuntimePlatform.WindowsPlayer && options.CacheDirectoryPath is not null) { try