Skip to content

Commit

Permalink
feat: macOS native crashpad handler integration & debug symbol upload
Browse files Browse the repository at this point in the history
  • Loading branch information
vaind committed Apr 22, 2022
1 parent 9130e34 commit 22448f1
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 37 deletions.
6 changes: 3 additions & 3 deletions package-dev/Runtime/Sentry.Unity.Native.dll.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 9 additions & 4 deletions package-dev/Runtime/SentryInitialization.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
Expand All @@ -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);
Expand Down
49 changes: 35 additions & 14 deletions src/Sentry.Unity.Editor/Native/BuildPostProcess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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))
Expand Down Expand Up @@ -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
Expand Down
8 changes: 7 additions & 1 deletion src/Sentry.Unity.Native/SentryNative.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Sentry.Extensibility;
using Sentry.Unity.Integrations;
using System.Collections.Generic;
using UnityEngine;

namespace Sentry.Unity.Native
{
Expand All @@ -17,7 +18,12 @@ public static class SentryNative
/// <param name="options">The Sentry Unity options to use.</param>
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 += () =>
Expand Down
26 changes: 11 additions & 15 deletions src/Sentry.Unity.Native/SentryNativeBridge.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,9 @@ namespace Sentry.Unity
/// <summary>
/// P/Invoke to `sentry-native` functions.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
/// <see href="https://github.com/getsentry/sentry-java"/>
/// <see href="https://github.com/getsentry/sentry-native"/>
public static class SentryNativeBridge
{

public static bool CrashedLastRun;

public static void Init(SentryUnityOptions options)
Expand Down Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions src/Sentry.Unity/SentryUnity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 22448f1

Please sign in to comment.