Commit 1aa0ea7
authored
[Mono.Android] Marshal .NET stack trace to Throwable.getStackTrace() (#8185)
Context: #1198
Context: #1188 (comment)
Context: #4877
Context: #4927 (comment)
What happens with unhandled exceptions?
throw new InvalidOperationException ("oops!");
This is a surprisingly complicated question:
If this happens when a debugger is attached, the debugger will get a
"first chance notification" at the `throw` site. If execution
continues, odds are high that the app will abort if there is a JNI
transition in the callstack.
If no debugger is attached, then it depends on which thread threw the
unhandled exception.
If the thread which threw the unhandled exception is a .NET Thread:
static void ThrowFromAnotherManagedThread() {
var t = new System.Threading.Thread(() => {
throw new new Java.Lang.Error ("from another thread?!");
});
t.Start ();
t.Join ();
}
Then .NET will report the unhandled exception, *and*
the app will restart:
F mono-rt : [ERROR] FATAL UNHANDLED EXCEPTION: System.InvalidOperationException: oops!
F mono-rt : at android_unhandled_exception.MainActivity.<>c.<ThrowFromAnotherManagedThread>b__1_0()
F mono-rt : at System.Threading.Thread.StartCallback()
# app restarts
If the thread which threw the unhandled exception is a *Java* thread,
which could be the UI thread (e.g. thrown from an `Activity.OnCreate()`
override) or via a `Java.Lang.Thread` instance:
static void ThrowFromAnotherJavaThread() {
var t = new Java.Lang.Thread(() => {
throw new InvalidOperationException ("oops!");
});
t.Start ();
t.Join ();
}
Then .NET will report the unhandled exception, *and* the app will
*not* restart (which differs from using .NET threads):
E AndroidRuntime: Process: com.companyname.android_unhandled_exception, PID: 5436
E AndroidRuntime: android.runtime.JavaProxyThrowable: System.InvalidOperationException: oops!
E AndroidRuntime: at android_unhandled_exception.MainActivity.<>c.<ThrowFromAnotherJavaThread>b__2_0()
E AndroidRuntime: at Java.Lang.Thread.RunnableImplementor.Run()
E AndroidRuntime: at Java.Lang.IRunnableInvoker.n_Run(IntPtr , IntPtr )
E AndroidRuntime: at Android.Runtime.JNINativeWrapper.Wrap_JniMarshal_PP_V(_JniMarshal_PP_V , IntPtr , IntPtr )
E AndroidRuntime: at mono.java.lang.RunnableImplementor.n_run(Native Method)
E AndroidRuntime: at mono.java.lang.RunnableImplementor.run(RunnableImplementor.java:31)
E AndroidRuntime: at java.lang.Thread.run(Thread.java:1012)
I MonoDroid: Android.Runtime.JavaProxyThrowable: Exception_WasThrown, Android.Runtime.JavaProxyThrowable
I MonoDroid:
I MonoDroid: --- End of managed Android.Runtime.JavaProxyThrowable stack trace ---
I MonoDroid: android.runtime.JavaProxyThrowable: System.InvalidOperationException: oops!
I MonoDroid: at android_unhandled_exception.MainActivity.<>c.<ThrowFromAnotherJavaThread>b__2_0()
I MonoDroid: at Java.Lang.Thread.RunnableImplementor.Run()
I MonoDroid: at Java.Lang.IRunnableInvoker.n_Run(IntPtr , IntPtr )
I MonoDroid: at Android.Runtime.JNINativeWrapper.Wrap_JniMarshal_PP_V(_JniMarshal_PP_V , IntPtr , IntPtr )
I MonoDroid: at mono.java.lang.RunnableImplementor.n_run(Native Method)
I MonoDroid: at mono.java.lang.RunnableImplementor.run(RunnableImplementor.java:31)
I MonoDroid: at java.lang.Thread.run(Thread.java:1012)
I MonoDroid:
I MonoDroid: --- End of managed Android.Runtime.JavaProxyThrowable stack trace ---
I MonoDroid: android.runtime.JavaProxyThrowable: System.InvalidOperationException: oops!
I MonoDroid: at android_unhandled_exception.MainActivity.<>c.<ThrowFromAnotherJavaThread>b__2_0()
I MonoDroid: at Java.Lang.Thread.RunnableImplementor.Run()
I MonoDroid: at Java.Lang.IRunnableInvoker.n_Run(IntPtr , IntPtr )
I MonoDroid: at Android.Runtime.JNINativeWrapper.Wrap_JniMarshal_PP_V(_JniMarshal_PP_V , IntPtr , IntPtr )
I MonoDroid: at mono.java.lang.RunnableImplementor.n_run(Native Method)
I MonoDroid: at mono.java.lang.RunnableImplementor.run(RunnableImplementor.java:31)
I MonoDroid: at java.lang.Thread.run(Thread.java
This "works", until we enter the world of crash logging for later
diagnosis and fixing. The problem with our historical approach is
that we would "stuff" the .NET stack trace into the "message" of the
Java-side `Throwable` instance, and the "message" may not be
transmitted as part of the crash logging!
(This is noticeable by the different indentation levels for the
`at …` lines in the crash output. Three space indents are from the
`Throwable.getMessage()` output, while four space indents are from
the Java-side stack trace.)
We *think* that we can improve this by replacing the Java-side stack
trace with a "merged" stack trace which includes both the Java-side
and .NET-side stack traces. This does nothing for unhandled exceptions
on .NET threads, but does alter the output from Java threads:
E AndroidRuntime: FATAL EXCEPTION: Thread-3
E AndroidRuntime: Process: com.companyname.android_unhandled_exception, PID: 12321
E AndroidRuntime: android.runtime.JavaProxyThrowable: [System.InvalidOperationException]: oops!
E AndroidRuntime: at android_unhandled_exception.MainActivity+<>c.<ThrowFromAnotherJavaThread>b__2_0(Unknown Source:0)
E AndroidRuntime: at Java.Lang.Thread+RunnableImplementor.Run(Unknown Source:0)
E AndroidRuntime: at Java.Lang.IRunnableInvoker.n_Run(Unknown Source:0)
E AndroidRuntime: at Android.Runtime.JNINativeWrapper.Wrap_JniMarshal_PP_V(Unknown Source:0)
E AndroidRuntime: at mono.java.lang.RunnableImplementor.n_run(Native Method)
E AndroidRuntime: at mono.java.lang.RunnableImplementor.run(RunnableImplementor.java:31)
E AndroidRuntime: at java.lang.Thread.run(Thread.java:1012)
I MonoDroid: UNHANDLED EXCEPTION:
I MonoDroid: Android.Runtime.JavaProxyThrowable: Exception_WasThrown, Android.Runtime.JavaProxyThrowable
I MonoDroid:
I MonoDroid: --- End of managed Android.Runtime.JavaProxyThrowable stack trace ---
I MonoDroid: android.runtime.JavaProxyThrowable: [System.InvalidOperationException]: oops!
I MonoDroid: at android_unhandled_exception.MainActivity+<>c.<ThrowFromAnotherJavaThread>b__2_0(Unknown Source:0)
I MonoDroid: at Java.Lang.Thread+RunnableImplementor.Run(Unknown Source:0)
I MonoDroid: at Java.Lang.IRunnableInvoker.n_Run(Unknown Source:0)
I MonoDroid: at Android.Runtime.JNINativeWrapper.Wrap_JniMarshal_PP_V(Unknown Source:0)
I MonoDroid: at mono.java.lang.RunnableImplementor.n_run(Native Method)
I MonoDroid: at mono.java.lang.RunnableImplementor.run(RunnableImplementor.java:31)
I MonoDroid: at java.lang.Thread.run(Thread.java:1012)
I MonoDroid:
I MonoDroid: --- End of managed Android.Runtime.JavaProxyThrowable stack trace ---
I MonoDroid: android.runtime.JavaProxyThrowable: [System.InvalidOperationException]: oops!
I MonoDroid: at android_unhandled_exception.MainActivity+<>c.<ThrowFromAnotherJavaThread>b__2_0(Unknown Source:0)
I MonoDroid: at Java.Lang.Thread+RunnableImplementor.Run(Unknown Source:0)
I MonoDroid: at Java.Lang.IRunnableInvoker.n_Run(Unknown Source:0)
I MonoDroid: at Android.Runtime.JNINativeWrapper.Wrap_JniMarshal_PP_V(Unknown Source:0)
I MonoDroid: at mono.java.lang.RunnableImplementor.n_run(Native Method)
I MonoDroid: at mono.java.lang.RunnableImplementor.run(RunnableImplementor.java:31)
I MonoDroid: at java.lang.Thread.run(Thread.java:1012)
Note how `at …` is always a four-space indent and always lines up.
*Hopefully* this means that crash loggers can provide more useful
information.
TODO:
* Create an "end-to-end" test which uses an actual crash logger
(which one?) in order to better understand what the
"end user" experience is.
* The "merged" stack trace always places the managed stack trace
above the Java-side stack trace. This means things will look
"weird"/"wrong" if you have an *intermixed* stack trace, e.g.
(Java code calls .NET code which calls Java code)+
which eventually throws from .NET.1 parent 4061928 commit 1aa0ea7
File tree
4 files changed
+109
-12
lines changed- src/Mono.Android
- Android.Runtime
- Java.Lang
- tests/Mono.Android-Tests/System
4 files changed
+109
-12
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
79 | 79 | | |
80 | 80 | | |
81 | 81 | | |
82 | | - | |
| 82 | + | |
83 | 83 | | |
84 | 84 | | |
85 | 85 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
2 | 6 | | |
3 | 7 | | |
4 | 8 | | |
5 | | - | |
| 9 | + | |
6 | 10 | | |
7 | 11 | | |
8 | 12 | | |
9 | | - | |
10 | | - | |
| 13 | + | |
| 14 | + | |
11 | 15 | | |
12 | 16 | | |
13 | 17 | | |
14 | 18 | | |
15 | | - | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
16 | 40 | | |
17 | | - | |
18 | | - | |
| 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 | + | |
19 | 79 | | |
20 | | - | |
| 80 | + | |
21 | 81 | | |
22 | 82 | | |
23 | 83 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
253 | 253 | | |
254 | 254 | | |
255 | 255 | | |
256 | | - | |
| 256 | + | |
257 | 257 | | |
258 | 258 | | |
259 | 259 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
| 2 | + | |
2 | 3 | | |
| 4 | + | |
3 | 5 | | |
4 | 6 | | |
5 | 7 | | |
| |||
17 | 19 | | |
18 | 20 | | |
19 | 21 | | |
20 | | - | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
21 | 30 | | |
22 | 31 | | |
23 | 32 | | |
24 | 33 | | |
25 | 34 | | |
26 | | - | |
27 | | - | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
28 | 44 | | |
| 45 | + | |
29 | 46 | | |
30 | 47 | | |
31 | 48 | | |
32 | 49 | | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
33 | 70 | | |
34 | 71 | | |
0 commit comments