sentry-java icon indicating copy to clipboard operation
sentry-java copied to clipboard

ANRs caused by Sentry.init() while it's doing heavy operations on the main thread.

Open Chog0 opened this issue 1 year ago • 4 comments

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

Chog0 avatar Aug 23 '24 14:08 Chog0

@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:

  1. Sentry NDK init In order to initialize the Sentry NDK, we need to load the .so libs using System.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.

  2. 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 few mkdirs() - those should definitely performed on a background - let me look into this.

  3. 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 avatar Aug 28 '24 07:08 markushi

@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?

Chog0 avatar Aug 30 '24 20:08 Chog0

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!

markushi avatar Sep 03 '24 12:09 markushi

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.

markushi avatar Oct 18 '24 11:10 markushi

Another quick update: 7.16.0 is out now, featuring some more improvements around ANRs and SDK init performance.

markushi avatar Oct 24 '24 05:10 markushi

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

stefanosiano avatar Dec 12 '24 10:12 stefanosiano

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!

Chog0 avatar Dec 16 '24 10:12 Chog0

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.

romtsn avatar Feb 12 '25 14:02 romtsn