The CPU profiler helps you inspect your app’s CPU usage and thread activity in real-time, and record method traces, so you can optimize and debug your app’s code.
To open the CPU Profiler, follow these steps:
- Click View > Tool Windows > Android Profiler (you can also click Android Profiler in the toolbar).
- Select the device and app process you want to profile from the Android Profiler toolbar. If you've connected a device over USB but don't see it listed, ensure that you have enabled USB debugging.
- Click anywhere in the CPU timeline to open the CPU Profiler.
Why you should profile CPU usage
Minimizing your app’s CPU usage has many advantages—such as providing a faster and smoother user experience, and preserving device battery life. It also helps your app perform well on a variety of newer and older devices. You can use the CPU profiler to monitor CPU usage and thread activity while interacting with your app. However, for more detailed information about how your app executes its code, you should record and inspect method traces.
For each thread in your app process, you can find out which methods are executed over a period of time and the CPU resources each method consumes during its execution. You can also use method traces to identify callers and callees. A caller is a method that invokes another method, and a callee is one that is invoked by another method. You can use this information to determine which methods are responsible for invoking particular resource-heavy tasks too often and try to optimize your app’s code to avoid unnecessary work.
If you want to collect detailed system-level data that helps you inspect native system processes and address UI jank caused by dropped frames, you should use Systrace.
CPU Profiler overview
When you open the CPU profiler, it immediately starts displaying your app’s CPU usage and thread activity. You should see something similar to figure 1.
As indicated in figure 1, the default view for the CPU Profiler includes the following:
- Event timeline: Shows the activities in your app as they transition through different states in their lifecycle, and indicates user interactions with the device, including screen rotation events. To learn more about the event timeline, including how to enable it, read Enable advanced profiling.
- CPU timeline: Shows real-time CPU usage of your app—as a percentage of total available CPU time—and the total number of threads your app is using. The timeline also shows the CPU usage of other processes (such as system processes or other apps), so you can compare it to your app’s usage. You can inspect historical CPU usage data by moving your mouse along the horizontal axis of the timeline.
- Thread activity timeline: Lists each thread that belongs to your app
process and indicates their activity along a timeline using the colors listed below.
After you record a method trace, you can select a thread from this timeline to
inspect its data in the trace pane.
- Green: The thread is active or is ready to use the CPU. That is, it's in a 'running' or 'runnable' state.
- Yellow: The thread is active, but it’s waiting on an I/O operation, such as disk or network I/O, before it can complete its work.
- Gray: The thread is sleeping and is not consuming any CPU time. This sometimes occurs when the thread requires access to a resource that is not yet available. Either the thread goes into voluntary sleep, or the kernal puts the thread to sleep until the required resource becomes available.
- Tracing type: Allows you to select one of the following options to
determine how the profiler records a method trace.
- Sampled: Captures your app’s call stack at frequent intervals during your app’s execution. The profiler compares sets of captured data to derive timing and resource usage information about your app’s code execution. An inherent issue of sampled-based tracing is that if your app enters a method after a capture of the call stack and exits the method before the next capture, that method call is not logged by the profiler. If you are interested in tracing methods with such short lifecycles, you should use instrumented tracing.
- Instrumented: Instruments your app at runtime to record a timestamp at the beginning and end of each method call. Timestamps are collected and compared to generate method tracing data, including timing information and CPU usage. Note that the overhead associated with instrumenting each method impacts runtime performance and may influence profiling data—this is even more noticeable for methods with relatively short lifecycles. Additionally, if your app executes a large number of methods in short amount of time, the profiler may quickly exceed its file size limit and not be able to record any further tracing data.
- Record button: Starts and stops recording a method trace. To learn more, go to the section about recording and inspecting method traces.
Note: The profiler also reports CPU usage of threads that Android
Studio and the Android platform add to your app process—such as
Studio:Heartbeat (although, the
exact names displayed in the thread activity timeline may vary). This means
your app’s CPU usage in the CPU timeline also reports CPU time used by these
threads. You can see some of these threads in the thread activity
timeline and also monitor their activity. (However, because the profiler
threads execute native code, you can’t record method tracing data for them.)
Android Studio reports this data so you can easily identify when thread
activity and CPU usage is actually caused by your app’s code.
Record and inspect method traces
To start recording a method trace, select either the Sampled or Instrumented trace type from the dropdown menu and click Record . Interact with your app and click Stop recording when you’re done. The profiler automatically selects the recorded time frame and displays its tracing information in the method trace pane, as shown in figure 2. If you want to inspect the method trace for a different thread, just select it from the thread activity timeline.
- Selected time frame: Determines the portion of the recorded time frame you want to inspect in the trace pane. When you first record a method trace, the CPU profiler automatically selects the entire length of your recording in the CPU timeline. If you want to inspect method trace data for only a portion of the recorded time frame, you can click and drag the edges of the highlighted region to modify its length.
- Timestamp: Indicates the start and end time of a recorded method trace (relative to when the profiler started collecting CPU usage information from your device). You can click the timestamp to automatically select the entire recording as your selected time frame—this is particularly useful if you have multiple recordings you want to switch between.
- Trace pane: Displays method trace data for the time frame and thread you’ve selected. This pane appears only after you record at least one method trace. In this pane, you can select how you want to view each stack trace (using the trace tabs) and how you want to measure execution time (using the time reference drop down menu).
- Choose to display your method trace as a Top Down tree, Bottom Up tree, Call Chart, or Flame Chart. You can learn more about each of the trace pane tabs in the sections below.
- Select one of the following options from the dropdown menu to determine how
timing information for each method call is measured:
- Wall clock time: timing information represents actual elapsed time.
- Thread time: timing information represents actual elapsed time minus any portion of that time where the thread is not consuming CPU resources. For any given method, its thread time is always less than or equal to its wall clock time. Using thread time gives you a better understanding of how much of a thread’s actual CPU usage is consumed by a given method.
Inspect traces using the Call Chart tab
The Call Chart tab provides a graphical representation of a method trace, where the period and timing of a method call (or caller) is represented in the horizontal axis, and its callees are shown along the vertical axis. Method calls to system APIs are shown in orange, calls to your app’s own methods are shown in green, and method calls to third-party APIs (including java language APIs) are shown in blue. Figure 3 below shows an example call chart and illustrates the concept of self time, children time, and total time for a given method. You learn more about these concepts in the section on how to inspect traces using Top Down and Bottom Up.
Tip: To jump the source code of a method, right-click the method and select Jump to Source. This works from any of the trace pane tabs.
Inspect traces using the Flame Chart tab
The Flame Chart tab provides an inverted call chart that aggregates identical call stacks. That is, identical methods that share the same sequence of callers are collected and represented as one longer bar in a flame chart (rather than displaying them as multiple shorter bars, as shown in a call chart). This makes it easier to see which methods consume the most amount of time. However, this also means that the horizontal axis no longer represents a timeline—instead, it indicates the relative amount of time each method takes to execute.
To help illustrate this concept, consider the call chart in figure 4 below. Note that method D makes multiple calls to B (B1, B2, and B3), and some of those calls to B make a call to C (C1 and C3).
Because B1, B2, and B3 share the same sequence of callers (A → D → B) they are aggregated, as shown below. Similarly, C1 and C3 are aggregated because they share the same sequence of callers (A → D → B → C)—note that C2 is not included because it has a different sequence of callers (A → D → C).
The aggregated method calls are used to create the flame chart, as shown in figure 6. Note that, for any given method call in a flame chart, the callees that consume the most amount of CPU time appear first.
Inspect traces using Top Down and Bottom Up
The Top Down tab displays a list of method calls where expanding a method node displays its callees. Figure 7 shows a top down graph for the call chart in figure 3. Each arrow in the graph points from a caller to a callee.
As shown in figure 7, expanding the node for method A in the top down tab displays its callees, methods B and D. After that, expanding the node for method D exposes it callees, methods B and C, and so on. Similar to the Flame chart tab, the top down tree aggregates trace information for identical methods that share the same call stack. That is, the Flame chart tab provides a graphical representation of the Top down tab.
The Top Down tab provides the following information to help describe CPU time spent on each method call (times are also represented as a percentage of the thread’s total time over the duration of the selected time frame):
- Self: the amount of time the method call spent executing its own code and not that of its callees, as illustrated in figure 3 for method D.
- Children: the amount of time the method call spent executing its callees and not its own code, as illustrated in figure 3 for method D.
- Total: the sum of the method’s Self and Children time. This represents the total amount of time the app spent executing a method call, as illustrated in figure 3 for method D.
The Bottom Up tab displays a list of method calls where expanding a method’s node displays its callers. Using the example trace shown in figure 7, figure 8 provides a bottom up tree for method C. Opening the node for method C in the bottom up tree displays each of its unique callers, methods B and D. Note that, although B calls C twice, B appears only once when expanding the node for method C in the bottom up tree. After that, expanding the node for B displays its caller, methods A and D.
The Bottom Up tab is useful for sorting methods by those that consume the most (or least) CPU time. And you can inspect each node to determine which callers spend the most CPU time invoking those methods. Compared to the top down tree, timing info for each method in a bottom tree is in reference to the method at the top of each tree (top node). CPU time is also represented as a percentage of the thread’s total time during that recording. The following table helps explain how to interpret timing information for the top node and its caller methods (sub-nodes).
|Method at the top of the bottom up tree (top node)||Represents the total amount of time the method spent executing its own code and not that of its callees. Compared to the top down tree, this timing info represents a sum of all calls to this method over the duration of the recording.||Represents the total amount of time the method spent executing its callees and not its own code. Compared to the top down tree, this timing info represents the sum of all calls to this method’s callees over the duration of the recording.||The sum of the self time and children time.|
|Caller methods (sub-nodes)||Represents the total self time of the callee when being called by the caller. Using the bottom up tree in figure 8 as an example, the self time for method B would equal the sum of the self times for each execution of method C when called by B.||Represents the total children time of the callee when being invoked by the caller. Using the bottom up tree in figure 8 as an example, the children time for method B would equal the sum of the children times for each execution of method C when called by B.||The sum of the self time and children time.|
Note: For a given recording, Android Studio stops collecting new data when the profiler reaches the file size limit (however, this does not stop the recording). This typically happens much more quickly when performing instrumented traces because this type of tracing collects more data in a shorter amount of time, compared to a sampled trace. If you extend the inspection time frame into a period of the recording that occurred after reaching the limit, timing data in the trace pane does not change (because no new data is available). Additionally, the trace pane displays NaN for timing information when you select only the portion of a recording that has no data available.