ANRs caused by Sentry.init() while it's doing heavy operations on the main thread.
Integration
sentry-android
Build System
Gradle
AGP Version
8.5.2
Proguard
Enabled
Version
7.11.0
Steps to Reproduce
What happened
SentryAndroid.init(application) is causing ANRs because of heavy operations on the main thread.
Both issues are caused by io.sentry.Sentry.init(Sentry.java:166)
stacktrace:
at java.lang.Runtime.nativeLoad(Native Method)
at java.lang.Runtime.nativeLoad(Runtime.java:1121)
at java.lang.Runtime.loadLibrary0(Runtime.java:1075)
at java.lang.Runtime.loadLibrary0(Runtime.java:998)
at java.lang.System.loadLibrary(System.java:1661)
at io.sentry.android.ndk.SentryNdk.<clinit>(SentryNdk.java:19)
at java.lang.Class.classForName(Native Method)
at java.lang.Class.forName(Class.java:454)
at java.lang.Class.forName(Class.java:379)
at io.sentry.android.core.LoadClass.loadClass(LoadClass.java:21)
at io.sentry.android.core.AndroidOptionsInitializer.installDefaultIntegrations(AndroidOptionsInitializer.java:257)
at io.sentry.android.core.SentryAndroid.lambda$init$1(SentryAndroid.java:117)
at io.sentry.Sentry.applyOptionsConfiguration(Sentry.java:196)
at io.sentry.Sentry.init(Sentry.java:166)
at io.sentry.android.core.SentryAndroid.init(SentryAndroid.java:90)
at io.sentry.android.core.SentryAndroid.init(SentryAndroid.java:73)
The second ANR is caused by io.sentry.android.core.AndroidOptionsInitializer.getCacheDir(AndroidOptionsInitializer.java:362)
stacktrace:
at android.app.ContextImpl.getCacheDir(ContextImpl.java:872)
at android.content.ContextWrapper.getCacheDir(ContextWrapper.java:332)
at io.sentry.android.core.AndroidOptionsInitializer.getCacheDir(AndroidOptionsInitializer.java:362)
at io.sentry.android.core.AndroidOptionsInitializer.loadDefaultAndMetadataOptions(AndroidOptionsInitializer.java:110)
at io.sentry.android.core.SentryAndroid.lambda$init$1(SentryAndroid.java:111)
at io.sentry.Sentry.applyOptionsConfiguration(Sentry.java:196)
at io.sentry.Sentry.init(Sentry.java:166)
at io.sentry.android.core.SentryAndroid.init(SentryAndroid.java:90)
at io.sentry.android.core.SentryAndroid.init(SentryAndroid.java:73)
How to reproduce
If you enable StrictMode for your SDK you'll get warnings triggered by these functions: io.sentry.android.core.AndroidOptionsInitializer.readDefaultOptionValues(AndroidOptionsInitializer.java:336), io.sentry.android.core.AndroidOptionsInitializer.loadDefaultAndMetadataOptions(AndroidOptionsInitializer.java:112)
android.os.strictmode.DiskReadViolation
at android.os.StrictMode$AndroidBlockGuardPolicy.onReadFromDisk(StrictMode.java:1679)
at libcore.io.BlockGuardOs.access(BlockGuardOs.java:74)
at libcore.io.ForwardingOs.access(ForwardingOs.java:128)
at android.app.ActivityThread$AndroidOs.access(ActivityThread.java:8469)
at java.io.UnixFileSystem.checkAccess(UnixFileSystem.java:313)
at java.io.File.exists(File.java:813)
at android.app.ContextImpl.ensurePrivateDirExists(ContextImpl.java:791)
at android.app.ContextImpl.ensurePrivateCacheDirExists(ContextImpl.java:787)
at android.app.ContextImpl.getCacheDir(ContextImpl.java:898)
at android.content.ContextWrapper.getCacheDir(ContextWrapper.java:328)
at io.sentry.android.core.AndroidOptionsInitializer.getCacheDir(AndroidOptionsInitializer.java:362)
at io.sentry.android.core.AndroidOptionsInitializer.loadDefaultAndMetadataOptions(AndroidOptionsInitializer.java:110)
at io.sentry.android.core.SentryAndroid.lambda$init$1(SentryAndroid.java:111)
at io.sentry.Sentry.applyOptionsConfiguration(Sentry.java:196)
at io.sentry.Sentry.init(Sentry.java:166)
at io.sentry.android.core.SentryAndroid.init(SentryAndroid.java:90)
at io.sentry.android.core.SentryAndroid.init(SentryAndroid.java:73)
android.os.strictmode.DiskReadViolation
at android.os.StrictMode$AndroidBlockGuardPolicy.onReadFromDisk(StrictMode.java:1679)
at libcore.io.BlockGuardOs.access(BlockGuardOs.java:74)
at libcore.io.ForwardingOs.access(ForwardingOs.java:128)
at android.app.ActivityThread$AndroidOs.access(ActivityThread.java:8469)
at java.io.UnixFileSystem.checkAccess(UnixFileSystem.java:313)
at java.io.File.exists(File.java:813)
at android.app.ContextImpl.ensurePrivateDirExists(ContextImpl.java:791)
at android.app.ContextImpl.ensurePrivateDirExists(ContextImpl.java:782)
at android.app.ContextImpl.getFilesDir(ContextImpl.java:827)
at android.content.ContextWrapper.getFilesDir(ContextWrapper.java:288)
at io.sentry.android.core.Installation.id(Installation.java:34)
at io.sentry.android.core.AndroidOptionsInitializer.readDefaultOptionValues(AndroidOptionsInitializer.java:336)
at io.sentry.android.core.AndroidOptionsInitializer.loadDefaultAndMetadataOptions(AndroidOptionsInitializer.java:112)
at io.sentry.android.core.SentryAndroid.lambda$init$1(SentryAndroid.java:111)
at io.sentry.Sentry.applyOptionsConfiguration(Sentry.java:196)
at io.sentry.Sentry.init(Sentry.java:166)
at io.sentry.android.core.SentryAndroid.init(SentryAndroid.java:90)
at io.sentry.android.core.SentryAndroid.init(SentryAndroid.java:73)
Severity
This issue increases ANR rate of the app getting it closer to the Google's threshold, therefore making it harder to justify using Sentry in our codebase. Can you please consider removing disk read/write or other heavy operations off the main thread and use StrictMode to test your SDK before releasing it?
Expected Result
No disk read/write or other heavy operations on main thread.
Actual Result
ANRs caused by heavy operations on main thread.
┆Issue is synchronized with this Jira Improvement by Unito
@Chog thanks for the detailed write-up! There are definitely a few areas where we can improve, but also a few areas where there's barely any way around causing a StrictMode violation. Let me elaborate a bit more in detail:
-
Sentry NDK init In order to initialize the Sentry NDK, we need to load the
.solibs usingSystem.loadLibrary(). We could move this off the main thread, but it's not straight forward - as an async init could lead to missing early startup crashes. We already have an issue for this over here. -
Cache directory On SDK init we're calling
android.content.Context.getCacheDir()to determine the disk location for storing events when the device is offline. This should be a very lightweight operation, as we're not reading or writing to any file at this point. On top of that I see a fewmkdirs()- those should definitely performed on a background - let me look into this. -
Startup Crash marker file Based on 2. we also have a feature to detect early crashes during app startup. This ensures that even if your app is loop crashing on startup, the SDK is able to send a crash report (before the app crashes again). This works by checking for the existence of a marker file on SDK init. Again no file content is read or written - so this should be a very lightweight operation.
Are you using any native (C/C++) code in your app? If not, you could safely disable the NDK (the docs have all the details about this) to at least avoid 1).
@markushi Thank you for looking into this. Unfortunately, our app is heavily loaded already and susceptible to extra work on the main thread. I understand your reasoning, sounds fair.
We don't use NDK directly, but third-party dependencies do. If I disable NDK, I won't receive any native crashes in Sentry, is that correct?
If I disable NDK, I won't receive any native crashes in Sentry, is that correct?
@Chog0 Yes, that's correct, so in your case you should keep using the NDK.
Having said that we've identified a few areas of improvement, e.g. for the NDK init part a PR is already up, I'll keep you posted about the progress here!
Quick update: We've released 7.15.0 which contains a good set of improvements around ANR and SDK init performance. On top of that we've released an alpha version 7.16.0-alpha.1 which moves the System.loadLibraries calls to the background, further speeding up the init.
Another quick update: 7.16.0 is out now, featuring some more improvements around ANRs and SDK init performance.
hi @Chog0
Did you have the possibility to check newer SDK versions?
We made several improvements related to ANRs. Latest version is currently 7.19.0
Hello! We updated it to 7.15 and later to 7.16. I can see that the ANR rate generally decreased after this by 0.03%. It's hard to say that Sentry updates were responsible for this or something else we shipped in the same version, but I don't see the same ANR in the new versions. Thank you for fixing those!
Hi everyone, we're closing this issue as we've done many improvements to Sentry.init and it should look less heavier now. We'll continue improving as we go.