Skip to content

Commit

Permalink
Use Java crash handler on not extracted libraries (#218)
Browse files Browse the repository at this point in the history
* Update native android libraries to version 3.8.3

* Update NativeClient to use JavaCrashHandler on Android when NDK is not extracted

* Warning comment change
  • Loading branch information
konraddysput authored Jun 17, 2024
1 parent 153a6d6 commit f53f346
Show file tree
Hide file tree
Showing 8 changed files with 185 additions and 34 deletions.
4 changes: 2 additions & 2 deletions Android/BacktraceANRWatchdog.java
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package backtrace.io.backtrace_unity_android_plugin;
package backtraceio.unity;
import android.os.Debug;
import android.os.Handler;
import android.os.Looper;
Expand Down Expand Up @@ -72,7 +72,7 @@ public void run() {
Log.d(LOG_TAG, "Starting ANR watchdog. Anr timeout: " + this.timeout);

while (!shouldStop && !isInterrupted()) {
final backtrace.io.backtrace_unity_android_plugin.BacktraceThreadWatcher threadWatcher = new backtrace.io.backtrace_unity_android_plugin.BacktraceThreadWatcher(0, 0);
final BacktraceThreadWatcher threadWatcher = new BacktraceThreadWatcher(0, 0);
mainThreadHandler.post(new Runnable() {
@Override
public void run() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package backtrace.io.backtrace_unity_android_plugin;
package backtraceio.unity;

import android.os.Build;
import android.os.Looper;
Expand Down
41 changes: 41 additions & 0 deletions Android/BacktraceCrashHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package backtraceio.library.nativeCalls;

import android.util.Log;

import java.util.Map;

public class BacktraceCrashHandler {
private static final String LOG_TAG = BacktraceCrashHandler.class.getSimpleName();

public static final String BACKTRACE_CRASH_HANDLER = "BACKTRACE_UNITY_CRASH_HANDLER";


private static native boolean handleCrash(String[] args);

public static void main(String[] args) {
run(args, System.getenv());
}

public static boolean run(String[] args, Map<String, String> environmentVariables) {
if (environmentVariables == null) {
Log.e(LOG_TAG, "Cannot capture crash dump. Environment variables are undefined");
return false;
}

String crashHandlerLibrary = environmentVariables.get(BACKTRACE_CRASH_HANDLER);
if (crashHandlerLibrary == null) {
Log.e(LOG_TAG, String.format("Cannot capture crash dump. Cannot find %s environment variable", BACKTRACE_CRASH_HANDLER));
return false;
}
System.load(crashHandlerLibrary);

boolean result = handleCrash(args);
if (!result) {
Log.e(LOG_TAG, String.format("Cannot capture crash dump. Invocation parameters: %s", String.join(" ", args)));
return false;
}

Log.i(LOG_TAG, "Successfully ran crash handler code.");
return true;
}
}
32 changes: 32 additions & 0 deletions Android/BacktraceCrashHandler.java.meta

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

2 changes: 1 addition & 1 deletion Android/BacktraceCrashHelper.java
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package backtrace.io.backtrace_unity_android_plugin;
package backtraceio.unity;

import android.os.Handler;
import android.os.Looper;
Expand Down
2 changes: 1 addition & 1 deletion Android/BacktraceThreadWatcher.java
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package backtrace.io.backtrace_unity_android_plugin;
package backtraceio.unity;

/**
* This class is a representation of the state of the thread,
Expand Down
22 changes: 17 additions & 5 deletions Runtime/Model/Attributes/MachineAttributeProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,23 @@ private void IncludeOsInformation(IDictionary<string, string> attributes)
attributes["device.manufacturer"] = build.GetStatic<string>("MANUFACTURER").ToString();
attributes["device.brand"] = build.GetStatic<string>("BRAND").ToString();
attributes["device.product"] = build.GetStatic<string>("PRODUCT").ToString();
}
using (var version = new AndroidJavaClass("android.os.Build$VERSION"))
{
attributes["uname.version"] = version.GetStatic<string>("RELEASE").ToString();

using (var version = new AndroidJavaClass("android.os.Build$VERSION"))
{
attributes["device.sdk"] = version.GetStatic<int>("SDK_INT").ToString();
attributes["uname.version"] = version.GetStatic<string>("RELEASE").ToString();
var deviceSdkVersion = version.GetStatic<int>("SDK_INT");
attributes["device.sdk"] = deviceSdkVersion.ToString();
if(deviceSdkVersion >= 21)
{
string[] supportedAbis = build.GetStatic<string[]>("SUPPORTED_ABIS");

if (supportedAbis != null && supportedAbis.Length > 0)
{
attributes["device.abi"] = supportedAbis[0];
}

}
}
}
attributes["uname.fullname"] = Environment.OSVersion.Version.ToString();
#else
Expand All @@ -80,6 +91,7 @@ private void IncludeOsInformation(IDictionary<string, string> attributes)
attributes["uname.fullname"] = Environment.OSVersion.Version.ToString();
#endif
}

private void IncludeGraphicCardInformation(IDictionary<string, string> attributes)
{
// if a graphic card is not available
Expand Down
114 changes: 90 additions & 24 deletions Runtime/Native/Android/NativeClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Backtrace.Unity.Model.Breadcrumbs;
using Backtrace.Unity.Runtime.Native.Base;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
Expand All @@ -24,6 +25,9 @@ internal sealed class NativeClient : NativeClientBase, INativeClient
[DllImport("backtrace-native")]
private static extern bool Initialize(IntPtr submissionUrl, IntPtr databasePath, IntPtr handlerPath, IntPtr keys, IntPtr values, IntPtr attachments, bool enableClientSideUnwinding, int unwindingMode);

[DllImport("backtrace-native")]
private static extern bool InitializeJavaCrashHandler(IntPtr submissionUrl, IntPtr databasePath, IntPtr classPath, IntPtr keys, IntPtr values, IntPtr attachments, IntPtr environmentVariables);

[DllImport("backtrace-native")]
private static extern bool AddAttribute(IntPtr key, IntPtr value);

Expand Down Expand Up @@ -75,8 +79,12 @@ private void SetDefaultAttributeMaps()
_attributeMapping.Add("VmallocUsed", "system.memory.vmalloc.used");
_attributeMapping.Add("VmallocChunk", "system.memory.vmalloc.chunk");
}
// Android native interface paths
private const string _namespace = "backtrace.io.backtrace_unity_android_plugin";

// Android base native interface path
private const string _baseNamespace = "backtraceio";

// Unity-Android native interface path
private const string _namespace = "backtraceio.unity";

/// <summary>
/// unwinding mode
Expand All @@ -93,6 +101,16 @@ private void SetDefaultAttributeMaps()
/// </summary>
private readonly string _unhandledExceptionPath = string.Format("{0}.{1}", _namespace, "BacktraceAndroidBackgroundUnhandledExceptionHandler");

/// <summary>
/// Path to class responsible for generating and sending native dump on crash
/// </summary>
private readonly string _crashHandlerPath = string.Format("{0}.library.nativeCalls.BacktraceCrashHandler", _baseNamespace);

/// <summary>
/// Backtrace-Android native library name
/// </summary>
private readonly string _nativeLibraryName = "libbacktrace-native.so";

/// <summary>
/// Determine if android integration should be enabled
/// </summary>
Expand Down Expand Up @@ -255,37 +273,20 @@ private void HandleNativeCrashes(IDictionary<string, string> backtraceAttributes
return;
}

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

var libDirectory = GetNativeDirectoryPath();
if (string.IsNullOrEmpty(libDirectory) || !Directory.Exists(libDirectory))
{
libDirectory = GuessNativeDirectoryPath();
}
if (!Directory.Exists(libDirectory))
{
return;
}
const string crashpadHandlerName = "libcrashpad_handler.so";
var crashpadHandlerPath = Path.Combine(libDirectory, crashpadHandlerName);

if (string.IsNullOrEmpty(crashpadHandlerPath) || !File.Exists(crashpadHandlerPath))
{
Debug.LogWarning("Backtrace native integration status: Cannot find crashpad library");
return;
}

var minidumpUrl = new BacktraceCredentials(_configuration.GetValidServerUrl()).GetMinidumpSubmissionUrl().ToString();
CaptureNativeCrashes = CanInitializeExecutableCrashHandler(libDirectory, crashpadHandlerPath)
? InitializeExecutableCrashHandler(minidumpUrl, databasePath, crashpadHandlerPath, attachments)
: InitializeJavaCrashHandler(minidumpUrl, databasePath, backtraceAttributes["device.abi"], libDirectory, attachments);

// reassign to captureNativeCrashes
// to avoid doing anything on crashpad binary, when crashpad isn't available
CaptureNativeCrashes = Initialize(
AndroidJNI.NewStringUTF(minidumpUrl),
AndroidJNI.NewStringUTF(databasePath),
AndroidJNI.NewStringUTF(crashpadHandlerPath),
AndroidJNIHelper.ConvertToJNIArray(new string[0]),
AndroidJNIHelper.ConvertToJNIArray(new string[0]),
AndroidJNIHelper.ConvertToJNIArray(attachments.ToArray()),
_enableClientSideUnwinding,
(int)UnwindingMode);
if (!CaptureNativeCrashes)
{
Debug.LogWarning("Backtrace native integration status: Cannot initialize Crashpad client");
Expand All @@ -309,6 +310,71 @@ private void HandleNativeCrashes(IDictionary<string, string> backtraceAttributes
AndroidJNI.NewStringUTF(CrashType));
}

private bool CanInitializeExecutableCrashHandler(String nativeLibraryDirectory, String handlerPath) {
return Directory.Exists(nativeLibraryDirectory) && File.Exists(handlerPath);
}

private bool InitializeExecutableCrashHandler(String minidumpUrl, String databasePath, String crashpadHandlerPath, IEnumerable<String> attachments) {
return Initialize(
AndroidJNI.NewStringUTF(minidumpUrl),
AndroidJNI.NewStringUTF(databasePath),
AndroidJNI.NewStringUTF(crashpadHandlerPath),
AndroidJNIHelper.ConvertToJNIArray(new string[0]),
AndroidJNIHelper.ConvertToJNIArray(new string[0]),
AndroidJNIHelper.ConvertToJNIArray(attachments.ToArray()),
_enableClientSideUnwinding,
(int)UnwindingMode);
}

private bool InitializeJavaCrashHandler(String minidumpUrl, String databasePath, String deviceAbi, String nativeDirectory, IEnumerable<String> attachments) {
if (String.IsNullOrEmpty(deviceAbi)) {
Debug.LogWarning("Cannot determine device ABI");
return false;
}

var envVariableDictionary = Environment.GetEnvironmentVariables();
if (envVariableDictionary == null) {
Debug.LogWarning("Environment variables are not defined.");
return false;
}

// verify if the library is already extracted
var backtraceNativeLibraryPath = Path.Combine(nativeDirectory, _nativeLibraryName);
if (!File.Exists(backtraceNativeLibraryPath)) {
backtraceNativeLibraryPath = string.Format("{0}!/lib/{1}/{2}", Application.dataPath, deviceAbi, _nativeLibraryName);
}

// prepare native crash handler environment variables
List<String> environmentVariables = new List<string> () {
string.Format("CLASSPATH={0}", Application.dataPath),
string.Format("BACKTRACE_UNITY_CRASH_HANDLER={0}", backtraceNativeLibraryPath),
string.Format("LD_LIBRARY_PATH={0}", string.Join(":", nativeDirectory, Directory.GetParent(nativeDirectory), GetLibrarySystemPath(), "/data/local")),
"ANDROID_DATA=/data"
};

foreach (DictionaryEntry kvp in envVariableDictionary) {
environmentVariables.Add(string.Format("{0}={1}", kvp.Key, kvp.Value == null ? "NULL" : kvp.Value));
}


return InitializeJavaCrashHandler(
AndroidJNI.NewStringUTF(minidumpUrl),
AndroidJNI.NewStringUTF(databasePath),
AndroidJNI.NewStringUTF(_crashHandlerPath),
AndroidJNIHelper.ConvertToJNIArray(new string[0]),
AndroidJNIHelper.ConvertToJNIArray(new string[0]),
AndroidJNIHelper.ConvertToJNIArray(attachments.ToArray()),
AndroidJNIHelper.ConvertToJNIArray(environmentVariables.ToArray())
);
}

private string GetLibrarySystemPath() {
using (var systemClass = new AndroidJavaClass("java.lang.System"))
{
return systemClass.CallStatic<string>("getProperty", "java.library.path");
}
}

/// <summary>
/// Retrieve Backtrace Attributes from the Android native code.
/// </summary>
Expand Down

0 comments on commit f53f346

Please sign in to comment.