Slide 1

Slide 1 text

@romtsn Revolutionizing ANR Detection at Sentry

Slide 2

Slide 2 text

What is Sentry? getsentry/sentry @getsentry sentry.io • Application Monitoring in Production • Developer Tool by devs for devs • Open-Source • Free*

Slide 3

Slide 3 text

What is Sentry?

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

What is ANR?

Slide 6

Slide 6 text

ANR Types

Slide 7

Slide 7 text

ANR Types • Input dispatching timeout (Activity): app doesn’t respond to an input event for 5 seconds • Executing Service: • Foreground: not calling startForeground() for 10 seconds • Regular: not fi nishing onCreate() / onStartCommand() / onBind() within 20 seconds • Broadcast Receiver: not fi nishing onReceive() within 10 seconds

Slide 8

Slide 8 text

ANR Types • Input dispatching timeout (Activity): app doesn’t respond to an input event for 5 seconds • Executing Service: • Foreground: not calling startForeground() for 10 seconds • Regular: not fi nishing onCreate() / onStartCommand() / onBind() within 20 seconds • Broadcast Receiver: not fi nishing onReceive() within 10 seconds

Slide 9

Slide 9 text

User-perceived ANR Main Thread Blocked User Input 1s 2s 3s 4s 5s ANR!

Slide 10

Slide 10 text

User-perceived ANR Main Thread Blocked User Input 1s 2s 3s 4s 5s ANR! Application.onAnrDetected()

Slide 11

Slide 11 text

ANR Detection Mechanisms

Slide 12

Slide 12 text

ANR Detection Mechanisms • Watchdog thread: periodically send a task to the main thread and check if it gets executed within 5 seconds • Native Signal Handler: install native SIGQUIT handler and wire it over JNI • ApplicationExitInfo: use system API to retrieve ANRs on next app launch

Slide 13

Slide 13 text

Watchdog Pros Cons • Almost real-time reporting • Ability to enrich ANR events with current state of the device (battery level, available memory, screenshot, etc.) • Available on all Android versions • A lot of false positives • No Service or BroadcastReceiver ANRs detection • Little thread info (no deadlocks detection)

Slide 14

Slide 14 text

Native Signal Handler Pros Cons • Almost real-time reporting • Ability to enrich ANR events with current state of the device (battery level, available memory, screenshot, etc.) • Available on all Android versions • False positives • No background ANR detection • Little thread info (no deadlocks detection)

Slide 15

Slide 15 text

ApplicationExitInfo Pros Cons • Most accurate ANR detection coming from the OS • Full stacktrace and thread information (with deadlock detection) • Background/Foreground ANRs • Only available on Android 11 and above • Not possible to access device dynamic data retroactively (battery, current memory, etc.)

Slide 16

Slide 16 text

Sentry ANR Detection V1 (Watchdog) ANR! Watchdog triggered Collect dynamic data Collect static data Send ANR Event Terminate process (or not)

Slide 17

Slide 17 text

Sentry ANR Detection V2 (AppExitInfo) ANR! Retrieve dynamic data from disk Collect static data Send ANR Event (async) Terminate process Continuously persist dynamic data to disk

Slide 18

Slide 18 text

New ANR detection internals

Slide 19

Slide 19 text

New ANR detection internals val appExitInfoList = activityManager.getHistoricalExitReasons() for (exitInfo in appExitInfoList) { // look for ANRs if (exitInfo.reason == ApplicationExitInfo.REASON_ANR) { // do not report duplicate events if (exitInfo.timestamp > lastReportedAnr) { val event = SentryEvent().apply { level = FATAL timestamp = exitInfo.timestamp // enrich with contexts,breadcrumbs,etc. } Sentry.captureEvent(event) } } }

Slide 20

Slide 20 text

New ANR detection internals val appExitInfoList = activityManager.getHistoricalExitReasons() for (exitInfo in appExitInfoList) { // look for ANRs if (exitInfo.reason == ApplicationExitInfo.REASON_ANR) { // do not report duplicate events if (exitInfo.timestamp > lastReportedAnr) { val event = SentryEvent().apply { level = FATAL timestamp = exitInfo.timestamp // enrich with contexts,breadcrumbs,etc. } Sentry.captureEvent(event) } } }

Slide 21

Slide 21 text

New ANR detection internals val appExitInfoList = activityManager.getHistoricalExitReasons() for (exitInfo in appExitInfoList) { // look for ANRs if (exitInfo.reason == ApplicationExitInfo.REASON_ANR) { // do not report duplicate events if (exitInfo.timestamp > lastReportedAnr) { val event = SentryEvent().apply { level = FATAL timestamp = exitInfo.timestamp // enrich with contexts,breadcrumbs,etc. } Sentry.captureEvent(event) } } }

Slide 22

Slide 22 text

New ANR detection internals val appExitInfoList = activityManager.getHistoricalExitReasons() for (exitInfo in appExitInfoList) { // look for ANRs if (exitInfo.reason == ApplicationExitInfo.REASON_ANR) { // do not report duplicate events if (exitInfo.timestamp > lastReportedAnr) { val event = SentryEvent().apply { level = FATAL timestamp = exitInfo.timestamp // enrich with contexts,breadcrumbs,etc. } Sentry.captureEvent(event) } } }

Slide 23

Slide 23 text

Preserving Contexts and Breadcrumbs

Slide 24

Slide 24 text

Preserving Contexts and Breadcrumbs V1 Sentry addBreadcrumb addTag addContext ActivityIntegration TimberIntegration OkHttpIntegration Enrich ANR event

Slide 25

Slide 25 text

Preserving Contexts and Breadcrumbs V2 Sentry addBreadcrumb observer.addBreadcrumb addTag observer.addTag addContext observer.addContext ActivityIntegration TimberIntegration OkHttpIntegration Enrich ANR event Persist Breadcrumb Persist Tag Persist Context

Slide 26

Slide 26 text

Preserving Contexts and Breadcrumbs val appExitInfoList = activityManager.getHistoricalExitReasons() for (exitInfo in appExitInfoList) { // look for ANRs if (exitInfo.reason == ApplicationExitInfo.REASON_ANR) { // do not report duplicate events if (exitInfo.timestamp > lastReportedAnr) { val event = SentryEvent().apply { level = FATAL timestamp = exitInfo.timestamp breadcrumbs = diskCache.breadcrumbs contexts = diskCache.contexts // enrich with threads and stacktraces } Sentry.captureEvent(event) } } }

Slide 27

Slide 27 text

System Trace Parsing

Slide 28

Slide 28 text

System Trace Parsing

Slide 29

Slide 29 text

Preserving Contexts and Breadcrumbs val appExitInfoList = activityManager.getHistoricalExitReasons() for (exitInfo in appExitInfoList) { // look for ANRs if (exitInfo.reason == ApplicationExitInfo.REASON_ANR) { // do not report duplicate events if (exitInfo.timestamp > lastReportedAnr) { val event = SentryEvent().apply { level = FATAL timestamp = exitInfo.timestamp breadcrumbs = diskCache.breadcrumbs contexts = diskCache.contexts val systraceParser = SystraceParser() threads = systraceParser.parse(exitInfo.getTraceInputStream()) } Sentry.captureEvent(event) } } }

Slide 30

Slide 30 text

What’s Next • Use Watchdog together with AppExitInfo • Can be useful if the main thread is blocked for less than 5s • Noti fi es if the user clicked “Wait” on ANR dialog and the app process continued • Gives more context data: • Screenshots • Current memory • Current battery

Slide 31

Slide 31 text

What’s Next • Persist pro fi les across app launches • Know which methods exactly were called prior to ANR

Slide 32

Slide 32 text

References • Sentry Android SDK - https://tinyurl.com/styjv • ApplicationExitInfo - https://developer.android.com/reference/android/app/ ApplicationExitInfo • ANRWatchdog - https://github.com/SalomonBrys/ANR-WatchDog

Slide 33

Slide 33 text

Thank you! @romtsn