Commit d0646a62 authored by Roger Yang's avatar Roger Yang
Browse files

Close #6903: Caught exception with no stack trace will be reported as UnexpectedlyMissingStacktrace

parent cb6e7a40
......@@ -139,11 +139,19 @@ class CrashReporter(
* Submit a caught exception report to all registered services.
*/
override fun submitCaughtException(throwable: Throwable): Job {
logger.info("Caught Exception report submitted to ${services.size} services")
/*
* if stacktrace is empty, replace throwable with UnexpectedlyMissingStacktrace exception so
* we can figure out which module is submitting caught exception reports without a stacktrace.
*/
var reportThrowable = throwable
if (throwable.stackTrace.isEmpty()) {
reportThrowable = CrashReporterException.UnexpectedlyMissingStacktrace("Missing Stacktrace", throwable)
}
logger.info("Caught Exception report submitted to ${services.size} services")
return scope.launch {
services.forEach {
it.report(throwable, crashBreadcrumbs.toSortedArrayList())
it.report(reportThrowable, crashBreadcrumbs.toSortedArrayList())
}
}
}
......@@ -292,3 +300,16 @@ class CrashReporter(
"You need to call install() on your CrashReporter instance from Application.onCreate().")
}
}
/**
* A base class for exceptions describing crash reporter exception.
*/
internal abstract class CrashReporterException(message: String, cause: Throwable?) : Exception(message, cause) {
/**
* Stacktrace was expected to be present, but it wasn't.
*/
internal class UnexpectedlyMissingStacktrace(
message: String,
cause: Throwable?
) : CrashReporterException(message, cause)
}
......@@ -429,6 +429,54 @@ class CrashReporterTest {
assert(exceptionBreadcrumb?.get(0) == breadcrumb)
}
@Test
fun `Caught exception with no stack trace should be reported as CrashReporterException`() {
val testMessage = "test_Message"
val testData = hashMapOf("1" to "one", "2" to "two")
val testCategory = "testing_category"
val testLevel = Breadcrumb.Level.CRITICAL
val testType = Breadcrumb.Type.USER
var exceptionCrash = false
var exceptionThrowable: Throwable? = null
var exceptionBreadcrumb: ArrayList<Breadcrumb>? = null
val service = object : CrashReporterService {
override val id: String = "test"
override val name: String = "TestReporter"
override fun createCrashReportUrl(identifier: String): String? = null
override fun report(crash: Crash.UncaughtExceptionCrash): String? = null
override fun report(crash: Crash.NativeCodeCrash): String? = null
override fun report(throwable: Throwable, breadcrumbs: ArrayList<Breadcrumb>): String? {
exceptionCrash = true
exceptionThrowable = throwable
exceptionBreadcrumb = breadcrumbs
return null
}
}
val reporter = spy(CrashReporter(
context = testContext,
services = listOf(service),
shouldPrompt = CrashReporter.Prompt.NEVER
).install(testContext))
val throwable = RuntimeException()
throwable.stackTrace = emptyArray()
val breadcrumb = Breadcrumb(testMessage, testData, testCategory, testLevel, testType)
reporter.recordCrashBreadcrumb(breadcrumb)
reporter.submitCaughtException(throwable).joinBlocking()
assertTrue(exceptionCrash)
assert(exceptionThrowable is CrashReporterException.UnexpectedlyMissingStacktrace)
assert(exceptionThrowable?.cause is java.lang.RuntimeException)
assertEquals(exceptionBreadcrumb?.get(0), breadcrumb)
}
@Test
fun `CrashReporter forwards native crashes to telemetry service`() {
var nativeCrash = false
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment