System tracing (ATrace) your Android apps for better performance

Using systrace and OpenTrace on release-style builds

Pluma Browser
3 min readDec 10, 2020
Photo by Nicolas Jehly on Unsplash

System tracing involves recording device information about an app or system component execution. The main purposes of system tracing are

  1. understanding what tasks are running during a specific workflow (e.g., app startup)
  2. measure how long those tasks take to execute

The workflow for system tracing is:

  1. identify a scenario to trace (e.g., app startup, or performing a time-sensitive scenario for the app)
  2. add any trace calls if the scenario is not well traced
  3. reset the device to the “before-the-scenario” state (e.g., for startup, the process is killed)
  4. start recording the system trace
  5. perform the identified scenario in (1)
  6. stop recording the system trace
  7. analyze the system trace
  8. fix any bottlenecks, go back to (2)

Both the Android SDK and NDKs have APIs to feed into a system trace:

  • From the Android SDK (Java/Kotlin/JVM): android.os.Trace is the API. We will only useTrace#beginSection and Trace#endSection.
  • From the Android NDK (C/C++): ATrace_* from trace.h. ATrace_beginSection and ATrace_endSection are the corresponding functions.

Tracing setup for non-debug builds

If you start adding calls to Trace.{begin,end}section in your codebase and then record a trace, you’ll be disappointed because the trace won’t contain the sections you added.

Trace System Calls (aka systrace, perfetto): Low impact on runtime, great for understanding how the app interacts with the system and CPUs, but not the Java method calls that happen inside the app VM. ― Py’s post: Android Vitals — Profiling App Startup

There are multiple ways to record system traces for your own custom sections in the app’s VM for non-debug builds.

Implementation for solution 1
  1. (API 29 / Android 10 and above) Add the profileableFromShell element to your AndroidManifest. Typically you’ll want to capture system traces from older devices, so this isn’t a great solution.
  2. (API 27 / Android 8.1 and above) Use the “wrap hack”, which involves marking the “release” (or better a new one called “perf”) build type as debuggable in the manifest, and then using a script (usually called wrap.sh) to run the app without the debug flags in ART.
  3. (Recommended) (API 18 / Android 4.3.x and above) Use reflection to enable tracing. The function Trace#setAppTracingAllowed controls whether the sections in your application can be traced. This function is indeed called with true iff the application is marked as debuggable or profileable. This can be done with reflection. Don’t be scared, you can configure that code to only run on a special “perf” build type and won’t make it to production.

OpenTrace

OpenTrace is a library I created that packages solution (3) above and comes with a sample app to show how to configure a “perf” build type to enable tracing.

Add OpenTrace

implementation 'com.qflair.opentrace:2.0.0'

Identify the scenario to trace

In this case, we’ll trace the scenario that is applicable to all apps: startup.

Start recording the system trace

There’s a couple of ways to record the trace for app startup. One is from Android Studio (API 26 / Android 8.0 and above)

The other one is to record the start the trace from the command line (while the process is gone) and start the app while recording:

$ python $ANDROID_HOME/platform-tools/systrace/systrace.py -a com.example.opentrace.perf

If we only want to include our app’s traces, we’d add the app option at the end of the above command. When we stop recording the trace, we are provided with an HTML file that contains all the default categories for a system trace and our own custom sections!

There is a lot of information on the Android documentation on how to read and interpret these reports.

We hope you found this useful. Happy hacking!

--

--