NEW REPORT! Overcoming key challenges in mobile observability: A guide for modern DevOps and SRE teams

Download now
Android

How to read a stack trace and analyze it for Android apps

Our Android engineers break down 5 sample stack traces to help you better understand and troubleshoot your mobile apps.

Chances are, you’ve encountered exceptions or crashes in your Android application that can be difficult to diagnose.

One of the most useful tools for debugging these issues is a stack trace.

A stack trace is a report of the function calls that were active at the time of an exception or crash. It can help you identify the root cause of the issue and pinpoint the location in your code where it occurred.

To help you make better use of your stack traces, we’ve reached out to our team of in-house Android developers for advice.

In this blog post, we’ll cover some techniques for reading and interpreting a stack trace in Android to help you better understand and troubleshoot your mobile apps. Additionally, we’ll provide a few code samples to show you exactly how to deploy these techniques.

Tips and techniques for reading a stack trace in Android

Reading and interpreting a stack trace in Android can seem daunting, but when you know where and what to look for, there’s no need to fret. The following tips and techniques will help you better understand the anatomy of a stack trace and each line of code’s relationship to potential issues.

Understanding the anatomy of the stack trace

  • The first line of the stack trace indicates the type of exception.
  • The remaining lines show the function calls that were made leading up to the exception. In that regard, all calls are listed in chronological order, with the most recent call listed at the top (or the second line of the stack trace) and the original call which triggered the exception at the bottom.

Locating the exception in the stack trace

  • To identify the specific location in your code where the exception occurred, look for line numbers and filenames. This will help you pinpoint the particular part of your codebase where the crash happened.

Identifying patterns and issues within the stack trace

  • Try to identify any error patterns or common lines in the stack traces. They may indicate a recurrent or repeated issue in the code.
  • If a third-party library appears in the stack trace, it is essential to review the library’s official documentation and/or contact the library’s support team.

4 examples of how to read a stack trace in Android

To bring these tips and techniques to life, our team of Android engineers pulled together a few sample stack traces for clarity.

Sample 1: NullPointerException

Let’s start with a pervasive case, a NullPointerException:

java.lang.NullPointerException: Attempt to invoke virtual method 'void com.mylib.android.MyLogger.log(java.lang.String, java.lang.String)' on a null object reference
                at com.mylib.android.MyLibBreadcrumbService.logCustom(MyLibBreadcrumbService.java:227)
                at com.mylib.android.MyLibImpl.logBreadcrumb(MyLibImpl.java:1601)
                at com.mylib.android.MyLib.logBreadcrumb(MyLib.java:565)
                at com.myapp.ui.fragments.base.BaseFragment.onAttach(BaseFragment.java:23)
                at androidx.fragment.app.Fragment.performAttach(Fragment.java:2922)
                at androidx.fragment.app.FragmentStateManager.attach(FragmentStateManager.java:464)
                at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:275)
                at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2189)
                at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:2100)
                at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:2002)
                at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:3138)
                at androidx.fragment.app.FragmentManager.dispatchActivityCreated(FragmentManager.java:3072)
                at androidx.fragment.app.FragmentController.dispatchActivityCreated(FragmentController.java:251)
                at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:501)
                at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:246)
                at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1455)
                at android.app.Activity.performStart(Activity.java:8076)
                at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3660)
                at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:221)
                at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:201)
                at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:173)
                at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
                at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2210)
                at android.os.Handler.dispatchMessage(Handler.java:106)
                at android.os.Looper.loopOnce(Looper.java:201)
                at android.os.Looper.loop(Looper.java:288)
                at android.app.ActivityThread.main(ActivityThread.java:7839)
                at java.lang.reflect.Method.invoke(Native Method)
                at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
                at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)

NullPointerException stack trace analysis

Returning to our tips above, let’s start with the first line. The first line indicates the type of exception, java.lang.NullPointerException, happened when the method 'void com.mylib.android.MyLogger.log(java.lang.String, java.lang.String)' was trying to be invoked.

We can then read the second line to determine specifically where the crash occurred (com.mylib.android.MyLibBreadcrumbService.logCustom(MyLibBreadcrumbService.java:227).

With that in mind, it’s also essential to consider packages that could be at the root cause. Here, we can see two packages: com.mylib.android and com.myApp. Given this, it is likely that the crash is due to an external library included as a dependency in our project, specifically com.mylib.android.

From here, we can open the file and navigate to that line in order to verify what may be causing the crash. If nothing in that line helps us understand the crash, we can move on to examining the remaining lines of code in the stack trace.

Sample 2: java.net.SocketTimeoutException

Another common exception you’ll come across is java.net.SocketTimeoutException:

java.net.SocketTimeoutException (crashed) timeout
		at okhttp3.internal.http2.Http2Stream$StreamTimeout.newTimeoutException (Http2Stream.kt:675)
		at okhttp3.internal.http2.Http2Stream$StreamTimeout.exitAndThrowIfTimedOut (Unknown Source:8)
		at okhttp3.internal.http2.Http2Stream.takeHeaders (Http2Stream.kt:143)
		at okhttp3.internal.http2.Http2ExchangeCodec.readResponseHeaders (Http2ExchangeCodec.kt:96)
		at okhttp3.internal.connection.Exchange.readResponseHeaders (Exchange.kt:106)
		at okhttp3.internal.http.CallServerInterceptor.intercept (CallServerInterceptor.kt:79)
		at okhttp3.internal.http.RealInterceptorChain.proceed (RealInterceptorChain.kt:109)
		at com.myLib.android.mySdk.okhttp3.MyOkHttp3NetworkInterceptor.intercept (MyOkHttp3NetworkInterceptor.java:88)
		at okhttp3.internal.http.RealInterceptorChain.proceed (RealInterceptorChain.kt:109)
		at okhttp3.internal.connection.ConnectInterceptor.intercept (ConnectInterceptor.kt:34)
		at okhttp3.internal.http.RealInterceptorChain.proceed (RealInterceptorChain.kt:109)
		at okhttp3.internal.cache.CacheInterceptor.intercept (CacheInterceptor.kt:95)
		at okhttp3.internal.http.RealInterceptorChain.proceed (RealInterceptorChain.kt:109)
		at okhttp3.internal.http.BridgeInterceptor.intercept (BridgeInterceptor.kt:83)
		at okhttp3.internal.http.RealInterceptorChain.proceed (RealInterceptorChain.kt:109)
		at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept (RetryAndFollowUpInterceptor.kt:76)
		at okhttp3.internal.http.RealInterceptorChain.proceed (RealInterceptorChain.kt:109)
		at okhttp3.logging.HttpLoggingInterceptor.intercept (HttpLoggingInterceptor.kt:221)
		at okhttp3.internal.http.RealInterceptorChain.proceed (RealInterceptorChain.kt:109)
		at com.myLib.android.mySdk.okhttp3.MyOkHttp3AppInterceptor.intercept (MyOkHttp3AppInterceptor.java:46)
		at okhttp3.internal.http.RealInterceptorChain.proceed (RealInterceptorChain.kt:109)
		at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp (RealCall.kt:201)
		at okhttp3.internal.connection.RealCall$AsyncCall.run (RealCall.kt:517)
		at java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1137)
		at java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:637)
		at java.lang.Thread.run (Thread.java:1012)

java.net.SocketTimeoutException stack trace analysis

Issues like the java.net.SocketTimeoutException indicate a network problem.

Analyzing the stack trace, the first candidate for a potential root cause of our problem comes at line 88 where we find MyOkhttp3NetworkInterceptor.

From here, we can examine two potential hypotheses:

  1. The interceptor is somehow failing and therefore generating the crash;
  2. Or, there’s likely a network problem on the device, and the app is not handling it correctly, leading to the crash. In that case, the interceptor is likely not causing the crash, but rather just passing the network calls through.

Sample 3: OutOfMemoryError

Now, let’s look at an OutOfMemoryError:

java.lang.OutOfMemoryError: Failed to allocate a 775576 byte allocation with 235140 free bytes and 229KB until OOM RAW 
		at java.lang.String. (String.java:400) 
		at java.lang.AbstractStringBuilder.toString (AbstractStringBuilder.java:633) 
		at java.lang.StringBuffer.toString (StringBuffer.java:723) 
		at java.io.StringWriter.toString (StringWriter.java:100) 
		at com.google.gson.Gson.toJson (Gson.java:639) 
		at com.myapp.android.instrumentation.GsonInstrumentation.toJson (GsonInstrumentation.java:37) 
		at com.lib.android.libsdk.MyCacheService.cacheObject (MyCacheService.java:56) 
		at com.lib.android.libsdk.MyCacheService.sessionService.cacheSessionMessage (SessionService.java:525) 
		at com.lib.android.libsdk.MyCacheService.service.runEndSession (SessionService.java:498) 
		at com.lib.android.libsdk.MyCacheService.sessionService.endSession (SessionService.java:480) 
		at com.lib.android.libsdk.MyCacheService.sessionService.onBackground (SessionService.java:431) 
		at com.lib.android.libsdk.MyCacheService.taskService.lambda$onBackground$4 (TaksService.java:204) 
		at com.lib.android.libsdk.MyCacheService..-$$Lambda$activityService$tFGWps1my5O8EOtTloy3FsZC5Xc.accept (lambda) 
		at java9.util.Spliterators$ArraySpliterator.forEachRemaining (Spliterators.java:1267)

OutOfMemoryError Stack Trace Analysis

The example above demonstrates an OutOfMemoryError and includes lines belonging to an external dependency called com.lib.android.libsdk. At first glance, it appears that the cache string is too large, causing an out-of-memory error when MyCacheService attempts to write it to the buffer in the service.

The first assumption could be that the library is causing the out-of-memory error. However, let’s analyze the cause of the error:

java.lang.OutOfMemoryError: Failed to allocate a 775576-byte allocation with 235140 free bytes and 229KB until OOM RAW.

The device was already low on memory with less than 300K of free heap space, which may indicate a need for performance improvement rather than this being a pervasive problem.

Sample 4: RuntimeException

RuntimeException is the superclass of exceptions that can be thrown during the normal operation of the Java Virtual Machine.

Caused by: java.lang.RuntimeException: Duplicate class 
com.myLib.android.sdk.LifecycleLogger
 found in modules jetified-myLib-android-sdk-1.1.0-runtime (com.myLib: myLib-android-sdk:1.1.0) and
 jetified-myLib-android-sdk-1.2.0-runtime (com.myLib: myLib-android-sdk-1.2.0.aar)

RuntimeException stack trace analysis

In this case, the error is really straightforward. Two instances of com.myLib.android.sdk were created while the app was running. When this happens, the app doesn’t know which instance it should run, and so it crashes instead. These types of crashes, while easy to identify and fix, are killers for user experience and can directly lead to user churn — making them important to find and fix proactively, when possible.

Sample 5: Android NDK crash

This sample shows an Android NDK crash.

Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0 in tid 31514 (ndkcrashcapture), pid 31514 (ndkcrashcapture)
Cmdline: com.myapp.ndkscreen
pid: 31514, tid: 31514, name: ndkcrashcapture  >>> com.myapp.ndkscreen <<<
 #00 pc 0000000000010354  /data/app/~~ACEvgQ3ytztSdGjEjeUeTg==/com.myapp.ndkscreen-brDLRKuYODpXSmvA4Gp6WA==/lib/arm64/libnative-lib-crash-test.so (AnotherClass::sigsegv()+16) (BuildId: fcb1f2bdd88daec6d71180516c0465296a9f5bd0)
 #01 pc 0000000000010528  /data/app/~~ACEvgQ3ytztSdGjEjeUeTg==/com.myapp.ndkscreen-brDLRKuYODpXSmvA4Gp6WA==/lib/arm64/libnative-lib-crash-test.so (TestClass::sigsegv()+36) (BuildId: fcb1f2bdd88daec6d71180516c0465296a9f5bd0)
 #02 pc 0000000000010a60  /data/app/~~ACEvgQ3ytztSdGjEjeUeTg==/com.myapp.ndkscreen-brDLRKuYODpXSmvA4Gp6WA==/lib/arm64/libnative-lib-crash-test.so (Java_com_myapp_ndkscreen_MyActivityKt_runMyCode+28) (BuildId: fcb1f2bdd88daec6d71180516c0465296a9f5bd0)
 #10 pc 000000000000132c  [anon:dalvik-classes3.dex extracted in memory from /data/app/~~ACEvgQ3ytztSdGjEjeUeTg==/com.myapp.ndkscreen-brDLRKuYODpXSmvA4Gp6WA==/base.apk!classes3.dex]
 #13 pc 00000000000011c8  [anon:dalvik-classes3.dex extracted in memory from /data/app/~~ACEvgQ3ytztSdGjEjeUeTg==/com.myapp.ndkscreen-brDLRKuYODpXSmvA4Gp6WA==/base.apk!classes3.dex]
 #16 pc 0000000000000dac  [anon:dalvik-classes3.dex extracted in memory from /data/app/~~ACEvgQ3ytztSdGjEjeUeTg==/com.myapp.ndkscreen-brDLRKuYODpXSmvA4Gp6WA==/base.apk!classes3.dex]
---------------------------- PROCESS ENDED(31514) for package com.myapp.ndkscreen ----------------------------

This error was caused by a segmentation fault (signal 11 (SIGSEGV)), and the code reports that the fault address is 0x0 and the fault type is SEGV_MAPERR.

The stack trace provides information about the call stack at the moment of the crash, which can help identify the issue’s root cause. In this example, there are several frames of note.

Let’s focus on the first three lines:

  1. #00 pc 0000000000010354 /data/app/~~ACEvgQ3ytztSdGjEjeUeTg==/com.myapp.ndkscreen-brDLRKuYODpXSmvA4Gp6WA==/lib/arm64/libnative-lib-crash-test.so (AnotherClass::sigsegv()+16) (BuildId: fcb1f2bdd88daec6d71180516c0465296a9f5bd0)The code here is showing us that the crash occurred in the class AnotherClass at address 0000000000010354.
  2. #01 pc 0000000000010528 /data/app/~~ACEvgQ3ytztSdGjEjeUeTg==/com.myapp.ndkscreen-brDLRKuYODpXSmvA4Gp6WA==/lib/arm64/libnative-lib-crash-test.so (TestClass::sigsegv()+36) (BuildId: fcb1f2bdd88daec6d71180516c0465296a9f5bd0)Here, we can see the subsequent class of the crash, which in this case is TestClass, located at address 0000000000010528.
  3. #02 pc 0000000000010a60 /data/app/~~ACEvgQ3ytztSdGjEjeUeTg==/com.myapp.ndkscreen-brDLRKuYODpXSmvA4Gp6WA==/lib/arm64/libnative-lib-crash-test.so (Java_com_myapp_ndkscreen_MyActivityKt_runMyCode+28) (BuildId: fcb1f2bdd88daec6d71180516c0465296a9f5bd0)Finally, we have the function call that triggered the crash. By analyzing this line Java_com_myapp_ndkscreen_MyActivityKt_runMyCode, we can conclude that the runMyCode function inside MyActivityKt caused or triggered the crash at address 0000000000010a60.

Go beyond basic stack traces with Embrace intelligent Crash Reporting

Knowing how to read and analyze a stack trace is an important skill for any Android developer to learn and perfect. But it’s just a small piece of the puzzle when it comes to improving performance, decreasing time to resolution, and spending more time on building great mobile experiences.

While the tips and techniques outlined above will help you more effectively address known issues, they do little to help you proactively understand when something is wrong with your app.

Embrace intelligent Crash Reporting helps mobile development teams quickly identify high-priority crashes and their root causes through accurate crash grouping, intelligent scoring, and user context, allowing them to focus on delivering exceptional experiences without having to solve the same crash more than once.

With Embrace, you always know when action needs to be taken, and where the root cause of the issue lies, so you can spend less time fixing and more time building.

Learn more about Embrace’s intelligent Crash Reporting tools and get started for free today.

Embrace Deliver incredible mobile experiences with Embrace.

Get started today with 1 million free user sessions.

Get started free

Build better mobile apps with Embrace

Find out how Embrace helps engineers identify, prioritize, and resolve app issues with ease.