-
-
Notifications
You must be signed in to change notification settings - Fork 60
Expand file tree
/
Copy pathSentryInitialization.cs
More file actions
436 lines (392 loc) · 17.2 KB
/
SentryInitialization.cs
File metadata and controls
436 lines (392 loc) · 17.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
#if !UNITY_EDITOR
#if UNITY_IOS || (UNITY_STANDALONE_OSX && ENABLE_IL2CPP)
#define SENTRY_NATIVE_COCOA
#elif UNITY_ANDROID && ENABLE_IL2CPP
#define SENTRY_NATIVE_ANDROID
#elif UNITY_64 && (UNITY_STANDALONE_WIN || UNITY_STANDALONE_LINUX)
#define SENTRY_NATIVE
#elif UNITY_WEBGL
#define SENTRY_WEBGL
#endif
#endif
#if ENABLE_IL2CPP && UNITY_2020_3_OR_NEWER && (SENTRY_NATIVE_COCOA || SENTRY_NATIVE_ANDROID || SENTRY_NATIVE)
#define IL2CPP_LINENUMBER_SUPPORT
#endif
using System;
using JetBrains.Annotations;
using Sentry.Extensibility;
#if UNITY_2020_3_OR_NEWER
using System.Buffers;
using System.Runtime.InteropServices;
#endif
using UnityEngine;
using UnityEngine.Scripting;
#if SENTRY_NATIVE_COCOA
using Sentry.Unity.iOS;
#elif SENTRY_NATIVE_ANDROID
using Sentry.Unity.Android;
#elif SENTRY_NATIVE
using Sentry.Unity.Native;
#elif SENTRY_WEBGL
using Sentry.Unity.WebGL;
#elif SENTRY_DEFAULT
using Sentry.Unity.Default;
#endif
[assembly: AlwaysLinkAssembly]
namespace Sentry.Unity
{
public static class SentryInitialization
{
public const string StartupTransactionOperation = "app.start";
public static ISpan InitSpan;
private const string InitSpanOperation = "runtime.init";
public static ISpan SubSystemRegistrationSpan;
private const string SubSystemSpanOperation = "runtime.init.subsystem";
#if SENTRY_WEBGL
// On WebGL SubsystemRegistration is too early for the UnityWebRequestTransport and errors with 'URI empty'
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
#else
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
#endif
public static void Init()
{
var unityInfo = new SentryUnityInfo();
// Loading the options invokes the ScriptableOption`Configure` callback. Users can disable the SDK via code.
var options = ScriptableSentryUnityOptions.LoadSentryUnityOptions(unityInfo);
if (options != null && options.ShouldInitializeSdk())
{
// Register LifeCycle Callbacks so the SDK is aware of the app losing focus during startup
RegisterAndroidCallbacks(options.DiagnosticLogger);
// Certain integrations require access to preprocessor directives so we provide them as `.cs` and
// compile them with the game instead of precompiling them with the rest of the SDK.
// i.e. SceneManagerAPI requires UNITY_2020_3_OR_NEWER
SentryIntegrations.Configure(options);
// Configures scope sync and (by default) initializes the native SDK.
SetupNativeSdk(options, unityInfo);
SentryUnity.Init(options);
SetupStartupTracing(options);
}
else
{
// If the SDK is not `enabled` we're closing down the native layer as well. This is especially relevant
// in a `built-time-initialization` scenario where the native SDKs self-initialize.
#if SENTRY_NATIVE_COCOA
SentryNativeCocoa.Close(options, unityInfo);
#elif SENTRY_NATIVE_ANDROID
SentryNativeAndroid.Close(options, unityInfo);
#endif
}
}
private static void RegisterAndroidCallbacks([CanBeNull] IDiagnosticLogger logger)
{
#if UNITY_ANDROID && !UNITY_EDITOR
logger?.LogError("Registering Android LifeCycleCallbacks.");
try
{
using var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
using var activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
using var application = activity.Call<AndroidJavaObject>("getApplication");
AndroidJavaProxy lifecycleCallbacks = new AndroidLifecycleCallbacks(logger);
application.Call("registerActivityLifecycleCallbacks", lifecycleCallbacks);
}
catch (Exception ex)
{
logger?.LogError("Failed to register Android LifeCycleCallbacks.");
}
#endif
}
private class AndroidLifecycleCallbacks : AndroidJavaProxy
{
[CanBeNull] private IDiagnosticLogger _logger;
public AndroidLifecycleCallbacks([CanBeNull] IDiagnosticLogger logger) : base(
"android.app.Application$ActivityLifecycleCallbacks")
{
_logger = logger;
}
// App going to background
public void onActivityPaused(AndroidJavaObject activity)
{
_logger?.LogInfo("Application move to background. Pausing session.");
}
// App coming to foreground
public void onActivityResumed(AndroidJavaObject activity)
{
_logger?.LogInfo("Application move to foreground. Resuming session.");
}
public void onActivityCreated(AndroidJavaObject activity, AndroidJavaObject savedInstanceState) { }
public void onActivityStarted(AndroidJavaObject activity) { }
public void onActivityStopped(AndroidJavaObject activity) { }
public void onActivitySaveInstanceState(AndroidJavaObject activity, AndroidJavaObject outState) { }
public void onActivityDestroyed(AndroidJavaObject activity) { }
}
private static void SetupNativeSdk(SentryUnityOptions options, SentryUnityInfo unityInfo)
{
try
{
#if SENTRY_NATIVE_COCOA
SentryNativeCocoa.Configure(options, unityInfo);
#elif SENTRY_NATIVE_ANDROID
SentryNativeAndroid.Configure(options, unityInfo);
#elif SENTRY_NATIVE
SentryNative.Configure(options, unityInfo);
#elif SENTRY_WEBGL
SentryWebGL.Configure(options);
#endif
}
catch (DllNotFoundException e)
{
options.DiagnosticLogger?.LogError(e,
"Sentry native-error capture configuration failed to load a native library. This usually " +
"means the library is missing from the application bundle or the installation directory.");
}
catch (Exception e)
{
options.DiagnosticLogger?.LogError(e, "Sentry native error capture configuration failed.");
}
}
private static void SetupStartupTracing(SentryUnityOptions options)
{
#if !SENTRY_WEBGL
if (options.TracesSampleRate > 0.0f && options.AutoStartupTraces)
{
options.DiagnosticLogger?.LogInfo("Creating '{0}' transaction for runtime initialization.",
StartupTransactionOperation);
var runtimeStartTransaction =
SentrySdk.StartTransaction("runtime.initialization", StartupTransactionOperation);
SentrySdk.ConfigureScope(scope => scope.Transaction = runtimeStartTransaction);
options.DiagnosticLogger?.LogDebug("Creating '{0}' span.", InitSpanOperation);
InitSpan = runtimeStartTransaction.StartChild(InitSpanOperation, "runtime initialization");
options.DiagnosticLogger?.LogDebug("Creating '{0}' span.", SubSystemSpanOperation);
SubSystemRegistrationSpan = InitSpan.StartChild(SubSystemSpanOperation, "subsystem registration");
}
#endif
}
}
public class SentryUnityInfo : ISentryUnityInfo
{
public bool IL2CPP
{
get =>
#if ENABLE_IL2CPP
true;
#else
false;
#endif
}
public Il2CppMethods Il2CppMethods => _il2CppMethods;
private Il2CppMethods _il2CppMethods
// Lowest supported version to have all required methods below
#if !IL2CPP_LINENUMBER_SUPPORT
;
#else
= new Il2CppMethods(
Il2CppGcHandleGetTargetShim,
Il2CppNativeStackTraceShim,
il2cpp_free);
#pragma warning disable 8632
// The incoming `IntPtr` is a native `char*`, a pointer to a
// nul-terminated C string. This function converts it to a C# string,
// and also byte-swaps/truncates on ELF platforms.
private static string? SanitizeDebugId(IntPtr debugIdPtr)
{
if (debugIdPtr == IntPtr.Zero)
{
return null;
}
#if UNITY_ANDROID || UNITY_STANDALONE_LINUX
// For ELF platforms, the 20-byte `NT_GNU_BUILD_ID` needs to be
// turned into a "little-endian GUID", which means the first three
// components need to be byte-swapped appropriately.
// See: https://getsentry.github.io/symbolicator/advanced/symbol-server-compatibility/#identifiers
// We unconditionally byte-flip these as we assume that we only
// ever run on little-endian platforms. Additionally, we truncate
// this down from a 40-char build-id to a 32-char debug-id as well.
SwapHexByte(debugIdPtr, 0, 3);
SwapHexByte(debugIdPtr, 1, 2);
SwapHexByte(debugIdPtr, 4, 5);
SwapHexByte(debugIdPtr, 6, 7);
Marshal.WriteByte(debugIdPtr, 32, 0);
// This will swap the two hex-encoded bytes at offsets 1 and 2.
// Internally, it treats these as Int16, as the hex-encoding means
// they occupy 2 bytes each.
void SwapHexByte(IntPtr buffer, Int32 offset1, Int32 offset2)
{
var a = Marshal.ReadInt16(buffer, offset1 * 2);
var b = Marshal.ReadInt16(buffer, offset2 * 2);
Marshal.WriteInt16(buffer, offset2 * 2, a);
Marshal.WriteInt16(buffer, offset1 * 2, b);
}
// All other platforms we care about (Windows, macOS) already have
// an appropriate debug-id format for that platform so no modifications
// are needed.
#endif
return Marshal.PtrToStringAnsi(debugIdPtr);
}
#if UNITY_2023_1_OR_NEWER
private static IntPtr Il2CppGcHandleGetTargetShim(IntPtr gchandle) => il2cpp_gchandle_get_target(gchandle);
// Available in Unity `2013.3.12f1` (and later)
// Il2CppObject* il2cpp_gchandle_get_target(Il2CppGCHandle gchandle)
[DllImport("__Internal")]
private static extern IntPtr il2cpp_gchandle_get_target(IntPtr gchandle);
#else
private static IntPtr Il2CppGcHandleGetTargetShim(IntPtr gchandle) => il2cpp_gchandle_get_target(gchandle.ToInt32());
// Available in Unity `2019.4.34f1` (and later)
// Il2CppObject* il2cpp_gchandle_get_target(uint32_t gchandle)
[DllImport("__Internal")]
private static extern IntPtr il2cpp_gchandle_get_target(int gchandle);
#endif
// Available in Unity `2019.4.34f1` (and later)
// void il2cpp_free(void* ptr)
[DllImport("__Internal")]
private static extern void il2cpp_free(IntPtr ptr);
#if UNITY_2021_3_OR_NEWER
private static void Il2CppNativeStackTraceShim(IntPtr exc, out IntPtr addresses, out int numFrames, out string? imageUUID, out string? imageName)
{
var uuidBuffer = IntPtr.Zero;
var imageNameBuffer = IntPtr.Zero;
il2cpp_native_stack_trace(exc, out addresses, out numFrames, out uuidBuffer, out imageNameBuffer);
try
{
imageUUID = SanitizeDebugId(uuidBuffer);
imageName = (imageNameBuffer == IntPtr.Zero) ? null : Marshal.PtrToStringAnsi(imageNameBuffer);
}
finally
{
il2cpp_free(uuidBuffer);
il2cpp_free(imageNameBuffer);
}
}
// Definition from Unity `2021.3` (and later):
// void il2cpp_native_stack_trace(const Il2CppException * ex, uintptr_t** addresses, int* numFrames, char** imageUUID, char** imageName)
[DllImport("__Internal")]
private static extern void il2cpp_native_stack_trace(IntPtr exc, out IntPtr addresses, out int numFrames, out IntPtr imageUUID, out IntPtr imageName);
#else
private static void Il2CppNativeStackTraceShim(IntPtr exc, out IntPtr addresses, out int numFrames, out string? imageUUID, out string? imageName)
{
imageName = null;
// Unity 2020 does not *return* a newly allocated string as out-parameter,
// but rather expects a pre-allocated buffer it writes into.
// That buffer needs to have space for either:
// - A hex-encoded `LC_UUID` on MacOS (32)
// - A hex-encoded GUID + Age on Windows (40)
// - A hex-encoded `NT_GNU_BUILD_ID` on ELF (Android/Linux) (40)
// plus a terminating nul-byte.
var uuidBuffer = il2cpp_alloc(40 + 1);
il2cpp_native_stack_trace(exc, out addresses, out numFrames, uuidBuffer);
try
{
imageUUID = SanitizeDebugId(uuidBuffer);
}
finally
{
il2cpp_free(uuidBuffer);
}
}
// Available in Unity `2020.3` (possibly even sooner)
// void* il2cpp_alloc(size_t size)
[DllImport("__Internal")]
private static extern IntPtr il2cpp_alloc(uint size);
// Definition from Unity `2020.3`:
// void il2cpp_native_stack_trace(const Il2CppException * ex, uintptr_t** addresses, int* numFrames, char* imageUUID)
[DllImport("__Internal")]
private static extern void il2cpp_native_stack_trace(IntPtr exc, out IntPtr addresses, out int numFrames, IntPtr imageUUID);
#endif
#pragma warning restore 8632
#endif
public bool IsKnownPlatform()
{
var platform = Application.platform;
return
platform == RuntimePlatform.Android ||
platform == RuntimePlatform.IPhonePlayer ||
platform == RuntimePlatform.WindowsEditor ||
platform == RuntimePlatform.WindowsPlayer ||
platform == RuntimePlatform.OSXEditor ||
platform == RuntimePlatform.OSXPlayer ||
platform == RuntimePlatform.LinuxEditor ||
platform == RuntimePlatform.LinuxPlayer ||
platform == RuntimePlatform.WebGLPlayer
#if UNITY_2021_3_OR_NEWER
||
platform == RuntimePlatform.WindowsServer ||
platform == RuntimePlatform.OSXServer ||
platform == RuntimePlatform.LinuxServer
#endif
;
}
public bool IsLinux()
{
var platform = Application.platform;
return
platform == RuntimePlatform.LinuxPlayer
#if UNITY_2021_3_OR_NEWER
|| platform == RuntimePlatform.LinuxServer
#endif
;
}
public bool IsNativeSupportEnabled(SentryUnityOptions options, RuntimePlatform platform)
{
switch (platform)
{
case RuntimePlatform.Android:
return options.AndroidNativeSupportEnabled;
case RuntimePlatform.IPhonePlayer:
return options.IosNativeSupportEnabled;
case RuntimePlatform.WindowsPlayer:
return options.WindowsNativeSupportEnabled;
case RuntimePlatform.OSXPlayer:
return options.MacosNativeSupportEnabled;
case RuntimePlatform.LinuxPlayer:
return options.LinuxNativeSupportEnabled;
#if UNITY_2021_3_OR_NEWER
case RuntimePlatform.WindowsServer:
return options.WindowsNativeSupportEnabled;
case RuntimePlatform.OSXServer:
return options.MacosNativeSupportEnabled;
case RuntimePlatform.LinuxServer:
return options.LinuxNativeSupportEnabled;
#endif
default:
return false;
}
}
public bool IsSupportedBySentryNative(RuntimePlatform platform)
{
return platform == RuntimePlatform.Android
|| platform == RuntimePlatform.LinuxPlayer
|| platform == RuntimePlatform.WindowsPlayer
#if UNITY_2021_3_OR_NEWER
|| platform == RuntimePlatform.WindowsServer
|| platform == RuntimePlatform.OSXServer
|| platform == RuntimePlatform.LinuxServer
#endif
;
}
public string GetDebugImageType(RuntimePlatform platform)
{
switch (platform)
{
case RuntimePlatform.Android:
return "elf";
case RuntimePlatform.IPhonePlayer:
return "macho";
case RuntimePlatform.OSXPlayer:
return "macho";
case RuntimePlatform.LinuxPlayer:
return "elf";
case RuntimePlatform.WindowsPlayer:
return "pe";
#if UNITY_2021_3_OR_NEWER
case RuntimePlatform.WindowsServer:
return "pe";
case RuntimePlatform.OSXServer:
return "macho";
case RuntimePlatform.LinuxServer:
return "elf";
#endif
default:
return "unknown";
}
}
}
}