support JavaException annotation for Java crashes
Categories
(Socorro :: Webapp, enhancement, P2)
Tracking
(Not tracked)
People
(Reporter: willkg, Assigned: willkg)
References
(Blocks 1 open bug)
Details
Attachments
(1 file)
android-components is adding a JavaException
annotation which is a structured form of the Java stack trace and exception information.
It's in the same form as what Sentry sends which is nice.
This bug covers:
- processing the
JavaException
field like we do withJavaStackTrace
where we have a raw form and a sanitized form that doesn't have the exception message which can contain PII (bug #1496599) - making the sanitized form viewable in the report view to everyone
Assignee | ||
Comment 1•4 years ago
|
||
Bug #1541120 covers improving signature generation using this information. We'll need to implement the processor bits before working on that, so I'm adding a blocker.
Assignee | ||
Comment 2•3 years ago
|
||
The JavaException
annotation is a JSON-encoded string that decodes to a dict. Here's an example:
{
"exception": {
"values": [
{
"stacktrace": {
"frames": [
{"module":"java.lang.AbstractStringBuilder","function":"enlargeBuffer","in_app":true,"lineno":95,"filename":"AbstractStringBuilder.java"},
{"module":"java.lang.AbstractStringBuilder","function":"append0","in_app":true,"lineno":163,"filename":"AbstractStringBuilder.java"},
{"module":"java.lang.StringBuilder","function":"append","in_app":true,"lineno":311,"filename":"StringBuilder.java"},
{"module":"org.json.JSONTokener","function":"nextString","in_app":true,"lineno":212,"filename":"JSONTokener.java"},
{"module":"org.json.JSONTokener","function":"nextValue","in_app":true,"lineno":107,"filename":"JSONTokener.java"},
{"module":"org.json.JSONTokener","function":"readObject","in_app":true,"lineno":385,"filename":"JSONTokener.java"},
{"module":"org.json.JSONTokener","function":"nextValue","in_app":true,"lineno":100,"filename":"JSONTokener.java"},
{"module":"org.json.JSONTokener","function":"readArray","in_app":true,"lineno":430,"filename":"JSONTokener.java"},
{"module":"org.json.JSONTokener","function":"nextValue","in_app":true,"lineno":103,"filename":"JSONTokener.java"},
{"module":"org.json.JSONTokener","function":"readObject","in_app":true,"lineno":385,"filename":"JSONTokener.java"},
{"module":"org.json.JSONTokener","function":"nextValue","in_app":true,"lineno":100,"filename":"JSONTokener.java"},
{"module":"org.json.JSONTokener","function":"readObject","in_app":true,"lineno":385,"filename":"JSONTokener.java"},
{"module":"org.json.JSONTokener","function":"nextValue","in_app":true,"lineno":100,"filename":"JSONTokener.java"},
{"module":"org.json.JSONObject","function":"<init>","in_app":true,"lineno":156,"filename":"JSONObject.java"},
{"module":"org.json.JSONObject","function":"<init>","in_app":true,"lineno":173,"filename":"JSONObject.java"},
{"module":"org.mozilla.geckoview.GeckoSession$SessionState","function":"fromString","in_app":true,"lineno":1,"filename":"GeckoSession.java"},
{"module":"mozilla.components.browser.engine.gecko.GeckoEngine","function":"createSessionState","in_app":true,"lineno":4,"filename":"GeckoEngine.kt"},
{"module":"mozilla.components.browser.session.storage.SnapshotSerializer","function":"itemFromJSON","in_app":true,"lineno":20,"filename":"SnapshotSerializer.kt"},
{"module":"mozilla.components.browser.session.storage.SnapshotSerializer","function":"fromJSON","in_app":true,"lineno":7,"filename":"SnapshotSerializer.kt"},
{"module":"androidx.core.app.AppOpsManagerCompat","function":"readSnapshot","in_app":true,"lineno":3,"filename":"AppOpsManagerCompat.java"},
{"module":"mozilla.components.browser.session.storage.SessionStorage","function":"restore","in_app":true,"lineno":3,"filename":"SessionStorage.kt"},
{"module":"org.mozilla.fenix.components.Core$sessionManager$2$$special$$inlined$also$lambda$1$1","function":"invokeSuspend","in_app":true,"lineno":2,"filename":"Core.kt"},
{"module":"kotlin.coroutines.jvm.internal.BaseContinuationImpl","function":"resumeWith","in_app":true,"lineno":3,"filename":"ContinuationImpl.kt"},
{"module":"kotlinx.coroutines.DispatchedTask","function":"run","in_app":true,"lineno":15,"filename":"DispatchedTask.kt"},
{"module":"kotlinx.coroutines.scheduling.CoroutineScheduler","function":"runSafely","in_app":true,"lineno":1,"filename":"CoroutineScheduler.kt"},
{"module":"kotlinx.coroutines.scheduling.CoroutineScheduler$Worker","function":"run","in_app":true,"lineno":11,"filename":"CoroutineScheduler.kt"}
],
"type":"OutOfMemoryError",
"module":"java.lang",
"value":"Failed to allocate a 34516 byte allocation with 13138 free bytes and 12KB until OOM"
}
}
]
}
}
The "exception message" is the ["stacktrace"]["value"]
thing. In this case, it's "Failed to allocate a 34516 byte allocation with 13138 free bytes and 12KB until OOM". That can contain protected data.
So if someone has protected data access, they can see the whole thing and if they don't, they see everything except that part.
We do something similar with "json_dump", but it's hard to reason about whether someone accessing data is getting protected data they shouldn't be getting if it's deep in the structure. I think I want to do what we do with java_stack_trace
where we have a "raw" form that has the whole structure and a "redacted form" that has everything that's fine, except the part that isn't.
Then we have:
- raw_crash.JavaException -- protected data
- processed_crash.java_exception_raw -- protected data
- processed_crash.java_exception -- public data
I'm not thrilled about the naming scheme, but switching it takes 6 months and it's probably better to accrue a bunch of bad naming decisions and then fix them all at once.
The Sentry docs for the exception part of the payload are here:
https://develop.sentry.dev/sdk/event-payloads/exception/
Here's an example of a crash report with a single exception:
https://crash-stats.mozilla.org/report/index/0aeae7f7-04ef-4d43-bf31-da6c30210115
We should make sure we support cascading exceptions. I bet we have an example in our data. I'll look for one later.
Assignee | ||
Updated•3 years ago
|
Assignee | ||
Comment 3•3 years ago
|
||
Some interesting bits about the data:
- The frame may not have a "filename". For example, this frame has no filename:
{'function': 'emitAllImpl$FlowKt__ChannelsKt', 'in_app': True, 'lineno': 13, 'module': 'kotlinx.coroutines.flow.FlowKt'}
- The stacktrace may not have a "value". I've only seen that with NullPointerExceptions so far, but I bet there are other exceptions that don't have values.
Need to keep that in mind when displaying things.
Assignee | ||
Comment 4•3 years ago
|
||
Example of crash report with multiple stacktraces: https://crash-stats.mozilla.org/report/index/5ea11bc7-5607-49d7-8dd7-1fc930210115
Assignee | ||
Comment 5•3 years ago
|
||
Assignee | ||
Comment 6•3 years ago
|
||
Assignee | ||
Comment 7•3 years ago
|
||
This was deployed in bug #1690378. Marking as FIXED.
There are some follow-up changes we could make, but we'll do those in future bugs as we figure out the details.
Description
•