diff --git a/Runtime/BacktraceClient.cs b/Runtime/BacktraceClient.cs index 4afb4d26..9029d0d5 100644 --- a/Runtime/BacktraceClient.cs +++ b/Runtime/BacktraceClient.cs @@ -320,6 +320,14 @@ public Action OnClientReportLimitReached private INativeClient _nativeClient; + internal INativeClient NativeClient + { + get + { + return _nativeClient; + } + } + public bool EnablePerformanceStatistics { get @@ -525,11 +533,6 @@ public void Refresh() #if !UNITY_WEBGL EnableMetrics(false); #endif - var nativeAttachments = _clientReportAttachments.ToList() - .Where(n => !string.IsNullOrEmpty(n)) - .OrderBy(System.IO.Path.GetFileName, StringComparer.InvariantCultureIgnoreCase) - .ToList(); - string breadcrumbsPath = string.Empty; if (Configuration.Enabled) { @@ -543,24 +546,17 @@ public void Refresh() if (_breadcrumbs != null) { breadcrumbsPath = _breadcrumbs.GetBreadcrumbLogPath(); - } } } - - // send minidump files generated by unity engine or unity game, not captured by Windows native integration - // this integration should start before native integration and before breadcrumbs integration - // to allow algorithm to send breadcrumbs file - if the breadcrumb file is available - var scopedAttributes = AttributeProvider.GenerateAttributes(false); -#if UNITY_STANDALONE_WIN - if (Configuration.SendUnhandledGameCrashesOnGameStartup && isActiveAndEnabled && Enabled) - { - StartCoroutine(Runtime.Native.Windows.NativeClient.SendUnhandledGameCrashesOnGameStartup(nativeAttachments, breadcrumbsPath, Configuration.GetFullDatabasePath(), BacktraceApi)); - } -#endif - if (Database != null) { + // send minidump files generated by unity engine or unity game, not captured by Windows native integration + // this integration should start before native integration and before breadcrumbs integration + // to allow algorithm to send breadcrumbs file - if the breadcrumb file is available + var scopedAttributes = AttributeProvider.GenerateAttributes(false); + + var nativeAttachments = GetNativeAttachments(); // avoid adding breadcurmbs file earlier - to avoid managing breadcrumb file in two places // in windows managed integration with unity crash handler // breadcrumb path is required by native integration and will be added just before native integration initialization @@ -1318,5 +1314,13 @@ private bool ShouldSkipReport(ReportFilterType type, Exception exception, string || (SkipReport != null && SkipReport.Invoke(type, exception, message)); } + + internal IList GetNativeAttachments() + { + return _clientReportAttachments + .Where(n => !string.IsNullOrEmpty(n)) + .OrderBy(System.IO.Path.GetFileName, StringComparer.InvariantCultureIgnoreCase) + .ToList(); + } } } diff --git a/Runtime/BacktraceDatabase.cs b/Runtime/BacktraceDatabase.cs index 33646f69..ff4eb13d 100644 --- a/Runtime/BacktraceDatabase.cs +++ b/Runtime/BacktraceDatabase.cs @@ -4,6 +4,7 @@ using Backtrace.Unity.Model.Breadcrumbs; using Backtrace.Unity.Model.Breadcrumbs.Storage; using Backtrace.Unity.Model.Database; +using Backtrace.Unity.Runtime.Native; using Backtrace.Unity.Services; using Backtrace.Unity.Types; using System; @@ -27,6 +28,7 @@ public class BacktraceDatabase : MonoBehaviour, IBacktraceDatabase private BacktraceBreadcrumbs _breadcrumbs; + private BacktraceClient _client; /// /// Backtrace Breadcrumbs /// @@ -167,7 +169,8 @@ public void Reload() // validate configuration if (Configuration == null) { - Configuration = GetComponent().Configuration; + _client = GetComponent(); + Configuration = _client.Configuration; } if (Instance != null) { @@ -261,10 +264,39 @@ private void Start() { return; } + string breadcrumbPath = string.Empty; + string breadcrumbArchive = string.Empty; + + if (Breadcrumbs != null) + { + breadcrumbPath = Breadcrumbs.GetBreadcrumbLogPath(); + breadcrumbArchive = Breadcrumbs.Archive(); + } // load reports from hard drive - LoadReports(); + LoadReports(breadcrumbPath, breadcrumbArchive); // remove orphaned files RemoveOrphaned(); + + // send minidump files generated by unity engine or unity game, not captured by Windows native integration + // this integration should start before native integration and before breadcrumbs integration + // to allow algorithm to send breadcrumbs file - if the breadcrumb file is available +#if UNITY_STANDALONE_WIN + + var isEnabled = Enable && Configuration.SendUnhandledGameCrashesOnGameStartup && isActiveAndEnabled; + var hasNativeConfiguration = _client && _client.NativeClient != null && _client.NativeClient is IStartupMinidumpSender; + if (isEnabled && hasNativeConfiguration) + { + var client = _client.NativeClient as IStartupMinidumpSender; + var attachments = _client.GetNativeAttachments(); + if(!string.IsNullOrEmpty(breadcrumbArchive)) + { + attachments.Add(breadcrumbArchive); + } + StartCoroutine(client.SendMinidumpOnStartup( + clientAttachments: attachments, + backtraceApi: BacktraceApi)); + } +#endif // enable breadcrumb support after finishing loading reports EnableBreadcrumbsSupport(); if (DatabaseSettings.AutoSendMode) @@ -618,7 +650,7 @@ protected virtual bool InitializeDatabasePaths() /// /// Load all records stored in database path /// - protected virtual void LoadReports() + protected virtual void LoadReports(string breadcrumbPath, string breadcrumbArchive) { if (!Enable) { @@ -629,15 +661,8 @@ protected virtual void LoadReports() { return; } - string breadcrumbPath = string.Empty; - string breadcrumbArchive = string.Empty; - - if (Breadcrumbs != null) - { - breadcrumbPath = Breadcrumbs.GetBreadcrumbLogPath(); - breadcrumbArchive = Breadcrumbs.Archive(); - } var shouldUseArchiveBreadcrumbArchive = !string.IsNullOrEmpty(breadcrumbArchive); + foreach (var file in files) { var record = BacktraceDatabaseRecord.ReadFromFile(file); @@ -751,6 +776,14 @@ private void IncrementBatchRetry() } } } + internal string GetBreadcrumbsPath() + { + if (_breadcrumbs == null) + { + return string.Empty; + } + return _breadcrumbs.GetBreadcrumbLogPath(); + } public bool EnableBreadcrumbsSupport() { diff --git a/Runtime/Native/IStartupMinidumpSender.cs b/Runtime/Native/IStartupMinidumpSender.cs new file mode 100644 index 00000000..bcfa64d5 --- /dev/null +++ b/Runtime/Native/IStartupMinidumpSender.cs @@ -0,0 +1,11 @@ +using Backtrace.Unity.Interfaces; +using System.Collections; +using System.Collections.Generic; + +namespace Backtrace.Unity.Runtime.Native +{ + internal interface IStartupMinidumpSender + { + IEnumerator SendMinidumpOnStartup(ICollection clientAttachments, IBacktraceApi backtraceApi); + } +} diff --git a/Runtime/Native/IStartupMinidumpSender.cs.meta b/Runtime/Native/IStartupMinidumpSender.cs.meta new file mode 100644 index 00000000..fb4a81c0 --- /dev/null +++ b/Runtime/Native/IStartupMinidumpSender.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6490c25dc6b3efb4499b856e9813b022 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Native/Windows/NativeClient.cs b/Runtime/Native/Windows/NativeClient.cs index 08f87d1e..5e5a837d 100644 --- a/Runtime/Native/Windows/NativeClient.cs +++ b/Runtime/Native/Windows/NativeClient.cs @@ -19,7 +19,7 @@ [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Backtrace.Unity.Tests.Runtime")] namespace Backtrace.Unity.Runtime.Native.Windows { - internal sealed class NativeClient : NativeClientBase, INativeClient + internal sealed class NativeClient : NativeClientBase, INativeClient, IStartupMinidumpSender { [Serializable] private class ScopedAttributesContainer @@ -242,7 +242,7 @@ public void SetAttribute(string key, string value) /// /// Read directory structure in the native crash directory and send new crashes to Backtrace /// - public static IEnumerator SendUnhandledGameCrashesOnGameStartup(ICollection clientAttachments, string breadcrumbPath, string databasePath, IBacktraceApi backtraceApi) + public IEnumerator SendMinidumpOnStartup(ICollection clientAttachments, IBacktraceApi backtraceApi) { // Path to the native crash directory string nativeCrashesDir = Path.Combine( @@ -258,25 +258,6 @@ public static IEnumerator SendUnhandledGameCrashesOnGameStartup(ICollection() : new List(clientAttachments); - // make sure - when user close game in the middle of sending data, the library won't have a chance to clean up temporary breadcurmb - // file. Becuase of that we prefer to always check if we need to clean something that left in the previous application session - - string breadcrumbsCopyName = string.Format("{0}-1", BacktraceStorageLogManager.BreadcrumbLogFilePrefix); - string breadcrumbCopyPath = Path.Combine(databasePath, breadcrumbsCopyName); - if (File.Exists(breadcrumbCopyPath)) - { - File.Delete(breadcrumbCopyPath); - } - - - // determine if handler should create a copy of a breadcrumb file - // on the application startup. This check also prevents a situation when - // algorithm will try to copy a breacrumb file when a breadcrumbs file doesn't exist - // Client prefers to make a copy of a breadcrumb file in the database directory. Otherwise, if database - // for any reason in new session is not available, algorithm shouldn't make a copy. - bool requireBreadcrumbsCopy = string.IsNullOrEmpty(breadcrumbPath) || string.IsNullOrEmpty(databasePath) ? false : true; - bool copiedFile = false; - var crashDirs = Directory.GetDirectories(nativeCrashesDir); IDictionary attributes = GetScopedAttributes(); @@ -299,23 +280,6 @@ public static IEnumerator SendUnhandledGameCrashesOnGameStartup(ICollection n != minidumpPath).ToList(); yield return backtraceApi.SendMinidump(minidumpPath, dumpAttachment, attributes, (BacktraceResult result) => { @@ -325,19 +289,6 @@ public static IEnumerator SendUnhandledGameCrashesOnGameStartup(ICollection