
In his book Hackers & Painters, Paul Graham asserted, “The disparity in the efficiency of languages is becoming more pronounced, hence the rising importance of profilers. Currently, performance analysis isn’t given the attention it deserves. Many still seem to hold onto the belief that the key to accelerating program execution lies in developing compilers that generate faster code. As the gap between code efficiency and machine performance widens, it will become increasingly apparent that enhancing the execution speed of application software hinges on having a good profiler to guide program development.” by Paul Graham, Hackers & Painters
Search for “Android optimization tools” and you’ll find plenty of articles. Many repeat the same material or go straight to instructions without explaining how the tools fit together. It is easy to come away thinking one tool can solve everything. Our team’s experience says otherwise: there is no silver bullet. Tools change, and familiar problems return in new forms. You still need to understand what is happening underneath.
We’ll start with the data we collect, how we collect it, and how we analyze it. Then we’ll look at Google’s traditional “big three” performance tools. We want you to understand the measurements as well as the tools, and save yourself some of the detours others have already taken.
Technical review — 2026-10-14: This article was published in January 2022. The Systrace and Studio screenshots are historical; current entry points and evidence limits are updated below. The three tools discussed are Systrace, Perfetto, and Android Studio Profiler, not a required sequence for every performance problem.
For current investigations, combine system tracing with repeatable measurement and field signals. FrameTimeline requires Android 12 / API 31 or later and a recording that includes its data. Use Macrobenchmark metrics such as StartupTimingMetric and FrameTimingMetric to compare controlled scenarios. Baseline Profiles and Startup Profiles are optimization inputs, not measurements or proof of improvement. Android vitals provides Google Play field signals from eligible, opted-in devices, not complete coverage of all users or every rendering path. Supplement it with telemetry appropriate to the product.
1 Observability Techniques in Performance Analysis
- Has this operation been executed? How long did it take?
- Why is there a significant difference between two versions?
- What operations is the system executing when CPU usage is high?
- Why has the startup speed slowed down?
- Why does this page always stutter when scrolling?
You’ve probably heard these questions from colleagues or your boss more than once. You get the logs, search for familiar keywords, and try to piece together what happened. Something is missing, so you add more logging and reproduce the problem again. It takes time, and other people’s time too. Isn’t there a better way to spend that effort—more time solving the problem, less time repeating the same manual work?
Of course, there is (otherwise this article wouldn’t exist)—we refer to it as observability techniques.
Observability uses recorded runtime information to help explain complex systems. Choose detail according to the question: collecting more data can add overhead without improving the explanation.
Static checks and code reviews cannot catch every problem. Some only show up when the software runs, even in an automated test. Once that happens, you still need to work out what the program did. Good observability makes that job easier. That is why we see it as part of a team’s basic engineering setup, rather than something to bolt on after a failure.
Reading code alone can make it hard to follow a complex system, especially when several components interact. Runtime observations let you see those interactions, investigate failures, and look for bottlenecks. Building that view takes work across the system.
Let’s look at the data, collection methods, and analysis methods in turn.
1.1 Data Types
Logs, metrics, and traces describe different aspects of a running system. They are not three file formats or an exhaustive classification of logs: profiles also describe resource usage at code level. A JSON file, database, or binary stream can carry several kinds of signal. The recording only tells us what its instrumentation and capture settings observed; it cannot reconstruct every part of the system. See the OpenTelemetry signal definitions.
Log Type
A log records an event, usually with a timestamp, source, severity, and message or structured fields. Text lines are common, but binary and structured logging also exist. Logs with suitable timestamps and identifiers can produce metrics or help reconstruct traces; missing correlation fields cannot be recovered by changing the storage format.
Error, warning, and debug levels help filter records, but severity is not a ranking of performance relevance. A scheduling delay may have no error log at all. Select records according to the question, and keep the cost of formatting, collection, and storage in mind.
Metric Type
Metrics record numerical measurements such as CPU utilization, CPU frequency, and context-switch counts. Distinguish a gauge or instantaneous observation from a cumulative counter and from a statistic aggregated over a window. A counter needs differences between observations, elapsed time, and reset handling before it can be interpreted as a rate.
For average CPU utilization over five minutes, state the time window and denominator: one core, all cores, or a process. Equally spaced utilization samples may be averaged arithmetically; unequal intervals require time weighting. Giving recent samples more weight produces a different statistic. Missing samples are unknown, not zero, and a mean can hide short spikes.
Typed numerical data often reduces parsing work and can save space, but metrics do not inherently use fixed-width storage, nor do logs necessarily use ASCII. Encoding, labels and their cardinality, compression, and sampling frequency determine the actual cost. An enum may describe a state, but averaging its numeric codes usually has no useful meaning.
Trace Type
Trace events can record names, timestamps, intervals, counters, and relationships. They help investigate function execution, Binder transactions, and work spanning threads or processes. Parent-child relationships or asynchronous flow identifiers must be recorded or derived from a defined protocol; nearby timestamps alone do not establish causality.
Android apps interact with system services through Binder. Following a transaction may require client, server, and scheduler data. A long synchronous call includes possible queueing and waiting as well as execution; it is not automatically time spent on the CPU.
Manually instrumented sections and method entry/exit tracing provide explicit boundaries for the code they cover. They do not reveal uninstrumented functions, and dense method tracing can substantially perturb execution. Nested durations also cannot simply be added without double-counting.
Stack sampling answers a different question: which stacks were observed, and how much sampled weight they account for. It does not record every invocation or exact entry/exit times, and short functions can be missed. A shorter interval may improve coverage but increases collection cost and does not guarantee a more accurate result. “Profiler” includes sampling and instrumentation tools, rather than naming sampling alone.
1.2 Data Acquisition Methods
Static Code and Dynamic Tracing
Static instrumentation adds recording points in source code, generated code, or libraries. Adding a missing point often requires a rebuild and deployment; existing switches may enable points already present. If one recording lacks a needed event, other signals or a new capture may still help.
Dynamic tracing can attach to existing tracepoints or add probes without editing the target source. It does not require every probe to have been embedded by the application, or require a dynamically typed language. For example, Linux uprobes can instrument userspace binary offsets when the kernel supports them; kprobes target kernel code.
Android includes eBPF infrastructure, but that does not grant an ordinary app unrestricted tracing access. Availability depends on kernel features, probe support, privileges, and SELinux policy. Choose the mechanism for the target build and deployment environment rather than assuming it is restricted to kernel developers or available on every retail phone.
Unconditional and Conditional Capture
Unconditional capture records the selected sources for a configured session without an anomaly trigger. Its cost depends on source frequency, stack collection, buffering, and storage. Reduce unnecessary data while retaining the events needed to answer the question.
Conditional capture uses a detector to start or finish a recording. The detector itself has a cost. Starting after an anomaly cannot recover events that were never recorded; continuously collecting into a bounded buffer and stopping after an anomaly can retain recent history, but collection still consumes resources before the trigger. These are different strategies, not a guarantee of zero overhead outside failures.
Disk Write Strategy
Continuous file output supports longer recordings but adds I/O and storage costs. Alternatively, a ring buffer can retain recent data in memory until a trigger finalizes the capture. Its capacity and event rate limit how much history survives: old data is overwritten, and producer-side loss can occur independently.
Perfetto distinguishes overwrite and discard policies and can periodically drain buffers to a file. A trigger does not guarantee that the root cause remains in the buffer. Check capture duration, enabled sources, buffer sizing, and reported data loss before treating an absent event as evidence. See buffer behavior and trace configuration.
1.3 Analysis Methods
Data Visualization Analysis
A timeline places events on process, thread, or other tracks so that their timing and recorded relationships can be examined together. Systrace and Perfetto provide this view; KernelShark serves related Linux tracing workflows. Overlapping intervals are useful clues but do not by themselves prove that one caused another.
The historical Systrace viewer belongs to the Catapult project. Its Trace Event Format is a specific schema, not a promise that any JSON log will render as a trace. Modern Perfetto also imports supported formats; check the format list for the actual input.
Database Analysis
Turning captured data into tables makes repeatable queries and aggregation possible. Perfetto’s Trace Processor exposes trace data through SQL; Python can organize queries and results into an automated analysis. Search systems such as Elastic’s stack provide another way to explore structured logs, with their own query model.
Text inspection, visualization, and SQL complement each other. A quick text search may locate an event; a timeline may explain its context; SQL may compare many occurrences. None is mandatory merely because a problem involves several modules. Preserve units, time boundaries, and correlation identifiers when converting data so that the analysis still answers the original question.
2 Google’s Android Performance Analysis Tools
The three tools discussed here are Systrace, Perfetto, and Android Studio Profiler. These were common starting points in our workflow when this article was written. Choose tools according to the evidence needed: a heap problem, for example, may call for a memory tool first. The comparison below summarizes scope rather than prescribing a fixed sequence.
2.1 First Generation System Performance Analysis Tool - Systrace
Systrace is a historical Android system-tracing workflow that visualizes selected app, framework, and kernel events. It was never the only performance tool. System tracing is often less intrusive than recording every method entry and exit, but its overhead is not zero and varies with enabled categories and event volume. For new investigations, start with Android system tracing and Perfetto.

Systrace Design Philosophy
Android instrumentation marks selected operations such as input dispatch, rendering, and inter-process communication. Userspace markers produced through android.os.Trace or native ATrace APIs are distinct from kernel events such as sched_switch and cpu_idle; the historical capture path can collect them together through ftrace.

The screenshot shows both tracing_mark_write markers and kernel events. Java framework code and application code can emit trace sections; native framework code has corresponding ATrace facilities. Categories, app selection, permissions, kernel support, and buffer loss determine what is actually captured. The result exposes useful parts of system activity, not every operation in Android.
Practical Applications
System traces help investigate startup, slow interactions, scrolling, window animations, and periods of apparent unresponsiveness. Scheduler and Binder events can distinguish execution from some forms of waiting when the relevant sources were captured.
An ANR investigation also needs its reason and thread stacks; a crash may require logcat, an exception stack, or a native tombstone. A timeline alone does not explain every failure. Old Systrace HTML files belong to the historical viewer workflow; for a new recording use the current system-tracing tools and open a supported trace in Perfetto UI.
2.2 The Next-Generation Performance Analysis Full Stack Tool - Perfetto
Perfetto provides recording, visualization, and analysis components for system and application tracing. It is the current Android system-tracing direction. Support for importing a format is also separate from having every historical viewer feature.
The service model separates producers, data sources, the tracing service, and consumers. This architecture supports sources beyond ftrace and allows several kinds of data to share a recording.
Highlighted Features
A producer supplies data for configured sources; the service manages sessions and buffers. Kernel ftrace events and Android userspace markers are important sources, but process statistics, heap profiling, and other sources have different collection paths. The native trace format uses protobuf messages; it is not a SQLite database.

Perfetto can capture into memory or stream to a file. Long recordings still depend on buffer capacity, source rate, storage, and overhead. The official Android log source is documented as userdebug-only: logcat is not guaranteed to be present in an ordinary user-build trace. Missing tracks or samples do not mean zero activity.
The UI, command-line tools, and Android’s System Tracing app provide different recording entry points. The trigger API activates names in a session configured in advance by an authorized consumer. START_TRACING begins recording after the trigger; STOP_TRACING can finalize an already recording session and retain bounded pre-trigger history. An app activating a trigger does not thereby gain permission to configure arbitrary system-wide tracing.
Trace Processor parses a trace and provides SQL tables through the UI, command line, and APIs. You do not have to export a SQLite file first. Python and DataFrames can help automate analysis. Large traces may benefit from a native Trace Processor, but memory and parsing limits remain; “300 MB” is not a universal failure threshold for either viewer.
For CPU totals, identify whether SurfaceFlinger means the process or one thread, then sum captured running intervals clipped to the chosen time window. Summing nested function wall times would double-count and include waiting. Likewise, map CPU IDs to the device’s actual topology before labeling a core as big or little.
Practical Application
Perfetto is a useful first tool for many system timing questions. Its flexibility does not make it an arbitrary dynamic-probe framework like an eBPF-based tracing setup, and it cannot manufacture events that its sources did not record.
Do not infer a high or negligible runtime cost from protobuf encoding alone. Measure the selected sources, sampling, buffers, and file output under the intended workload. The command-line Trace Processor supports scripted SQL and text output as well as interactive investigation; analysis is not limited to the webpage.
2.3 Android Studio Profiler Tool
Android Studio integrates profiling with application development. Its current Profiler workflow is organized around tasks and recordings; the old “CPU, Memory, Network, Battery” list is not a reliable map of today’s UI.
Network Inspector is under View → Tool Windows → App Inspection, with support depending on the networking library. Power Profiler, introduced in Android Studio Hedgehog 2023.1.1, uses supported device data; ODPM power rails require supported Pixel hardware, rather than being a generic battery meter on every Android phone.
Use a release-like, profileable build where the chosen task supports it. Some tasks, including Java/Kotlin allocation recording and heap dumps, require a debuggable app. Record Studio, Android, app build, and capture settings when comparing results; changing debug mode or instrumentation changes the measurement conditions.
Highlighted Features
The historical CPU Profiler menu below offers several different kinds of evidence:
- System Trace records selected scheduling, rendering, and instrumented intervals. UI aggregation or filtering is not the same as a guarantee that all short events were discarded. Export a supported recording to Perfetto UI when broader system analysis is useful.
- Java/Kotlin sampling or method tracing: stack samples estimate where execution is observed; instrumented entry/exit records expose call boundaries with greater perturbation. They are not interchangeable measurements.
- Native sampling, backed by simpleperf in the historical workflow, identifies sampled native stacks. It is not a complete C/C++ entry/exit trace. Simpleperf supports hardware PMU events and software events; available events, stack unwinding, and symbols depend on the target. See the simpleperf documentation.

This is an old Studio menu, retained to explain the original example. Current task names and available recording options depend on the Studio and Android versions.
Practical Application
Slow response and jank are common timing problems, alongside memory, power, and other performance concerns. For an unfamiliar rendering problem, a system trace can locate a long interval, then method or native profiling can provide more detail. Choose the next recording according to the unresolved question rather than always running all three tools.

Suppose you’re handed a badly performing app and don’t know its code. Where would you start? In this historical example, egl_core has a long onDrawFrame interval. The selected interval is about one second of elapsed time. Its name alone does not establish the underlying source method or the amount of CPU work; use source instrumentation and scheduler data to check those points.

The Java recording points toward Utils.onDraw. Its selected interval shows roughly 1 s wall time but only 402 μs CPU time, suggesting waiting in this recording rather than one second of computation. That distinction matters before deciding what to optimize.

The native view contains Java_com_gl_shader_Utils_onDraw → sleep → nanosleep. This makes sleeping a candidate explanation for the delay. The three screenshots come from separate recordings, and their displayed CPU-time fields cannot be combined into a single CPU accounting result. The original complete captures and example source are not available here. Confirm the sleep call in source, correlate scheduling in a new capture, and repeat the same scenario after removing it before claiming a verified fix.
Studio’s integration makes this drill-down convenient for application developers. System-wide analysis may still need additional tools and permissions.
2.4 Comparative Analysis
Capture triggers, buffering, and file output are separate choices. The following table describes capabilities, not a claim that every tool continuously writes every event to disk.
| Tool | Main use | Evidence and limits |
|---|---|---|
| Systrace | Historical Android system tracing | Selected kernel events and userspace markers; legacy viewer workflow |
| Perfetto | System and app tracing | Events, counters, stacks, and other configured sources; memory/file and trigger modes; UI and SQL analysis |
| Android Studio Profiler | App-focused profiling tasks | System traces, samples, method records, heap or allocation data depending on the task and build |
| simpleperf | CPU event counting and stack sampling | Hardware or software events; sampled weight is not invocation count; text and visual reports |
| Snapdragon Profiler | Supported Snapdragon platforms | CPU, GPU, and some DSP analysis; DSP support is not universal |
| Historical Mali Graphics Debugger / Graphics Analyzer | Graphics API debugging on supported devices | Arm says Graphics Analyzer was last included in Performance Studio 2024.2; consult current Arm tools for the applicable workflow |
| Android logcat / dumpsys | Event logs / service diagnostics | Log buffers and service-specific snapshots or reports are different data sources; collection and redirection determine persistence |
| Android GPU Inspector | Supported Android GPU system/frame profiling | Device, driver, Android version, and graphics API requirements apply |
| eBPF-based tools | Programmable tracing and aggregation | Events, statistics, and stacks depending on the program and attachment point; requires suitable privileges and kernel support |
| ftrace | Linux tracing infrastructure | Kernel events, function tracing, and supported dynamic probes; buffer and output policies are configurable |
Choose GPU tools by the actual GPU, driver, OS, and API support matrix. A SoC vendor name alone does not establish Mali/Adreno compatibility.
3 On “Instruments, Techniques, Philosophy”
At the tool level, Linux and Android tools keep gaining data sources and bringing more information into one place. Those changes have a pattern. Learn what a tool can show you and when it is useful; the point is to solve problems more efficiently, not to spend all your time learning the next tool.
The technique level depends on knowing the system you work on: how a frame is drawn, how the CPU schedules work, how I/O is submitted. That knowledge helps you choose a tool and interpret its output. After enough experience, you can sometimes spot a useful clue before digging through every detail. You start connecting what you see with problems you’ve encountered before.
At the level of judgment, ask what problem you actually need to solve. How far does the improvement need to go? What will it cost, and what will you get for that effort? Which path is worth taking? What needs to happen first, and what depends on it?
We’ll use these three perspectives in the articles that follow. We want you to leave with more than one technique to copy: when the tool or even the system changes, you should still be able to ask useful questions, choose the information you need, and judge where to spend your effort.
About Me && Blog
- About Me: I am eager to interact and progress together with everyone.
- Follow me on Twitter
- Blog Content Navigation
- Record of Excellent Blog Articles - Essential Skills and Tools for Android Performance Optimization
An individual can move faster, a group can go further.