Skip to content

Commit

Permalink
Version 3.7.4 (#153)
Browse files Browse the repository at this point in the history
* Native client compilation issues in older .NET platforms (#134)

* Android compilation issues

* Add missing namespace

* Apply default when value is below limit (#133)

* Apply default when value is below limit

* use defaults and different cast for backtracedatabasesettings

* Backtrace-cocoa - purge invalid report on game startup

* Improvement/prevent duplicated anrs (#135)

* Safe way to serialize Backtrace response

* Configurable ANR watchdog timeout

* Formatting + logger changes

* Code review suggestions

* Version update

* Update CHANGELOG.md

* Update CHANGELOG.md

* Avoid using unsupported property in not-native builds (#140)

* Avoid using unsupported property in not-native builds

* Filter out whole base client

* Version update

* Invalid background exception behavior (#139)

* INvalid background exception behaviour

* remove cast

* Java background exception better variable names/comments

* correcet variable name

* Updates CHANGELOG for 3.7.2

* Do not test native client on not supported platforms

* Invalid cast (#144)

* prevent monitor disposing in the disable method (#143)

* Use game object name instead of 'backtrace'

* Version update + make sure backtrace-unity don't throw an exception when disk is full

* Typo

* Changelog

* Add attributes to runtime xception

* fix unable to delete reports when db path has trailing slash

* Update README.md (#152)

NDK16b support is now baked into the main.

* Invalid crashpad handler behavior on x86 devices (#149)

* Fixed crashpad handler for x86

* Different meta guid

* Different crashpad path to x86/x64 directories

* Disable native client on x86 devices on Windows

* Disable on editor mode

* Android memory usage improvements (#150)

* Small nits that should speedup anr algorithm

* Background exception handler

* Forward crashes in the background exception handler, capture and store unhandled android Exceptions

* ANR watchdog adjustements

* Version Update

* Apply suggestions from code review

Change request changes

Co-authored-by: Lysanne Pinto <[email protected]>

* Delay breadcrumbs integration initialization

* Version 3.7.4 preview 1

* Unity dynamic attributes (#154)

* Unity dynamic attributes

* background attribute

* Release improvements

* Preview version update

* Version 3.7.4

Co-authored-by: Vincent Lussenburg <[email protected]>
Co-authored-by: Lysanne Pinto <[email protected]>
Co-authored-by: Sarah Edkins <[email protected]>
Co-authored-by: Sarah Edkins <[email protected]>
Co-authored-by: jasoncdavis0 <[email protected]>
  • Loading branch information
6 people authored Mar 14, 2022
1 parent 18f4114 commit 4b8a959
Show file tree
Hide file tree
Showing 18 changed files with 148 additions and 71 deletions.
22 changes: 6 additions & 16 deletions Android/BacktraceANRWatchdog.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,8 @@
*/
public class BacktraceANRWatchdog extends Thread {

private static BacktraceANRWatchdog _instance;

private final static transient String LOG_TAG = BacktraceANRWatchdog.class.getSimpleName();


/**
* Enable debug mode - errors will not be sent if the debugger is connected
*/
private final boolean debug;
/**
* Handler for UI Thread - used to check if the thread is not blocked
*/
Expand Down Expand Up @@ -62,8 +55,6 @@ public BacktraceANRWatchdog(String gameObjectName, String methodName, int anrTim
this.methodName = methodName;
this.gameObjectName = gameObjectName;
this.timeout = anrTimeout;
this.debug = false;
BacktraceANRWatchdog._instance = this;
this.start();
}

Expand All @@ -72,10 +63,15 @@ public BacktraceANRWatchdog(String gameObjectName, String methodName, int anrTim
*/
@Override
public void run() {
if (Debug.isDebuggerConnected() || Debug.waitingForDebugger()) {
Log.d(LOG_TAG, "Detected a debugger connection. ANR Watchdog is disabled");
return;
}

Boolean reported = false;
Log.d(LOG_TAG, "Starting ANR watchdog. Anr timeout: " + this.timeout);

while (!shouldStop && !isInterrupted()) {
String dateTimeNow = Calendar.getInstance().getTime().toString();
final backtrace.io.backtrace_unity_android_plugin.BacktraceThreadWatcher threadWatcher = new backtrace.io.backtrace_unity_android_plugin.BacktraceThreadWatcher(0, 0);
mainThreadHandler.post(new Runnable() {
@Override
Expand All @@ -96,11 +92,6 @@ public void run() {
continue;
}

if (debug && (Debug.isDebuggerConnected() || Debug.waitingForDebugger())) {
Log.d(LOG_TAG, "ANR detected but will be ignored because debug mode " +
"is on and connected debugger");
continue;
}
if (reported) {
// skipping, because we already reported an ANR report for current ANR
continue;
Expand Down Expand Up @@ -131,6 +122,5 @@ public static void printStackTrace(StackTraceElement[] stackTrace, PrintWriter p
public void stopMonitoring() {
Log.d(LOG_TAG, "ANR handler has been disabled.");
shouldStop = true;
BacktraceANRWatchdog._instance = null;
}
}
30 changes: 19 additions & 11 deletions Android/BacktraceAndroidBackgroundUnhandledExceptionHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,34 +29,38 @@ public class BacktraceAndroidBackgroundUnhandledExceptionHandler implements Thre
private volatile boolean shouldStop = false;

private final String _gameObject;
private final String _methodName;
private final String _methodName;

public BacktraceAndroidBackgroundUnhandledExceptionHandler(String gameObject, String methodName) {
Log.d(LOG_TAG, "Initializing Android unhandled exception handler");
this._gameObject = gameObject;
this._methodName = methodName;

mRootHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this);
}

@Override
public void uncaughtException(final Thread thread, final Throwable throwable) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.CUPCAKE && mRootHandler != null && shouldStop == false) {
if(Looper.getMainLooper().getThread().getId() == thread.getId()) {
// prevent from sending exception happened to main thread - we will catch them via unity logger
return;
}
_lastCaughtBackgroundExceptionThread = thread;
_lastCaughtBackgroundException = throwable;
if (shouldStop == true) {
Log.d(LOG_TAG, "Background exception handler is disabled.");
finish();
return;
}
if (throwable instanceof Exception) {
String throwableType = throwable.getClass().getName();
Log.d(LOG_TAG, "Detected unhandled background thread exception. Exception type: " + throwableType + ". Reporting to Backtrace");
_lastCaughtBackgroundExceptionThread = thread;
_lastCaughtBackgroundException = throwable;
ReportThreadException(throwableType + " : " + throwable.getMessage(), stackTraceToString(throwable.getStackTrace()));
} else {
Log.d(LOG_TAG, "Detected android crash. Using native crash reporter to report an error.");
finish();
}
}

public void ReportThreadException(String message, String stackTrace) {
UnityPlayer.UnitySendMessage(this._gameObject, this._methodName, message + '\n' + stackTrace);
Log.d(LOG_TAG, "UnitySendMessageFinished. passing an exception object. Game object: " + this._gameObject + " method name: " + this._methodName);
}

private static String stackTraceToString(StackTraceElement[] stackTrace) {
Expand All @@ -73,13 +77,17 @@ private static void printStackTrace(StackTraceElement[] stackTrace, PrintWriter

public void finish() {
if (_lastCaughtBackgroundExceptionThread == null || _lastCaughtBackgroundException == null) {
Log.d(LOG_TAG, "pass unhandled exception to the thread root handler, because exception thread/background thread doesn't exist");
Log.d(LOG_TAG, "The exception object or the exception thread is not available. This is probably a bug.");
return;
}
if (shouldStop) {
Log.d(LOG_TAG, "Backtrace client has been disposed. The report won't be available.");
return;
}
Log.d(LOG_TAG, "The unhandled exception has been stored in the database.");
mRootHandler.uncaughtException(_lastCaughtBackgroundExceptionThread, _lastCaughtBackgroundException);
}


public void stopMonitoring() {
Log.d(LOG_TAG, "Uncaught exception handler has been disabled.");
shouldStop = true;
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Backtrace Unity Release Notes

## Version 3.7.4

Bugfixes
- Updates native libraries for Windows.
- Removes native support for x86 games on Windows.
- Improves the background thread exception handler on Android, which allows you to capture managed crashes and forward exception information to other exceptions handlers.
- Handles database paths in the configuration file that end with trailing slashes (“/”).
- Disables the dialog used to upload symbols for Android when Unity is running in batch mode.
- Fixes a problem with nullable breadcrumbs when initializing the Backtrace client.

## Version 3.7.3

Bugfixes
Expand Down
2 changes: 1 addition & 1 deletion Editor/Native/Android/SymbolsUpload.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public void OnPostprocessBuild(BuildReport report)

Debug.Log("Backtrace symbols upload. Detected Backtrace configuration with enabled symbols upload option.");
Debug.Log(string.Format("Configuration path {0}", path));
if (!EditorUtility.DisplayDialog("Backtrace symbols upload",
if (!Application.isBatchMode && !EditorUtility.DisplayDialog("Backtrace symbols upload",
"Would you like to upload generated symbols files for better debugging experience?",
"Yes", "Skip"))
{
Expand Down
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

[Backtrace](http://backtrace.io/)'s integration with Unity allows developers to capture and report log errors, handled and unhandled Unity exceptions, and native crashes to their Backtrace instance, instantly offering the ability to prioritize and debug software errors.

**Note**: For developers creating Android games on **Unity 2018.4 (NDK16b)** and requiring support for **native events on Android (crashes, ANR, low memory)** Backtrace **requires** you use the [3.6.0-ndk16b](https://github.com/backtrace-labs/backtrace-unity/tree/3.6.0-ndk16b) version.

Create your Backtrace instance at https://register.backtrace.io/signup/ today and then integrate this library into your game.

[![openupm](https://img.shields.io/npm/v/io.backtrace.unity?label=openupm&registry_uri=https://package.openupm.com)](https://openupm.com/packages/io.backtrace.unity/)
Expand Down
4 changes: 2 additions & 2 deletions Runtime/BacktraceClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ namespace Backtrace.Unity
/// </summary>
public class BacktraceClient : MonoBehaviour, IBacktraceClient
{
public const string VERSION = "3.7.3";
public const string VERSION = "3.7.4";
internal const string DefaultBacktraceGameObjectName = "BacktraceClient";
public BacktraceConfiguration Configuration;

Expand Down Expand Up @@ -536,8 +536,8 @@ public void Refresh()
Database = GetComponent<BacktraceDatabase>();
if (Database != null)
{
_breadcrumbs = (BacktraceBreadcrumbs)Database.Breadcrumbs;
Database.Reload();
_breadcrumbs = (BacktraceBreadcrumbs)Database.Breadcrumbs;
Database.SetApi(BacktraceApi);
Database.SetReportWatcher(_reportLimitWatcher);
if (_breadcrumbs != null)
Expand Down
11 changes: 11 additions & 0 deletions Runtime/Common/ClientPathHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,16 @@ private static string GenerateFullPath(this string path)
}

}

internal static bool IsFileInDatabaseDirectory(string databasePath, string filePath)
{
// If databasePath does not have a trailing slash, it is already a directory.
if (!databasePath.EndsWith("/"))
{
return new DirectoryInfo(databasePath).FullName == new DirectoryInfo(Path.GetDirectoryName(filePath)).FullName;
}
// Handles case when users put a trailing slash in their database path
return Path.GetDirectoryName(databasePath) == Path.GetDirectoryName(filePath);
}
}
}
4 changes: 4 additions & 0 deletions Runtime/Model/Attributes/ProcessAttributeProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ public void GetAttributes(IDictionary<string, string> attributes)
attributes["system.memory.temp"] = Profiler.GetTempAllocatorSize().ToString(CultureInfo.InvariantCulture);
attributes["mono.heap"] = Profiler.GetMonoHeapSizeLong().ToString(CultureInfo.InvariantCulture);
attributes["mono.used"] = Profiler.GetMonoUsedSizeLong().ToString(CultureInfo.InvariantCulture);
attributes["application.playing"] = Application.isPlaying.ToString(CultureInfo.InvariantCulture);
attributes["application.focused"] = Application.isFocused.ToString(CultureInfo.InvariantCulture);
attributes["application.background"] = Application.runInBackground.ToString(CultureInfo.InvariantCulture);
attributes["application.internet_reachability"] = Application.internetReachability.ToString();

}
}
Expand Down
8 changes: 2 additions & 6 deletions Runtime/Model/Attributes/RuntimeAttributeProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,8 @@ public void GetAttributes(IDictionary<string, string> attributes)
attributes["application.data_path"] = Application.dataPath;
attributes["application.id"] = Application.identifier;
attributes["application.installer.name"] = Application.installerName;
attributes["application.internet_reachability"] = Application.internetReachability.ToString();
attributes["application.editor"] = Application.isEditor.ToString(CultureInfo.InvariantCulture);
attributes["application.focused"] = Application.isFocused.ToString(CultureInfo.InvariantCulture);
attributes["application.mobile"] = Application.isMobilePlatform.ToString(CultureInfo.InvariantCulture);
attributes["application.playing"] = Application.isPlaying.ToString(CultureInfo.InvariantCulture);
attributes["application.background"] = Application.runInBackground.ToString(CultureInfo.InvariantCulture);
attributes["application.editor"] = Application.isEditor.ToString(CultureInfo.InvariantCulture);
attributes["application.mobile"] = Application.isMobilePlatform.ToString(CultureInfo.InvariantCulture);
attributes["application.sandboxType"] = Application.sandboxType.ToString();
attributes["application.system.language"] = Application.systemLanguage.ToString();
attributes["application.unity.version"] = Application.unityVersion;
Expand Down
10 changes: 10 additions & 0 deletions Runtime/Model/Breadcrumbs/Storage/BacktraceStorageLogManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ public BacktraceStorageLogManager(string storagePath)
/// <returns>true if breadcrumbs file was created. Otherwise false.</returns>
public bool Enable()
{
if (currentSize != 0)
{
return true;
}
try
{
if (BreadcrumbFile.Exists())
Expand All @@ -116,6 +120,7 @@ public bool Enable()
_breadcrumbStream.Write(StartOfDocument, 0, StartOfDocument.Length);
_breadcrumbStream.Write(EndOfDocument, 0, EndOfDocument.Length);
}
_emptyFile = true;
currentSize = StartOfDocument.Length + EndOfDocument.Length;
}
catch (Exception e)
Expand All @@ -136,6 +141,11 @@ public bool Enable()
/// <returns>True if breadcrumb was stored in the breadcrumbs file. Otherwise false.</returns>
public bool Add(string message, BreadcrumbLevel level, UnityEngineLogLevel type, IDictionary<string, string> attributes)
{
// file is not initialized
if (currentSize == 0)
{
return false;
}
byte[] bytes;
lock (_lockObject)
{
Expand Down
8 changes: 5 additions & 3 deletions Runtime/Native/Android/NativeClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,9 @@ public void HandleAnr()
NativeReport(AndroidJNI.NewStringUTF(AnrMessage), true);
// update error.type attribute in case when crash happen
SetAttribute(ErrorTypeAttribute, CrashType);
AddAttribute(
AndroidJNI.NewStringUTF(ErrorTypeAttribute),
AndroidJNI.NewStringUTF(CrashType));
}
}
}
Expand Down Expand Up @@ -495,8 +497,8 @@ public override void Disable()
{
if (CaptureNativeCrashes)
{
CaptureNativeCrashes = false;
DisableNativeIntegration();
CaptureNativeCrashes = false;
DisableNativeIntegration();
}
if (_anrWatcher != null)
{
Expand Down
4 changes: 3 additions & 1 deletion Runtime/Native/NativeClientFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ internal static class NativeClientFactory
{
internal static INativeClient CreateNativeClient(BacktraceConfiguration configuration, string gameObjectName, BacktraceBreadcrumbs breadcrumbs, IDictionary<string, string> attributes, ICollection<string> attachments)
{
#if UNITY_STANDALONE_WIN
#if UNITY_EDITOR
return null;
#elif UNITY_STANDALONE_WIN
return new Windows.NativeClient(configuration, breadcrumbs, attributes, attachments);
#elif UNITY_ANDROID
return new Android.NativeClient(configuration, breadcrumbs, attributes, attachments, gameObjectName);
Expand Down
55 changes: 40 additions & 15 deletions Runtime/Native/Windows/NativeClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,20 +81,35 @@ private void HandleNativeCrashes(IDictionary<string, string> clientAttributes, I
{
return;
}
var databasePath = _configuration.CrashpadDatabasePath;
if (string.IsNullOrEmpty(databasePath) || !Directory.Exists(_configuration.GetFullDatabasePath()))


var pluginDirectoryPath = GetPluginDirectoryPath();
if (!Directory.Exists(pluginDirectoryPath))
{
Debug.LogWarning("Backtrace native lib directory doesn't exist");
return;
}
// prevent from initialization in the x86 devices
const int intPtrSizeOnx86 = 4;
if (Isx86Build(pluginDirectoryPath) || IntPtr.Size == intPtrSizeOnx86)
{
Debug.LogWarning("Backtrace native integration status: database path doesn't exist");
return;
}

var crashpadHandlerPath = GetDefaultPathToCrashpadHandler();
if (!File.Exists(crashpadHandlerPath))
var crashpadHandlerPath = GetDefaultPathToCrashpadHandler(pluginDirectoryPath);
if (string.IsNullOrEmpty(crashpadHandlerPath) || !File.Exists(crashpadHandlerPath))
{
Debug.LogWarning("Backtrace native integration status: Cannot find path to Crashpad handler.");
return;
}

var databasePath = _configuration.CrashpadDatabasePath;
if (string.IsNullOrEmpty(databasePath) || !Directory.Exists(_configuration.GetFullDatabasePath()))
{
Debug.LogWarning("Backtrace native integration status: database path doesn't exist");
return;
}

var minidumpUrl = new BacktraceCredentials(_configuration.GetValidServerUrl()).GetMinidumpSubmissionUrl().ToString();

if (!Directory.Exists(databasePath))
Expand All @@ -117,7 +132,7 @@ private void HandleNativeCrashes(IDictionary<string, string> clientAttributes, I

foreach (var attribute in clientAttributes)
{
AddNativeAttribute(attribute.Key, attribute.Value);
AddNativeAttribute(attribute.Key, attribute.Value == null ? string.Empty : attribute.Value);
}

// add exception type to crashes handled by crashpad - all exception handled by crashpad
Expand All @@ -130,6 +145,15 @@ private void HandleNativeCrashes(IDictionary<string, string> clientAttributes, I
// attribute via attributes parameters.
AddNativeAttribute(ErrorTypeAttribute, CrashType);
}

private bool Isx86Build(string pluginDirectoryPath)
{
const string unsupportedx86BuildPath = "x86";
const string backtraceLib = "BacktraceCrashpadWindows.dll";
var buildPath = Path.Combine(pluginDirectoryPath, unsupportedx86BuildPath);
return File.Exists(Path.Combine(buildPath, backtraceLib));
}

public void GetAttributes(IDictionary<string, string> attributes)
{
return;
Expand Down Expand Up @@ -315,21 +339,22 @@ public static IEnumerator SendUnhandledGameCrashesOnGameStartup(ICollection<stri
}
}
}

private string GetPluginDirectoryPath()
{
const string pluginDir = "Plugins";
return Path.Combine(Application.dataPath, pluginDir);
}
/// <summary>
/// Generate path to Crashpad handler binary
/// </summary>
/// <returns>Path to crashpad handler binary</returns>
private string GetDefaultPathToCrashpadHandler()
private string GetDefaultPathToCrashpadHandler(string pluginDirectoryPath)
{
const string crashpadHandlerName = "crashpad_handler.dll";
const string pluginDir = "Plugins";
string architecture = IntPtr.Size == 8 ? "x86_64" : "x86";

string pluginPath = Path.Combine(pluginDir, architecture);
string pluginHandlerPath = Path.Combine(pluginPath, crashpadHandlerName);

// generate full path to .dll file in plugins dir.
return Path.Combine(Application.dataPath, pluginHandlerPath);
const string supportedArchitecture = "x86_64";
var architectureDirectory = Path.Combine(pluginDirectoryPath, supportedArchitecture);
return Path.Combine(architectureDirectory, crashpadHandlerName);

}
/// <summary>
Expand Down
Loading

0 comments on commit 4b8a959

Please sign in to comment.