The systrace
command allows you to collect and inspect timing information
across all processes running on your device at the system level. It combines
data from the Android kernel, such as the CPU scheduler, disk activity, and
app threads, to generate an HTML report, similar to what's shown in
figure 1.
Figure 1. A sample systrace
HTML report, which
shows 5 seconds of interaction with an app. The report highlights frames
that systrace
believes may not have been rendered properly.
This report provides an overall picture of an Android device’s system processes
for a given period of time. It also inspects the captured tracing information to
highlight problems that it observes, such as UI jank while displaying motion or
animation, and provides recommendations about how to fix them. However,
systrace
doesn't collect information about code execution within your app
process. For more detailed information about which methods your app is executing
and how much CPU resources it's using, use the Android Studio
CPU profiler. You can also
generate trace logs and
import and inspect them using the CPU Profiler.
This document explains how to generate
systrace
reports from the command line, navigate the trace files produced by
the tool, and use them to analyze and improve the performance of an
application's user interface (UI).
In order to run systrace
, complete the following steps:
- From Android Studio, download and install the latest Android SDK Tools.
- Install Python and include it in your workstation's execution path.
- Connect a device running Android 4.3 (API Level 18) or higher to your development system using a USB debugging connection.
The systrace
tool is provided in the Android SDK Tools package and is
located in android-sdk/platform-tools/systrace/
.
Syntax
To generate the HTML report for app, you need to run systrace
from the
command line using the following syntax:
python systrace.py [options] [categories]
For example, the following command calls systrace
to record device activity
and generate a HTML report named mynewtrace.html
. This list of categories is a
reasonable default list for most devices.
$ python systrace.py -o mynewtrace.html sched freq idle am wm gfx view \
binder_driver hal dalvik camera input res
Tip: If you want to see the names of tasks in
the trace output, you must include the sched
category in
your command parameters.
To view the list of categories that your connected device supports, run the following command:
$ python systrace.py --list-categories
If you don't specify any categories or options, systrace
generates a report
that includes all available categories and uses default settings. The categories
available depend on the connected device you're using.
Global options
Global options | Description |
---|---|
-h | --help |
Show the help message. |
-l | --list-categories |
Lists the tracing categories available to your connected device. |
Commands and command options
Commands and options | Description |
---|---|
-o file |
Write the HTML trace report to the specified file. If
you don't specify this option, systrace saves your report to the same
directory as systrace.py and names it trace.html .
|
-t N | --time=N |
Trace device activity for N seconds. If you don't specify
this option, systrace prompts you to end the trace by pressing the
Enter key from the command line.
|
-b N | --buf-size=N |
Use a trace buffer size of N kilobytes. This option lets you limit the total size of the data collected during a trace. |
-k functions |
Trace the activity of specific kernel functions, specified in a comma-separated list. |
-a app-name |
Enable tracing for apps, specified as a comma-separated list of
process names.
The apps must contain tracing instrumentation calls from the
Trace class. You should specify this option whenever you
profile your app—many libraries, such as
RecyclerView , include tracing
instrumentation calls that provide useful information when you enable
app-level tracing. For more information, go to the section about
how to instrument your app code.
|
--from-file=file-path |
Create an interactive HTML report from a file, such as TXT files that include raw trace data, instead of running a live trace. |
-e device-serial |
Conduct the trace on a specific connected device, identified by its device serial number. |
categories |
Include tracing information for the system processes you specify, such
as gfx for system processes that render graphics. You
can run systrace with the -l command to see a
list of services available to your connected device.
|
Investigate UI performance problems
systrace
is particularly useful for inspecting your app's UI performance
because it can analyze your code and frame rate to identify problem areas and
suggest possible solutions. To begin, proceed as follows:
- Run your app on a connected device.
Run
systrace
with the following command:python systrace.py -t 10 [other-options] [categories]
This example traces your app for 10 seconds.
Interact with your app while
systrace
continues running.After your defined time limit has elapsed, in this case 10 seconds,
systrace
generates an HTML report.Open the HTML report using a web browser.
By interacting with this report, you can inspect device CPU usage over the duration of the recording. For help navigating the HTML report, see the keyboard shortcuts section, or click the ? button in the top right corner of the report.
The section below explains how to inspect information in the report to find and fix UI performance problems.
Inspecting frames and alerts
As shown in figure 2, the report lists each process that renders UI frames and indicates each rendered frame along the timeline. Frames that render within the 16.6 millisecond required to maintain a stable 60 frames per second are indicated with green frame circles. Frames that take longer than 16.6 milliseconds to render are indicated with yellow or red frame circles.
Figure 2. Systrace display after zooming in on a long-running frame.
Clicking on a frame circle highlights it and provides additional information about the work done by the system to render that frame, including alerts. It also shows you the methods that the system was executing while rendering that frame, so you can investigate those methods for causes of UI jank.
Figure 3. Selecting the problematic frame, an alert appears below the trace report identifying the problem.
After you select a slow frame, you may see an alert in the bottom pane of the
report. The alert shown in figure 3 calls out that the primary problem with the
frame is that too much time is spent inside
ListView
recycling and rebinding.
There are links to the relevant events in the trace which explain more about
what the system is doing during this time.
To see each alert that the tool discovered in your trace, and the number of times the device triggered each alert, click the Alerts tab at the far right of the window, as shown in figure 4. The Alerts panel helps you see which problems occur in the trace, and how often they contribute to jank. Think of the panel as a list of bugs to be fixed. Often, a tiny change or improvement in one area can eliminate an entire class of alerts from your app.
Figure 4. Clicking the Alert button to the right reveals the alert tab.
If you see too much work being done on the UI thread, you need to find out which
methods are consuming too much CPU time. One approach is to add trace markers
(see Instrument your app code) to methods that you think are
causing these bottlenecks to see those function calls appear in systrace
. If you
are unsure which methods may be causing bottlenecks on the UI thread, use
the Android Studio CPU profiler. You can
generate trace logs and import and
inspect them using the CPU Profiler.
HTML report keyboard shortcuts
The table below lists the keyboard shortcuts that are available while viewing a
systrace
HTML report.
Key | Description |
---|---|
W | Zoom into the trace timeline. |
S | Zoom out of the trace timeline. |
A | Pan left on the trace timeline. |
D | Pan right on the trace timeline. |
E | Center the trace timeline on the current mouse location. |
G | Show grid at the start of the currently selected task. |
Shift + G | Show grid at the end of the currently selected task. |
Right Arrow | Select the next event on the currently selected timeline. |
Left Arrow | Select the previous event on the currently selected timeline. |
Instrument your app code
Because systrace
shows you information about processes only at the system
level, it's difficult to know what methods your app was executing at a given
time in the HTML report. In Android 4.3 (API level 18) and higher, you can use
the Trace
class in your code to flag execution events in the
HTML report. You don't need to instrument your code to record traces with
systrace
, but doing so helps you see what parts of your app's code may be
contributing to hanging threads or UI jank. This approach is different from
using the Debug
class—the Trace
class
simply adds flags to a systrace
report, while the Debug
class helps you inspect detailed app CPU usage by
generating .trace
files.
To generate a systrace
HTML report that includes your instrumented trace
events, you need to run systrace
with the -a
or --app
command line option
and specify the package name of your app.
The following code example shows you how to use the Trace
class to flag the
execution of a method, including two nested code blocks within that
method:
Kotlin
class MyAdapter : RecyclerView.Adapter<MyViewHolder>() {
...
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
Trace.beginSection("MyAdapter.onCreateViewHolder")
return try {
MyViewHolder.newInstance(parent)
} finally {
// In 'try...catch' statements, always call endSection()
// in a 'finally' block to ensure it is invoked even when an exception
// is thrown.
Trace.endSection()
}
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
Trace.beginSection("MyAdapter.onBindViewHolder")
try {
try {
Trace.beginSection("MyAdapter.queryDatabase")
val rowItem = queryDatabase(position)
dataset.add(rowItem)
} finally {
Trace.endSection()
}
holder.bind(dataset[position])
} finally {
Trace.endSection()
}
}
...
}
Java
public class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {
...
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Trace.beginSection("MyAdapter.onCreateViewHolder");
MyViewHolder myViewHolder;
try {
myViewHolder = MyViewHolder.newInstance(parent);
} finally {
// In 'try...catch' statements, always call endSection()
// in a 'finally' block to ensure it is invoked even when an exception
// is thrown.
Trace.endSection();
}
return myViewHolder;
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
Trace.beginSection("MyAdapter.onBindViewHolder");
try {
try {
Trace.beginSection("MyAdapter.queryDatabase");
RowItem rowItem = queryDatabase(position);
dataset.add(rowItem);
} finally {
Trace.endSection();
}
holder.bind(dataset.get(position));
} finally {
Trace.endSection();
}
}
...
}