[Bug] Unity App freezes for several seconds to minutes when an exception occurs with Crashlytics running
See original GitHub issue[REQUIRED] Please fill in the following fields:
- Unity editor version: 2021.3.1f1
- Firebase Unity SDK version: 10.2.0
- Source you installed the SDK: .unitypackage
- Problematic Firebase Component: Crashlytics
- Other Firebase Components in use: Analytics
- Additional SDKs you are using: None
- Platform you are using the Unity editor on: Windows
- Platform you are targeting: iOS, Android
- Scripting Runtime: IL2CPP
- Pre-built SDK from the website
[REQUIRED] Please describe the issue here:
Crashlytics increases the time a Unity app freezes when handling an uncaught exception by several orders of magnitude. We are struggling with this in a production app where users often quit the app because they assume it’s stuck forever.
We managed to reproduce it in isolation (crashlytics-issue-repro project), and have used this isolated case to set up 3 test cases:
- Synchronous, 50 Method Callstack: A
void
method that calls itself 50 times recursively and then throws an Exception - Asynchronous, 50 Method Callstack: An
async Task
method that calls itself 50 times recursively usingawait
and then throws an Exception - Synchronous, 50 Background Threads: A
void
method that starts 50 worker threads that idle for 1 second and immediately throws an Exception (to find out whether uploading all thread info to Crashlytics could also cause overhead)
We’ve ran them with Crashlytics not initialized yet (“Crashlytics OFF”), then initialized Crashlytics and ran them again (“Crashlytics ON”). Test results running on a Huawei Y6 2018 / ATU-L21 with Android 8.0.0:
Crashlytics OFF | Crashlytics ON | |
---|---|---|
Synchronous, 50 Method Callstack | 00:00.015 | 00:05.131 |
Asynchronous, 50 Method Callstack | 00:03.607 | 28:29.103 |
Synchronous, 50 Background Threads | 00:00.109 | 00:05.205 |
Exceptions that didn’t cause any perceivable stutter without Crashlytics suddenly cause a >5s freeze when Crashlytics is enabled. The most troublesome was the asynchronous callstack, though, which already incurs a 3.6s freeze without Crashlytics, but suddenly jumps to nearly half an hour of freeze with Crashlytics enabled.
Steps to reproduce:
- Open the crashlytics-issue-repro project in Unity 2021.3.1f1
- Import your own
google-services.json
- Ensure scripting backend is set to IL2CPP
- Build and run on any Android phone. The issue is much more visible on slightly older phones. I’ve used a Huawei Y6 2018 / ATU-L21.
- After the app starts, press the “Freeze for 3 Seconds” button as a calibration test. This simply invokes
Thread.Sleep(3000)
, so the resulting text should show a freeze of around 3 seconds. - Press the three “Throw” buttons in sequence. “Throw (Simple Stack)” and “Throw (Many Threads)” should be very fast, while “Throw (Async Stack)” could lead to a short freeze.
- Press the “Initialize Firebase” button. Crashlytics is now running.
- Press the three “Throw” buttons in sequence. “Throw (Simple Stack)” and “Throw (Many Threads)” should now take several seconds (depending on device), while “Throw (Async Stack)” should lead to a very long freeze (almost 30 minutes on the Huawei device).
Downloadable Repro Project: https://github.com/Noshire/crashlytics-issue-repro
Code Snippets
All code can be found in the linked repository. This snippet, for example, demonstrates how we fabricate the async callstack that throws an exception deep down, which leads to a 28 minute freeze with Crashlytics enabled:
/// <summary>
/// Fabricates a deep async callchain and then throws
/// </summary>
public async Task ThrowAsync()
{
await EnlargeAsyncStacktraceAndFinallyThrow(50);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static async Task EnlargeAsyncStacktraceAndFinallyThrow(int count)
{
if (count > 0)
// ReSharper disable once TailRecursiveCall
await EnlargeAsyncStacktraceAndFinallyThrow(count - 1);
else
throw new Exception("This is a test exception.");
}
Issue Analytics
- State:
- Created 9 months ago
- Reactions:1
- Comments:10 (5 by maintainers)
Top GitHub Comments
Thanks for the additional input, @Noshire. I was able to replicate this issue using my physical devices. Let me relay this to the team so that we could identify any underlying causes. I’ll be marking this as a bug for now.
Here are my results after testing:
Can you update the Firebase Crashlytics Unity SDK to v10.4.0 and then set
Crashlytics.ReportUncaughtExceptionsAsFatal = true;
when you initialize Crashlytics. This will make all your uncaught C# exceptions come in as fatals, but it will also set the Crashlytics behaviour to better match the nature of Unity. Specifically it is aware that uncaught exceptions don’t crash a Unity game, and will process them on a worker thread. You can see more details at https://firebase.google.com/docs/crashlytics/customize-crash-reports?platform=unity