408 lines
13 KiB
C#
408 lines
13 KiB
C#
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// Licensed under the MIT license.
|
|
|
|
using Microsoft.AppCenter.Unity.Crashes.Internal;
|
|
using Microsoft.AppCenter.Unity.Internal.Utils;
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace Microsoft.AppCenter.Unity.Crashes
|
|
{
|
|
#if UNITY_IOS || UNITY_ANDROID
|
|
using RawType = System.IntPtr;
|
|
#else
|
|
using RawType = System.Type;
|
|
#endif
|
|
|
|
public class Crashes
|
|
{
|
|
// Used by App Center Unity Editor Extensions: https://github.com/Microsoft/AppCenter-SDK-Unity-Extension
|
|
public const string CrashesSDKVersion = "4.3.0";
|
|
private static bool _reportUnhandledExceptions = false;
|
|
private static bool _enableErrorAttachmentsCallbacks = false;
|
|
private static readonly object _objectLock = new object();
|
|
|
|
#if !UNITY_WSA_10_0
|
|
private static Queue<Exception> _unhandledExceptions = new Queue<Exception>();
|
|
#endif
|
|
|
|
public static void PrepareEventHandlers()
|
|
{
|
|
AppCenterBehavior.InitializingServices += Initialize;
|
|
AppCenterBehavior.InitializedAppCenterAndServices += HandleAppCenterInitialized;
|
|
}
|
|
|
|
public static void Initialize()
|
|
{
|
|
CrashesDelegate.SetDelegate();
|
|
}
|
|
|
|
public static void AddNativeType(List<RawType> nativeTypes)
|
|
{
|
|
CrashesInternal.AddNativeType(nativeTypes);
|
|
}
|
|
|
|
public static void TrackError(Exception exception, IDictionary<string, string> properties = null, params ErrorAttachmentLog[] attachments)
|
|
{
|
|
if (exception != null)
|
|
{
|
|
var exceptionWrapper = CreateWrapperException(exception);
|
|
CrashesInternal.TrackException(exceptionWrapper.GetRawObject(), properties, attachments);
|
|
}
|
|
}
|
|
|
|
public static void OnHandleLog(string logString, string stackTrace, LogType type)
|
|
{
|
|
if (LogType.Assert == type || LogType.Exception == type || LogType.Error == type)
|
|
{
|
|
var exception = CreateWrapperException(logString, stackTrace, type);
|
|
var errorReportId = CrashesInternal.TrackException(exception.GetRawObject(), null, null);
|
|
if (_enableErrorAttachmentsCallbacks)
|
|
{
|
|
SendErrorAttachments(errorReportId);
|
|
}
|
|
}
|
|
}
|
|
|
|
#if !UNITY_WSA_10_0
|
|
public static void OnHandleUnresolvedException(object sender, UnhandledExceptionEventArgs args)
|
|
{
|
|
if (args == null || args.ExceptionObject == null)
|
|
{
|
|
return;
|
|
}
|
|
var exception = args.ExceptionObject as Exception;
|
|
if (exception != null)
|
|
{
|
|
Debug.Log("Unhandled exception: " + exception.ToString());
|
|
#if UNITY_IOS && !UNITY_EDITOR
|
|
TrackErrorWithAttachments(exception);
|
|
#else
|
|
lock (_unhandledExceptions)
|
|
{
|
|
_unhandledExceptions.Enqueue(exception);
|
|
}
|
|
UnityCoroutineHelper.StartCoroutine(SendUnhandledExceptionReports);
|
|
#endif
|
|
}
|
|
}
|
|
#endif
|
|
|
|
public static AppCenterTask<string> GetMinidumpDirectoryAsync()
|
|
{
|
|
return CrashesInternal.GetMinidumpDirectoryAsync();
|
|
}
|
|
|
|
public static AppCenterTask<bool> HasReceivedMemoryWarningInLastSessionAsync()
|
|
{
|
|
return CrashesInternal.HasReceivedMemoryWarningInLastSessionAsync();
|
|
}
|
|
|
|
public static AppCenterTask<bool> IsEnabledAsync()
|
|
{
|
|
return CrashesInternal.IsEnabledAsync();
|
|
}
|
|
|
|
public static AppCenterTask SetEnabledAsync(bool enabled)
|
|
{
|
|
return CrashesInternal.SetEnabledAsync(enabled);
|
|
}
|
|
|
|
public static void GenerateTestCrash()
|
|
{
|
|
CrashesInternal.GenerateTestCrash();
|
|
}
|
|
|
|
public static AppCenterTask<bool> HasCrashedInLastSessionAsync()
|
|
{
|
|
return CrashesInternal.HasCrashedInLastSessionAsync();
|
|
}
|
|
|
|
public static void DisableMachExceptionHandler()
|
|
{
|
|
CrashesInternal.DisableMachExceptionHandler();
|
|
}
|
|
|
|
public static AppCenterTask<ErrorReport> GetLastSessionCrashReportAsync()
|
|
{
|
|
return CrashesInternal.GetLastSessionCrashReportAsync();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Report unhandled exceptions, automatically captured by Unity, as handled errors
|
|
/// </summary>
|
|
/// <param name="enabled">Specify true to enable reporting of unhandled exceptions, automatically captured by Unity, as handled errors; otherwise, false.</param>
|
|
/// <param name="enableAttachmentsCallback">Specify true to enable a callback that gives the app an opportunity to augment crash reports with additional attachments.</param>
|
|
public static void ReportUnhandledExceptions(bool enabled, bool enableAttachmentsCallback = false)
|
|
{
|
|
if (!enabled && enableAttachmentsCallback)
|
|
{
|
|
Debug.LogWarning("Cannot enable attachments callbacks without enabling unhandled exception reporting.");
|
|
}
|
|
else
|
|
{
|
|
_enableErrorAttachmentsCallbacks = enableAttachmentsCallback;
|
|
}
|
|
if (_reportUnhandledExceptions == enabled)
|
|
{
|
|
return;
|
|
}
|
|
_reportUnhandledExceptions = enabled;
|
|
if (enabled)
|
|
{
|
|
SubscribeToUnhandledExceptions();
|
|
}
|
|
else
|
|
{
|
|
UnsubscribeFromUnhandledExceptions();
|
|
}
|
|
}
|
|
|
|
public static bool IsReportingUnhandledExceptions()
|
|
{
|
|
return _reportUnhandledExceptions;
|
|
}
|
|
|
|
#if ENABLE_IL2CPP
|
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
|
#endif
|
|
public delegate bool UserConfirmationHandler();
|
|
|
|
public static UserConfirmationHandler ShouldAwaitUserConfirmation
|
|
{
|
|
set
|
|
{
|
|
CrashesInternal.SetUserConfirmationHandler(value);
|
|
}
|
|
}
|
|
|
|
public enum ConfirmationResult { DontSend, Send, AlwaysSend };
|
|
|
|
public static void NotifyUserConfirmation(ConfirmationResult answer)
|
|
{
|
|
CrashesInternal.NotifyWithUserConfirmation(answer);
|
|
}
|
|
|
|
#if ENABLE_IL2CPP
|
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
|
#endif
|
|
public delegate bool ShouldProcessErrorReportHandler(ErrorReport errorReport);
|
|
|
|
public static ShouldProcessErrorReportHandler ShouldProcessErrorReport
|
|
{
|
|
set
|
|
{
|
|
CrashesDelegate.SetShouldProcessErrorReportHandler(value);
|
|
}
|
|
}
|
|
|
|
#if ENABLE_IL2CPP
|
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
|
#endif
|
|
public delegate ErrorAttachmentLog[] GetErrorAttachmentsHandler(ErrorReport errorReport);
|
|
|
|
public static GetErrorAttachmentsHandler GetErrorAttachments
|
|
{
|
|
set
|
|
{
|
|
CrashesDelegate.SetGetErrorAttachmentsHandler(value);
|
|
}
|
|
}
|
|
|
|
#if ENABLE_IL2CPP
|
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
|
#endif
|
|
public delegate void SendingErrorReportHandler(ErrorReport errorReport);
|
|
|
|
public static event SendingErrorReportHandler SendingErrorReport
|
|
{
|
|
add
|
|
{
|
|
lock (_objectLock)
|
|
{
|
|
CrashesDelegate.SendingErrorReport += value;
|
|
}
|
|
}
|
|
remove
|
|
{
|
|
lock (_objectLock)
|
|
{
|
|
CrashesDelegate.SendingErrorReport -= value;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if ENABLE_IL2CPP
|
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
|
#endif
|
|
public delegate void SentErrorReportHandler(ErrorReport errorReport);
|
|
|
|
public static event SentErrorReportHandler SentErrorReport
|
|
{
|
|
add
|
|
{
|
|
lock (_objectLock)
|
|
{
|
|
CrashesDelegate.SentErrorReport += value;
|
|
}
|
|
}
|
|
remove
|
|
{
|
|
lock (_objectLock)
|
|
{
|
|
CrashesDelegate.SentErrorReport -= value;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if ENABLE_IL2CPP
|
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
|
#endif
|
|
public delegate void FailedToSendErrorReportHandler(ErrorReport errorReport, Models.Exception exception);
|
|
|
|
public static event FailedToSendErrorReportHandler FailedToSendErrorReport
|
|
{
|
|
add
|
|
{
|
|
lock (_objectLock)
|
|
{
|
|
CrashesDelegate.FailedToSendErrorReport += value;
|
|
}
|
|
}
|
|
remove
|
|
{
|
|
lock (_objectLock)
|
|
{
|
|
CrashesDelegate.FailedToSendErrorReport -= value;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void StartCrashes()
|
|
{
|
|
CrashesInternal.StartCrashes();
|
|
}
|
|
|
|
private static void SubscribeToUnhandledExceptions()
|
|
{
|
|
#if !UNITY_EDITOR && !UNITY_WSA_10_0
|
|
Application.logMessageReceived += OnHandleLog;
|
|
System.AppDomain.CurrentDomain.UnhandledException += OnHandleUnresolvedException;
|
|
#endif
|
|
}
|
|
|
|
private static void UnsubscribeFromUnhandledExceptions()
|
|
{
|
|
#if !UNITY_EDITOR && !UNITY_WSA_10_0
|
|
Application.logMessageReceived -= OnHandleLog;
|
|
System.AppDomain.CurrentDomain.UnhandledException -= OnHandleUnresolvedException;
|
|
#endif
|
|
}
|
|
|
|
private static void HandleAppCenterInitialized()
|
|
{
|
|
if (_reportUnhandledExceptions)
|
|
{
|
|
SubscribeToUnhandledExceptions();
|
|
}
|
|
}
|
|
|
|
#if !UNITY_WSA_10_0
|
|
private static IEnumerator SendUnhandledExceptionReports()
|
|
{
|
|
yield return null; // ensure that code is executed in main thread
|
|
while (true)
|
|
{
|
|
Exception exception = null;
|
|
lock (_unhandledExceptions)
|
|
{
|
|
if (_unhandledExceptions.Count > 0)
|
|
{
|
|
exception = _unhandledExceptions.Dequeue();
|
|
}
|
|
else
|
|
{
|
|
yield break;
|
|
}
|
|
}
|
|
if (exception != null)
|
|
{
|
|
TrackErrorWithAttachments(exception);
|
|
}
|
|
yield return null; // report remaining exceptions on next frames
|
|
}
|
|
}
|
|
#endif
|
|
|
|
private static void TrackErrorWithAttachments(Exception exception)
|
|
{
|
|
var exceptionWrapper = CreateWrapperException(exception);
|
|
var errorId = CrashesInternal.TrackException(exceptionWrapper.GetRawObject(), null, null);
|
|
|
|
// If the main thread is not crashed, attachments should be sent.
|
|
if (_enableErrorAttachmentsCallbacks)
|
|
{
|
|
SendErrorAttachments(errorId);
|
|
}
|
|
}
|
|
|
|
private static WrapperException CreateWrapperException(Exception exception)
|
|
{
|
|
var exceptionWrapper = new WrapperException();
|
|
exceptionWrapper.SetWrapperSdkName(GetExceptionWrapperSdkName());
|
|
exceptionWrapper.SetStacktrace(exception.StackTrace);
|
|
exceptionWrapper.SetMessage(exception.Message);
|
|
exceptionWrapper.SetType(exception.GetType().ToString());
|
|
|
|
if (exception.InnerException != null)
|
|
{
|
|
var innerExceptionWrapper = CreateWrapperException(exception.InnerException).GetRawObject();
|
|
exceptionWrapper.SetInnerException(innerExceptionWrapper);
|
|
}
|
|
|
|
return exceptionWrapper;
|
|
}
|
|
|
|
private static WrapperException CreateWrapperException(string logString, string stackTrace, LogType type)
|
|
{
|
|
var exception = new WrapperException();
|
|
exception.SetWrapperSdkName(GetExceptionWrapperSdkName());
|
|
|
|
string sanitizedLogString = logString.Replace("\n", " ");
|
|
exception.SetMessage(sanitizedLogString);
|
|
exception.SetType(type.ToString());
|
|
|
|
string[] stacktraceLines = stackTrace.Split('\n');
|
|
string stackTraceString = "";
|
|
foreach (string line in stacktraceLines)
|
|
{
|
|
if (line.Length > 0)
|
|
{
|
|
stackTraceString += "at " + line + "\n";
|
|
}
|
|
}
|
|
exception.SetStacktrace(stackTraceString);
|
|
|
|
return exception;
|
|
}
|
|
|
|
private static string GetExceptionWrapperSdkName()
|
|
{
|
|
//return WrapperSdk.Name;
|
|
return "appcenter.xamarin"; // fix stack traces are not showing up in the portal UI
|
|
}
|
|
|
|
private static void SendErrorAttachments(string errorReportId)
|
|
{
|
|
// Send attachments for error report.
|
|
var errorReport = CrashesInternal.BuildHandledErrorReport(errorReportId);
|
|
errorReport.IsCrash = false;
|
|
var attachments = CrashesDelegate.GetErrorAttachmentsHandler == null ? null : CrashesDelegate.GetErrorAttachmentsHandler(errorReport);
|
|
CrashesInternal.SendErrorAttachments(errorReportId, attachments);
|
|
}
|
|
}
|
|
}
|