forked from backtrace-labs/backtrace-unity
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathBacktraceANRWatchdog.java
More file actions
126 lines (105 loc) · 3.96 KB
/
BacktraceANRWatchdog.java
File metadata and controls
126 lines (105 loc) · 3.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package backtrace.io.backtrace_unity_android_plugin;
import android.os.Debug;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import com.unity3d.player.UnityPlayer;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Calendar;
/**
* This is the class that is responsible for monitoring the
* user interface thread and sending an error if it is blocked
*/
public class BacktraceANRWatchdog extends Thread {
private final static transient String LOG_TAG = BacktraceANRWatchdog.class.getSimpleName();
/**
* Handler for UI Thread - used to check if the thread is not blocked
*/
private final Handler mainThreadHandler = new Handler(Looper.getMainLooper());
/**
* Maximum time in milliseconds after which should check if the main thread is not hanged
*/
private int timeout;
/**
* Check if thread should stop
*/
private volatile boolean shouldStop = false;
/**
* Game object name - required by JNI to learn how to call Backtrace Unity plugin
* when library will detect ANR
*/
private String gameObjectName;
/**
* Unity library callback method name
*/
private String methodName;
/**
* Initialize new instance of BacktraceANRWatchdog with default timeout
*/
public BacktraceANRWatchdog(String gameObjectName, String methodName, int anrTimeout) {
Log.d(LOG_TAG, "Initializing ANR watchdog");
this.methodName = methodName;
this.gameObjectName = gameObjectName;
this.timeout = anrTimeout;
this.start();
}
/**
* Method which is using to check if the user interface thread has been blocked
*/
@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()) {
final backtrace.io.backtrace_unity_android_plugin.BacktraceThreadWatcher threadWatcher = new backtrace.io.backtrace_unity_android_plugin.BacktraceThreadWatcher(0, 0);
mainThreadHandler.post(new Runnable() {
@Override
public void run() {
threadWatcher.tickCounter();
}
});
try {
Thread.sleep(this.timeout);
} catch (InterruptedException e) {
Log.d(LOG_TAG, "Thread is interrupted", e);
return;
}
threadWatcher.tickPrivateCounter();
if (threadWatcher.getCounter() == threadWatcher.getPrivateCounter()) {
reported = false;
continue;
}
if (reported) {
// skipping, because we already reported an ANR report for current ANR
continue;
}
reported = true;
Log.d(LOG_TAG, "Detected blocked Java thread. Reporting Java ANR.");
NotifyUnityAboutANR();
}
}
public void NotifyUnityAboutANR() {
String stackTrace = stackTraceToString(Looper.getMainLooper().getThread().getStackTrace());
Log.d(LOG_TAG, stackTrace);
UnityPlayer.UnitySendMessage(this.gameObjectName, this.methodName, stackTrace);
}
public static String stackTraceToString(StackTraceElement[] stackTrace) {
StringWriter sw = new StringWriter();
printStackTrace(stackTrace, new PrintWriter(sw));
return sw.toString();
}
public static void printStackTrace(StackTraceElement[] stackTrace, PrintWriter pw) {
for(StackTraceElement stackTraceEl : stackTrace) {
pw.println(stackTraceEl);
}
}
public void stopMonitoring() {
Log.d(LOG_TAG, "ANR handler has been disabled.");
shouldStop = true;
}
}