Skip to content

Commit f53f346

Browse files
authored
Use Java crash handler on not extracted libraries (#218)
* Update native android libraries to version 3.8.3 * Update NativeClient to use JavaCrashHandler on Android when NDK is not extracted * Warning comment change
1 parent 153a6d6 commit f53f346

File tree

8 files changed

+185
-34
lines changed

8 files changed

+185
-34
lines changed

Android/BacktraceANRWatchdog.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package backtrace.io.backtrace_unity_android_plugin;
1+
package backtraceio.unity;
22
import android.os.Debug;
33
import android.os.Handler;
44
import android.os.Looper;
@@ -72,7 +72,7 @@ public void run() {
7272
Log.d(LOG_TAG, "Starting ANR watchdog. Anr timeout: " + this.timeout);
7373

7474
while (!shouldStop && !isInterrupted()) {
75-
final backtrace.io.backtrace_unity_android_plugin.BacktraceThreadWatcher threadWatcher = new backtrace.io.backtrace_unity_android_plugin.BacktraceThreadWatcher(0, 0);
75+
final BacktraceThreadWatcher threadWatcher = new BacktraceThreadWatcher(0, 0);
7676
mainThreadHandler.post(new Runnable() {
7777
@Override
7878
public void run() {

Android/BacktraceAndroidBackgroundUnhandledExceptionHandler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package backtrace.io.backtrace_unity_android_plugin;
1+
package backtraceio.unity;
22

33
import android.os.Build;
44
import android.os.Looper;

Android/BacktraceCrashHandler.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package backtraceio.library.nativeCalls;
2+
3+
import android.util.Log;
4+
5+
import java.util.Map;
6+
7+
public class BacktraceCrashHandler {
8+
private static final String LOG_TAG = BacktraceCrashHandler.class.getSimpleName();
9+
10+
public static final String BACKTRACE_CRASH_HANDLER = "BACKTRACE_UNITY_CRASH_HANDLER";
11+
12+
13+
private static native boolean handleCrash(String[] args);
14+
15+
public static void main(String[] args) {
16+
run(args, System.getenv());
17+
}
18+
19+
public static boolean run(String[] args, Map<String, String> environmentVariables) {
20+
if (environmentVariables == null) {
21+
Log.e(LOG_TAG, "Cannot capture crash dump. Environment variables are undefined");
22+
return false;
23+
}
24+
25+
String crashHandlerLibrary = environmentVariables.get(BACKTRACE_CRASH_HANDLER);
26+
if (crashHandlerLibrary == null) {
27+
Log.e(LOG_TAG, String.format("Cannot capture crash dump. Cannot find %s environment variable", BACKTRACE_CRASH_HANDLER));
28+
return false;
29+
}
30+
System.load(crashHandlerLibrary);
31+
32+
boolean result = handleCrash(args);
33+
if (!result) {
34+
Log.e(LOG_TAG, String.format("Cannot capture crash dump. Invocation parameters: %s", String.join(" ", args)));
35+
return false;
36+
}
37+
38+
Log.i(LOG_TAG, "Successfully ran crash handler code.");
39+
return true;
40+
}
41+
}

Android/BacktraceCrashHandler.java.meta

Lines changed: 32 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Android/BacktraceCrashHelper.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package backtrace.io.backtrace_unity_android_plugin;
1+
package backtraceio.unity;
22

33
import android.os.Handler;
44
import android.os.Looper;

Android/BacktraceThreadWatcher.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package backtrace.io.backtrace_unity_android_plugin;
1+
package backtraceio.unity;
22

33
/**
44
* This class is a representation of the state of the thread,

Runtime/Model/Attributes/MachineAttributeProvider.cs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,23 @@ private void IncludeOsInformation(IDictionary<string, string> attributes)
5858
attributes["device.manufacturer"] = build.GetStatic<string>("MANUFACTURER").ToString();
5959
attributes["device.brand"] = build.GetStatic<string>("BRAND").ToString();
6060
attributes["device.product"] = build.GetStatic<string>("PRODUCT").ToString();
61-
}
61+
using (var version = new AndroidJavaClass("android.os.Build$VERSION"))
62+
{
63+
attributes["uname.version"] = version.GetStatic<string>("RELEASE").ToString();
6264

63-
using (var version = new AndroidJavaClass("android.os.Build$VERSION"))
64-
{
65-
attributes["device.sdk"] = version.GetStatic<int>("SDK_INT").ToString();
66-
attributes["uname.version"] = version.GetStatic<string>("RELEASE").ToString();
65+
var deviceSdkVersion = version.GetStatic<int>("SDK_INT");
66+
attributes["device.sdk"] = deviceSdkVersion.ToString();
67+
if(deviceSdkVersion >= 21)
68+
{
69+
string[] supportedAbis = build.GetStatic<string[]>("SUPPORTED_ABIS");
70+
71+
if (supportedAbis != null && supportedAbis.Length > 0)
72+
{
73+
attributes["device.abi"] = supportedAbis[0];
74+
}
75+
76+
}
77+
}
6778
}
6879
attributes["uname.fullname"] = Environment.OSVersion.Version.ToString();
6980
#else
@@ -80,6 +91,7 @@ private void IncludeOsInformation(IDictionary<string, string> attributes)
8091
attributes["uname.fullname"] = Environment.OSVersion.Version.ToString();
8192
#endif
8293
}
94+
8395
private void IncludeGraphicCardInformation(IDictionary<string, string> attributes)
8496
{
8597
// if a graphic card is not available

Runtime/Native/Android/NativeClient.cs

Lines changed: 90 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using Backtrace.Unity.Model.Breadcrumbs;
66
using Backtrace.Unity.Runtime.Native.Base;
77
using System;
8+
using System.Collections;
89
using System.Collections.Generic;
910
using System.Globalization;
1011
using System.IO;
@@ -24,6 +25,9 @@ internal sealed class NativeClient : NativeClientBase, INativeClient
2425
[DllImport("backtrace-native")]
2526
private static extern bool Initialize(IntPtr submissionUrl, IntPtr databasePath, IntPtr handlerPath, IntPtr keys, IntPtr values, IntPtr attachments, bool enableClientSideUnwinding, int unwindingMode);
2627

28+
[DllImport("backtrace-native")]
29+
private static extern bool InitializeJavaCrashHandler(IntPtr submissionUrl, IntPtr databasePath, IntPtr classPath, IntPtr keys, IntPtr values, IntPtr attachments, IntPtr environmentVariables);
30+
2731
[DllImport("backtrace-native")]
2832
private static extern bool AddAttribute(IntPtr key, IntPtr value);
2933

@@ -75,8 +79,12 @@ private void SetDefaultAttributeMaps()
7579
_attributeMapping.Add("VmallocUsed", "system.memory.vmalloc.used");
7680
_attributeMapping.Add("VmallocChunk", "system.memory.vmalloc.chunk");
7781
}
78-
// Android native interface paths
79-
private const string _namespace = "backtrace.io.backtrace_unity_android_plugin";
82+
83+
// Android base native interface path
84+
private const string _baseNamespace = "backtraceio";
85+
86+
// Unity-Android native interface path
87+
private const string _namespace = "backtraceio.unity";
8088

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

104+
/// <summary>
105+
/// Path to class responsible for generating and sending native dump on crash
106+
/// </summary>
107+
private readonly string _crashHandlerPath = string.Format("{0}.library.nativeCalls.BacktraceCrashHandler", _baseNamespace);
108+
109+
/// <summary>
110+
/// Backtrace-Android native library name
111+
/// </summary>
112+
private readonly string _nativeLibraryName = "libbacktrace-native.so";
113+
96114
/// <summary>
97115
/// Determine if android integration should be enabled
98116
/// </summary>
@@ -255,37 +273,20 @@ private void HandleNativeCrashes(IDictionary<string, string> backtraceAttributes
255273
return;
256274
}
257275

276+
var minidumpUrl = new BacktraceCredentials(_configuration.GetValidServerUrl()).GetMinidumpSubmissionUrl().ToString();
277+
258278
var libDirectory = GetNativeDirectoryPath();
259279
if (string.IsNullOrEmpty(libDirectory) || !Directory.Exists(libDirectory))
260280
{
261281
libDirectory = GuessNativeDirectoryPath();
262282
}
263-
if (!Directory.Exists(libDirectory))
264-
{
265-
return;
266-
}
267283
const string crashpadHandlerName = "libcrashpad_handler.so";
268284
var crashpadHandlerPath = Path.Combine(libDirectory, crashpadHandlerName);
269285

270-
if (string.IsNullOrEmpty(crashpadHandlerPath) || !File.Exists(crashpadHandlerPath))
271-
{
272-
Debug.LogWarning("Backtrace native integration status: Cannot find crashpad library");
273-
return;
274-
}
275-
276-
var minidumpUrl = new BacktraceCredentials(_configuration.GetValidServerUrl()).GetMinidumpSubmissionUrl().ToString();
286+
CaptureNativeCrashes = CanInitializeExecutableCrashHandler(libDirectory, crashpadHandlerPath)
287+
? InitializeExecutableCrashHandler(minidumpUrl, databasePath, crashpadHandlerPath, attachments)
288+
: InitializeJavaCrashHandler(minidumpUrl, databasePath, backtraceAttributes["device.abi"], libDirectory, attachments);
277289

278-
// reassign to captureNativeCrashes
279-
// to avoid doing anything on crashpad binary, when crashpad isn't available
280-
CaptureNativeCrashes = Initialize(
281-
AndroidJNI.NewStringUTF(minidumpUrl),
282-
AndroidJNI.NewStringUTF(databasePath),
283-
AndroidJNI.NewStringUTF(crashpadHandlerPath),
284-
AndroidJNIHelper.ConvertToJNIArray(new string[0]),
285-
AndroidJNIHelper.ConvertToJNIArray(new string[0]),
286-
AndroidJNIHelper.ConvertToJNIArray(attachments.ToArray()),
287-
_enableClientSideUnwinding,
288-
(int)UnwindingMode);
289290
if (!CaptureNativeCrashes)
290291
{
291292
Debug.LogWarning("Backtrace native integration status: Cannot initialize Crashpad client");
@@ -309,6 +310,71 @@ private void HandleNativeCrashes(IDictionary<string, string> backtraceAttributes
309310
AndroidJNI.NewStringUTF(CrashType));
310311
}
311312

313+
private bool CanInitializeExecutableCrashHandler(String nativeLibraryDirectory, String handlerPath) {
314+
return Directory.Exists(nativeLibraryDirectory) && File.Exists(handlerPath);
315+
}
316+
317+
private bool InitializeExecutableCrashHandler(String minidumpUrl, String databasePath, String crashpadHandlerPath, IEnumerable<String> attachments) {
318+
return Initialize(
319+
AndroidJNI.NewStringUTF(minidumpUrl),
320+
AndroidJNI.NewStringUTF(databasePath),
321+
AndroidJNI.NewStringUTF(crashpadHandlerPath),
322+
AndroidJNIHelper.ConvertToJNIArray(new string[0]),
323+
AndroidJNIHelper.ConvertToJNIArray(new string[0]),
324+
AndroidJNIHelper.ConvertToJNIArray(attachments.ToArray()),
325+
_enableClientSideUnwinding,
326+
(int)UnwindingMode);
327+
}
328+
329+
private bool InitializeJavaCrashHandler(String minidumpUrl, String databasePath, String deviceAbi, String nativeDirectory, IEnumerable<String> attachments) {
330+
if (String.IsNullOrEmpty(deviceAbi)) {
331+
Debug.LogWarning("Cannot determine device ABI");
332+
return false;
333+
}
334+
335+
var envVariableDictionary = Environment.GetEnvironmentVariables();
336+
if (envVariableDictionary == null) {
337+
Debug.LogWarning("Environment variables are not defined.");
338+
return false;
339+
}
340+
341+
// verify if the library is already extracted
342+
var backtraceNativeLibraryPath = Path.Combine(nativeDirectory, _nativeLibraryName);
343+
if (!File.Exists(backtraceNativeLibraryPath)) {
344+
backtraceNativeLibraryPath = string.Format("{0}!/lib/{1}/{2}", Application.dataPath, deviceAbi, _nativeLibraryName);
345+
}
346+
347+
// prepare native crash handler environment variables
348+
List<String> environmentVariables = new List<string> () {
349+
string.Format("CLASSPATH={0}", Application.dataPath),
350+
string.Format("BACKTRACE_UNITY_CRASH_HANDLER={0}", backtraceNativeLibraryPath),
351+
string.Format("LD_LIBRARY_PATH={0}", string.Join(":", nativeDirectory, Directory.GetParent(nativeDirectory), GetLibrarySystemPath(), "/data/local")),
352+
"ANDROID_DATA=/data"
353+
};
354+
355+
foreach (DictionaryEntry kvp in envVariableDictionary) {
356+
environmentVariables.Add(string.Format("{0}={1}", kvp.Key, kvp.Value == null ? "NULL" : kvp.Value));
357+
}
358+
359+
360+
return InitializeJavaCrashHandler(
361+
AndroidJNI.NewStringUTF(minidumpUrl),
362+
AndroidJNI.NewStringUTF(databasePath),
363+
AndroidJNI.NewStringUTF(_crashHandlerPath),
364+
AndroidJNIHelper.ConvertToJNIArray(new string[0]),
365+
AndroidJNIHelper.ConvertToJNIArray(new string[0]),
366+
AndroidJNIHelper.ConvertToJNIArray(attachments.ToArray()),
367+
AndroidJNIHelper.ConvertToJNIArray(environmentVariables.ToArray())
368+
);
369+
}
370+
371+
private string GetLibrarySystemPath() {
372+
using (var systemClass = new AndroidJavaClass("java.lang.System"))
373+
{
374+
return systemClass.CallStatic<string>("getProperty", "java.library.path");
375+
}
376+
}
377+
312378
/// <summary>
313379
/// Retrieve Backtrace Attributes from the Android native code.
314380
/// </summary>

0 commit comments

Comments
 (0)