haroldadmin / WhatTheStack

See a pretty error screen when your Android app crashes
Apache License 2.0
245 stars 22 forks source link

Large exceptions (such as stack overflow error) can crash the library, eating the original exception #31

Closed matejdro closed 2 years ago

matejdro commented 2 years ago

When app crashes with a really large exception (such as stack overflow error with a really long stack trace), WhatTheStack will crash:

kotlinx.coroutines.CoroutinesInternalError: Fatal exception in coroutines machinery for CancellableContinuation(DispatchedContinuation[AndroidUiDispatcher@491392b, Continuation at androidx.compose.runtime.PausableMonotonicFrameClock.withFrameNanos(PausableMonotonicFrameClock.kt:63)@382a188]){Completed}@3066721. Please read KDoc to 'handleFatalException' method and report this incident to maintainers
    at kotlinx.coroutines.DispatchedTask.handleFatalException(DispatchedTask.kt:144)
    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:115)
    at androidx.compose.ui.platform.AndroidUiDispatcher.performTrampolineDispatch(AndroidUiDispatcher.android.kt:81)
    at androidx.compose.ui.platform.AndroidUiDispatcher.access$performTrampolineDispatch(AndroidUiDispatcher.android.kt:41)
    at androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.doFrame(AndroidUiDispatcher.android.kt:68)
    at [android.view.Choreographer$CallbackRecord.run(Choreographer.java:1035)](http://android.view.Choreographer$CallbackRecord.run(Choreographer.java:1035))
    at [android.view.Choreographer.doCallbacks(Choreographer.java:845)](http://android.view.Choreographer.doCallbacks(Choreographer.java:845))
    at [android.view.Choreographer.doFrame(Choreographer.java:775)](http://android.view.Choreographer.doFrame(Choreographer.java:775))
    at [android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1022)](http://android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1022))
    at [android.os.Handler.handleCallback(Handler.java:938)](http://android.os.Handler.handleCallback(Handler.java:938))
    at [android.os.Handler.dispatchMessage(Handler.java:99)](http://android.os.Handler.dispatchMessage(Handler.java:99))
    at [android.os.Looper.loopOnce(Looper.java:201)](http://android.os.Looper.loopOnce(Looper.java:201))
    at [android.os.Looper.loop(Looper.java:288)](http://android.os.Looper.loop(Looper.java:288))
    at [android.app.ActivityThread.main(ActivityThread.java:7840)](http://android.app.ActivityThread.main(ActivityThread.java:7840))
    at java.lang.reflect.Method.invoke(Native Method)
    at [com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:550)](http://com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:550))
    at [com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)](http://com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003))
Caused by: android.os.TransactionTooLargeException: data parcel size 7859848 bytes
    at android.os.BinderProxy.transactNative(Native Method)
    at [android.os.BinderProxy.transact(BinderProxy.java:571)](http://android.os.BinderProxy.transact(BinderProxy.java:571))
    at [android.os.IMessenger$Stub$Proxy.send(IMessenger.java:125)](http://android.os.IMessenger$Stub$Proxy.send(IMessenger.java:125))
    at [android.os.Messenger.send(Messenger.java:59)](http://android.os.Messenger.send(Messenger.java:59))
    at com.haroldadmin.whatthestack.WhatTheStackExceptionHandler.uncaughtException(WhatTheStackExceptionHandler.kt:22)
    at [java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:1073)](http://java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:1073))
    at [java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:1068)](http://java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:1068))
    at kotlinx.coroutines.CoroutineExceptionHandlerImplKt.handleCoroutineExceptionImpl(CoroutineExceptionHandlerImpl.kt:45)
    at kotlinx.coroutines.CoroutineExceptionHandlerKt.handleCoroutineException(CoroutineExceptionHandler.kt:39)
    at kotlinx.coroutines.StandaloneCoroutine.handleJobException(Builders.common.kt:192)
    at kotlinx.coroutines.JobSupport.finalizeFinishingState(JobSupport.kt:229)
    at kotlinx.coroutines.JobSupport.tryMakeCompletingSlowPath(JobSupport.kt:906)
    at kotlinx.coroutines.JobSupport.tryMakeCompleting(JobSupport.kt:863)
    at kotlinx.coroutines.JobSupport.makeCompletingOnce$kotlinx_coroutines_core(JobSupport.kt:828)
    at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:100)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
    at kotlinx.coroutines.UndispatchedCoroutine.afterResume(CoroutineContext.kt:147)
    at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:102)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
    at kotlinx.coroutines.internal.ScopeCoroutine.afterResume(Scopes.kt:33)
    at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:102)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:104)

Even worse, this exception will override original exception, making it unaccessible unless I disable WhatTheStack.

Maybe in this rare case, WhatTheStack could try catch this error, print original exception to the logcat and just display the message (without the stacktrace)?

haroldadmin commented 2 years ago

Thanks for reporting the issue. I'll try to investigate it over the weekend and get back to you.

haroldadmin commented 2 years ago

I tried to reproduce this issue locally by simulating a stack-overflow exception. I can confirm that such an error overrides the original exception, but I couldn't reproduce the library crashing. Can you give me a bit more info on when/how the library itself crashes due to large stack traces?

As for handling extremely large stack traces, I think the issue is the limit on the size of IPC messages in Android. If it's too large, the IPC communication throws an error.

Would it be an acceptable solution to you if we log every exception caught by WhatTheStack?

matejdro commented 2 years ago

Sorry, I was unclear. WhatTheStack still works normally and displays the exception, just the crash that happens inside WhatTheStack replaces the original exception.

Would it be an acceptable solution to you if we log every exception caught by WhatTheStack?

Ideally, misleading WhatTheStack window with the parcel error would be replaced by something else, but since the issues is so rare, I think this compromise is okay.

haroldadmin commented 2 years ago

Fix available in 1.0.0-alpha04