Introduction to Systrace
Systrace is one of the most fundamental performance analysis tools in the Android ecosystem. It collects system and application process execution times, CPU scheduling, disk I/O, and custom application events to provide a holistic view of what the device is doing over a specific period.
While newer tools like Perfetto have largely superseded the original Python based systrace.py script for capturing traces, understanding the data Systrace collects and how to interpret it is mandatory for any AOSP performance engineer. The underlying mechanism, atrace, remains heavily used.
Systrace Overview and Script Usage
Historically, Systrace was invoked via a Python script bundled with the Android SDK.
python systrace.py -o mynewtrace.html sched freq idle am wm gfx view \
binder_driver hal dalvik camera input res
This command instructs the device to collect data for specific "categories" (like am for ActivityManager, gfx for Graphics) and outputs a standalone HTML file. This HTML file contains an embedded trace viewer (built on the Catapult project) that allows you to zoom and pan through the timeline.
If you are using modern AOSP, you will often capture these same atrace categories using the perfetto command line tool instead.
Trace Categories
Systrace captures data by enabling specific tracing categories. These categories correspond to different subsystems in the Android OS:
sched: CPU Scheduling. Shows which thread is running on which CPU core at any given time. Crucial for finding thread contention and understanding if your app is actually getting CPU time.gfx: Graphics. Traces SurfaceFlinger, VSYNC events, and hardware composer. Essential for jank and frame drop analysis.view: View System. Traces UI thread operations likemeasure,layout, anddrawfor Android views.am: Activity Manager. Traces the lifecycle of Activities, Services, and Broadcasts. Useful for startup optimization.wm: Window Manager. Traces window animations, focus changes, and window transitions.input: Input Events. Traces the path of touch and key events from the kernel input driver up to the application.res: Resource Loading. Traces the loading of APK resources, which often causes UI thread blockage during startup.binder_driver: Traces IPC calls. Vital for understanding blocking calls between processes.
Reading Systrace Output
When you open a trace file (either the classic HTML or via the modern ui.perfetto.dev interface), you are presented with a timeline.
CPU Timelines
At the top of the trace, you will see a track for each CPU core (e.g., CPU 0, CPU 1, etc.). The blocks inside these tracks represent threads currently executing on that core.
- A busy CPU track means the system is fully utilizing that core.
- If your thread is not on a CPU track, it is not executing.
Thread States
When you select a thread in its process track, pay attention to its state:
- Running (Green): The thread is executing on a CPU.
- Runnable (Blue): The thread is ready to run but is waiting in the scheduler queue for a CPU core to become available. Long runnable times indicate CPU starvation or thermal throttling.
- Sleeping (White/Transparent): The thread is intentionally sleeping or waiting on a lock/monitor.
- Uninterruptible Sleep (Orange/Red): The thread is blocked waiting for disk I/O. This is common during app startup when reading APK files or databases.
Atrace: Userspace Tracing API
The magic behind the system specific categories (am, wm, etc.) is atrace (Android Trace). atrace is a userspace wrapper around the Linux kernel's ftrace subsystem.
AOSP developers can instrument their own system services or applications using the atrace APIs to emit custom events into the trace timeline.
C/C++ Tracing (<cutils/trace.h>)
In native code, you use ATRACE_CALL() to trace the duration of a function.
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <cutils/trace.h>
void drawFrame() {
ATRACE_CALL(); // Automatically traces until the end of the scope
ATRACE_BEGIN("setup_gl");
setupGLContext();
ATRACE_END();
renderInternal();
}
Java/Kotlin Tracing (android.os.Trace)
In the Android framework or apps, you use the Trace class.
import android.os.Trace;
public void processData(List<Data> items) {
Trace.beginSection("ProcessDataList");
try {
for (Data item : items) {
processSingleItem(item);
}
} finally {
// MUST call endSection to close the event
Trace.endSection();
}
}
By adding these markers to your code, you can see exactly how long processData takes in relation to CPU scheduling and VSYNC events when you capture a trace. This is the most effective way to pinpoint the exact line of code causing a performance bottleneck.