<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>DevTools on MangoDriod</title><link>https://md.eknath.dev/tags/devtools/</link><description>Recent content in DevTools on MangoDriod</description><generator>Hugo -- 0.141.0</generator><language>en-us</language><lastBuildDate>Mon, 14 Sep 2026 12:46:48 +0530</lastBuildDate><atom:link href="https://md.eknath.dev/tags/devtools/index.xml" rel="self" type="application/rss+xml"/><item><title>A Practical Guide to Google Perfetto: Why, Setup, Recording, and Deep Trace Analysis</title><link>https://md.eknath.dev/posts/android/mastering-perfetto-system-tracing-guide/</link><pubDate>Tue, 01 Sep 2026 15:10:00 +0530</pubDate><guid>https://md.eknath.dev/posts/android/mastering-perfetto-system-tracing-guide/</guid><description>&lt;p>If you have ever spent hours debugging a mysterious UI frame drop, an unpredictable app startup delay, or high CPU utilization, you know that log statements and simple timers only tell half the story.&lt;/p>
&lt;p>Modern operating systems and apps are deeply asynchronous. A lag spike in your app&amp;rsquo;s main thread could be caused by garbage collection, lock contention, CPU frequency throttling, or another background process starving your app of CPU cycles.&lt;/p></description><content:encoded><![CDATA[<p>If you have ever spent hours debugging a mysterious UI frame drop, an unpredictable app startup delay, or high CPU utilization, you know that log statements and simple timers only tell half the story.</p>
<p>Modern operating systems and apps are deeply asynchronous. A lag spike in your app&rsquo;s main thread could be caused by garbage collection, lock contention, CPU frequency throttling, or another background process starving your app of CPU cycles.</p>
<p>Enter <strong><a href="https://github.com/google/perfetto">Perfetto</a></strong> — Google&rsquo;s open-source, next-generation system profiling, app tracing, and metrics analysis platform.</p>
<p>In this guide, we&rsquo;ll walk through what Perfetto is, why it completely supersedes legacy tools like Systrace, how to set it up, various ways to record traces, and how to analyze them like a pro (including querying trace data with SQL!).</p>
<hr>
<h2 id="tldr---quick-start">TL;DR - Quick Start</h2>
<p>Want to capture a trace right now?</p>
<ol>
<li>Connect your Android device via USB with USB Debugging enabled.</li>
<li>Open <strong><a href="https://ui.perfetto.dev">ui.perfetto.dev</a></strong> in Chrome.</li>
<li>Click <strong>Record new trace</strong> in the left sidebar.</li>
<li>Select <strong>Recording mode: Stop when full</strong>, choose <strong>UI &amp; Rendering</strong> preset, select your device, and click <strong>Start Recording</strong>.</li>
<li>Reproduce your app workflow. The trace will open automatically in the web visualizer.</li>
</ol>
<hr>
<h2 id="1-what-is-perfetto--why-should-you-care">1. What is Perfetto &amp; Why Should You Care?</h2>
<h3 id="the-evolution-why-legacy-systrace-was-retired">The Evolution: Why Legacy Systrace Was Retired</h3>
<p>For years, Android developers relied on <code>systrace</code> and <code>atrace</code>. While useful, Systrace had significant limitations:</p>
<ul>
<li>High performance overhead when capturing high-frequency events.</li>
<li>Fixed in-memory buffer constraints leading to lost events.</li>
<li>Heavy HTML trace viewers that crashed browser tabs when opening files larger than a few hundred megabytes.</li>
<li>Limited cross-platform extensibility.</li>
</ul>
<p>Google engineered <strong>Perfetto</strong> from the ground up to unify tracing across the entire stack — Android OS (from Android 9/10+), Linux, Chrome, and native/userspace applications.</p>
<pre tabindex="0"><code>+-------------------------------------------------------------------------+
|                              Perfetto Stack                             |
+-------------------------------------------------------------------------+
|  Data Sources: Kernel ftrace | App Trace points | heapprofd | GPU/Power |
+-------------------------------------------------------------------------+
|  Collection:   traced daemon (Lock-free Shared Memory Ring Buffers)      |
+-------------------------------------------------------------------------+
|  Storage:      Compact, streaming Protobuf format (.perfetto-trace)     |
+-------------------------------------------------------------------------+
|  Analysis:     Trace Processor (SQL engine) + ui.perfetto.dev (Canvas)  |
+-------------------------------------------------------------------------+
</code></pre><h3 id="why-perfetto-stands-out">Why Perfetto Stands Out</h3>
<ol>
<li><strong>Near-Zero Overhead</strong>: Uses a shared-memory IPC architecture (<code>traced</code> daemon) where data is written directly to shared ring buffers without heavy kernel context switches.</li>
<li><strong>Unified Data Sources</strong>: Correlates kernel events (CPU scheduling, thread states, CPU frequencies, disk I/O, IRQs) with app-level custom slices (<code>Trace.beginSection</code>), memory allocations, and battery counters.</li>
<li><strong>No App Instrumentation Required for System Insights</strong>: You can see which CPU core ran your thread, when it got preempted, and what other process took over the core.</li>
<li><strong>Trace Processor (SQL Analysis Engine)</strong>: Every trace is essentially an in-memory SQLite database. You can query your trace using SQL to calculate 99th percentile frame times, lock wait times, or memory spikes.</li>
<li><strong>Blazing-Fast Web UI</strong>: <a href="https://ui.perfetto.dev">ui.perfetto.dev</a> uses WebAssembly and HTML5 Canvas to smoothly pan and zoom through gigabytes of trace data at 60 FPS.</li>
</ol>
<hr>
<h2 id="2-setting-up-perfetto">2. Setting Up Perfetto</h2>
<h3 id="prerequisites--device-compatibility">Prerequisites &amp; Device Compatibility</h3>
<ul>
<li><strong>Android 10 (API 29) or higher</strong>: Perfetto daemons are built into the OS and enabled by default.</li>
<li><strong>Android 9 (Pie)</strong>: Perfetto is available but must be enabled via adb properties.</li>
<li><strong>Host PC</strong>: macOS, Linux, or Windows with <code>adb</code> installed.</li>
</ul>
<h3 id="preparing-your-android-app-for-production-accurate-tracing">Preparing Your Android App for Production-Accurate Tracing</h3>
<blockquote>
<p><strong>Warning</strong>: Never profile an app with <code>debuggable = true</code> in production performance tests. Debug builds disable ART compiler optimizations (like inline functions and Ahead-of-Time compilation) and add significant JIT overhead.</p>
</blockquote>
<p>Instead, use the <code>&lt;profileable&gt;</code> tag introduced in Android 10 (API 29) to allow Perfetto to capture detailed app-level traces without debug overhead:</p>
<p>In your <code>AndroidManifest.xml</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#f92672">&lt;manifest</span> <span style="color:#a6e22e">xmlns:android=</span><span style="color:#e6db74">&#34;http://schemas.android.com/apk/res/android&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;application</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">android:allowBackup=</span><span style="color:#e6db74">&#34;true&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">android:icon=</span><span style="color:#e6db74">&#34;@mipmap/ic_launcher&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">android:label=</span><span style="color:#e6db74">&#34;@string/app_name&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">android:theme=</span><span style="color:#e6db74">&#34;@style/Theme.MyApp&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">&lt;!-- Allows Perfetto &amp; Android Studio to profile non-debuggable release builds --&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;profileable</span> 
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">android:shell=</span><span style="color:#e6db74">&#34;true&#34;</span> 
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">android:enabled=</span><span style="color:#e6db74">&#34;true&#34;</span> <span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;activity</span> <span style="color:#a6e22e">android:name=</span><span style="color:#e6db74">&#34;.MainActivity&#34;</span> <span style="color:#a6e22e">android:exported=</span><span style="color:#e6db74">&#34;true&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>            ...
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;/activity&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;/application&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">&lt;/manifest&gt;</span>
</span></span></code></pre></div><h3 id="adding-custom-application-trace-markers-kotlin--jetpack-compose">Adding Custom Application Trace Markers (Kotlin / Jetpack Compose)</h3>
<p>Add the Jetpack Tracing library to your <code>app/build.gradle.kts</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span>dependencies {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Official AndroidX Tracing library
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    implementation(<span style="color:#e6db74">&#34;androidx.tracing:tracing-ktx:1.3.0&#34;</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h4 id="tracing-standard-kotlin-functions">Tracing Standard Kotlin Functions</h4>
<p>Use the <code>trace</code> inline function to wrap critical sections (e.g. database queries, network JSON parsing, complex transformations):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> androidx.tracing.trace
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">loadFeedData</span>(): List&lt;FeedItem&gt; {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> trace(<span style="color:#e6db74">&#34;FeedRepository:loadFeedData&#34;</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">val</span> rawData = fetchFromDatabase()
</span></span><span style="display:flex;"><span>        trace(<span style="color:#e6db74">&#34;FeedRepository:mapItems&#34;</span>) {
</span></span><span style="display:flex;"><span>            rawData.map { <span style="color:#66d9ef">it</span>.toUiModel() }
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h4 id="tracing-jetpack-compose-recompositions">Tracing Jetpack Compose Recompositions</h4>
<p>If you want Compose to automatically generate trace slices for every composable during recomposition, add the Compose runtime tracing dependency:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span>dependencies {
</span></span><span style="display:flex;"><span>    implementation(<span style="color:#e6db74">&#34;androidx.compose.runtime:runtime-tracing:1.7.8&#34;</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>This annotates the trace with composable names, source file names, and recomposition counts directly in the Perfetto timeline.</p>
<hr>
<h2 id="3-how-to-record-traces">3. How to Record Traces</h2>
<p>There are 4 popular ways to record Perfetto traces depending on your workflow.</p>
<h3 id="method-1-the-perfetto-web-ui-recommended-for-quick-visual-debugging">Method 1: The Perfetto Web UI (Recommended for Quick Visual Debugging)</h3>
<ol>
<li>Open <strong><a href="https://ui.perfetto.dev">ui.perfetto.dev</a></strong> in Google Chrome or any Chromium browser.</li>
<li>Click <strong>Record new trace</strong> on the left menu.</li>
<li>Select <strong>Add ADB device</strong> (or WebUSB). Approve the USB debugging prompt on your phone.</li>
<li>Customize your probe selections:
<ul>
<li><strong>CPU</strong>: Cores, CPU Frequency, Syscalls, Scheduling details (<code>sched_switch</code>).</li>
<li><strong>Android apps &amp; jank</strong>: App startup, Choreographer, SurfaceFlinger, Frame timeline.</li>
<li><strong>Target App</strong>: Enter your package name (e.g., <code>com.example.myapp</code>).</li>
</ul>
</li>
<li>Set buffer size (e.g., <code>64 MB</code>) and duration (e.g., <code>10s</code>).</li>
<li>Click <strong>Start Recording</strong>, perform your user action, and watch the trace automatically parse and open.</li>
</ol>
<hr>
<h3 id="method-2-command-line-with-record_android_trace">Method 2: Command Line with <code>record_android_trace</code></h3>
<p>If you prefer the terminal or want to automate trace collection, Google provides a standalone Python helper script:</p>
<h4 id="download-the-script">Download the script:</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -O https://raw.githubusercontent.com/google/perfetto/main/tools/record_android_trace
</span></span><span style="display:flex;"><span>chmod +x record_android_trace
</span></span></code></pre></div><h4 id="record-a-10-second-trace">Record a 10-second trace:</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>./record_android_trace <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -o app_jank.perfetto-trace <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -t 10s <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -b 64mb <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -a com.example.myapp <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  sched freq idle am wm gfx view binder_driver hal dalvik camera
</span></span></code></pre></div><h4 id="breakdown-of-flags">Breakdown of flags:</h4>
<ul>
<li><code>-o &lt;file&gt;</code>: Output file path.</li>
<li><code>-t &lt;time&gt;</code>: Tracing duration (e.g. <code>10s</code>, <code>1m</code>).</li>
<li><code>-b &lt;size&gt;</code>: In-memory buffer size.</li>
<li><code>-a &lt;package&gt;</code>: Enables app-level <code>atrace</code> / <code>androidx.tracing</code> categories for this specific package.</li>
<li><code>sched freq am wm gfx view binder_driver</code>: Key kernel and OS tracing categories.</li>
</ul>
<p>Once finished, open the output <code>.perfetto-trace</code> file directly at <a href="https://ui.perfetto.dev">ui.perfetto.dev</a> by clicking <strong>Open trace file</strong>.</p>
<hr>
<h3 id="method-3-system-tracing-quick-settings-tile-on-device">Method 3: System Tracing Quick Settings Tile (On-Device)</h3>
<p>For capturing real-world bugs in the wild without a PC connected:</p>
<ol>
<li>On your Android phone, go to <strong>Settings &gt; System &gt; Developer options &gt; System Tracing</strong>.</li>
<li>Toggle on <strong>Show Quick Settings tile</strong>.</li>
<li>Pull down your phone&rsquo;s notification shade and tap the <strong>System Tracing</strong> tile to start recording.</li>
<li>Reproduce the bug or jank.</li>
<li>Tap the tile again to stop.</li>
<li>Share or pull the generated trace from <code>/data/local/traces/</code> via ADB:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>adb pull /data/local/traces/ .
</span></span></code></pre></div></li>
</ol>
<hr>
<h3 id="method-4-automated-tracing-with-jetpack-macrobenchmark">Method 4: Automated Tracing with Jetpack Macrobenchmark</h3>
<p>If you want traces captured automatically during CI/CD regression tests, use Jetpack Macrobenchmark:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#a6e22e">@RunWith</span>(AndroidJUnit4<span style="color:#f92672">::</span><span style="color:#66d9ef">class</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">ScrollBenchmark</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">@get</span>:Rule
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">val</span> benchmarkRule = MacrobenchmarkRule()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">@Test</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">fun</span> <span style="color:#a6e22e">scrollFeedCompilationMode</span>() = benchmarkRule.measureRepeated(
</span></span><span style="display:flex;"><span>        packageName = <span style="color:#e6db74">&#34;com.example.myapp&#34;</span>,
</span></span><span style="display:flex;"><span>        metrics = listOf(FrameTimingMetric(), StartupTimingMetric()),
</span></span><span style="display:flex;"><span>        compilationMode = <span style="color:#a6e22e">CompilationMode</span>.Full(),
</span></span><span style="display:flex;"><span>        iterations = <span style="color:#ae81ff">5</span>,
</span></span><span style="display:flex;"><span>        startupMode = <span style="color:#a6e22e">StartupMode</span>.COLD
</span></span><span style="display:flex;"><span>    ) {
</span></span><span style="display:flex;"><span>        pressHome()
</span></span><span style="display:flex;"><span>        startActivityAndWait()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">val</span> feedList = device.findObject(<span style="color:#a6e22e">By</span>.res(<span style="color:#e6db74">&#34;feed_list&#34;</span>))
</span></span><span style="display:flex;"><span>        feedList.setGestureMargin(device.displayWidth / <span style="color:#ae81ff">5</span>)
</span></span><span style="display:flex;"><span>        feedList.fling(<span style="color:#a6e22e">Direction</span>.DOWN)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Every benchmark run generates a direct link to a Perfetto trace file in Android Studio’s test results window.</p>
<hr>
<h2 id="4-how-to-navigate-and-read-a-trace-in-perfetto-ui">4. How to Navigate and Read a Trace in Perfetto UI</h2>
<p>When you open a trace in <a href="https://ui.perfetto.dev">ui.perfetto.dev</a>, the interface might look intimidating at first. Here is how to navigate it smoothly:</p>
<h3 id="essential-keyboard-shortcuts">Essential Keyboard Shortcuts</h3>
<table>
  <thead>
      <tr>
          <th>Key</th>
          <th>Action</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>W</code></td>
          <td>Zoom in on timeline (centered at cursor)</td>
      </tr>
      <tr>
          <td><code>S</code></td>
          <td>Zoom out</td>
      </tr>
      <tr>
          <td><code>A</code></td>
          <td>Pan left</td>
      </tr>
      <tr>
          <td><code>D</code></td>
          <td>Pan right</td>
      </tr>
      <tr>
          <td><code>M</code></td>
          <td>Mark selected range (measure time delta)</td>
      </tr>
      <tr>
          <td><code>F</code></td>
          <td>Focus / zoom to fit selected slice</td>
      </tr>
      <tr>
          <td><code>?</code></td>
          <td>Show all keyboard shortcuts</td>
      </tr>
  </tbody>
</table>
<pre tabindex="0"><code>+--------------------------------------------------------------------------------+
|  Time: 0s               2s               4s               6s               8s  |
+--------------------------------------------------------------------------------+
| CPU 0-7:  [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||]  |
|                                                                                |
| Process: com.example.myapp (PID: 14203)                                        |
|   ├─ Main Thread (14203)                                                       |
|   │    [Choreographer#doFrame] -&gt; [inflate] -&gt; [bindViewHolder]                |
|   ├─ RenderThread (14220)                                                      |
|   │    [DrawFrame] -&gt; [syncFrameState] -&gt; [eglSwapBuffers]                     |
|   └─ DefaultDispatcher-worker-1                                                |
|        [FeedRepository:loadFeedData]                                           |
+--------------------------------------------------------------------------------+
</code></pre><h3 id="key-tracks-to-inspect">Key Tracks to Inspect</h3>
<ol>
<li><strong>CPU Usage &amp; Scheduling Tracks (<code>sched</code>)</strong>:
<ul>
<li>Each thread bar has colored blocks representing thread states:
<ul>
<li><strong>Green (Running)</strong>: The thread is actively executing on a CPU core.</li>
<li><strong>Blue (Runnable)</strong>: The thread is ready to run, but waiting for an available CPU core (<strong>CPU contention</strong> or CPU starvation!).</li>
<li><strong>Red/Orange (Sleeping/Blocked)</strong>: The thread is waiting on an I/O operation, a lock, a mutex, or <code>Thread.sleep()</code>.</li>
</ul>
</li>
</ul>
</li>
<li><strong>App Main Thread</strong>:
<ul>
<li>Shows your app&rsquo;s UI message loop. Look for <code>Choreographer#doFrame</code> slices.</li>
<li>On a 60Hz display, a frame slice must complete in under <strong>16.6ms</strong>; on 120Hz, under <strong>8.3ms</strong>. If <code>doFrame</code> exceeds this, you have dropped frames (jank).</li>
</ul>
</li>
<li><strong>RenderThread</strong>:
<ul>
<li>Handles the hardware acceleration commands sent from the main thread and syncs with the GPU via OpenGL/Vulkan (<code>eglSwapBuffersWithDamageKHR</code>).</li>
</ul>
</li>
<li><strong>Android Frame Timeline (Jank Detection Track)</strong>:
<ul>
<li>Colored markers indicate whether each frame was:
<ul>
<li><strong>Green</strong>: On-time.</li>
<li><strong>Yellow/Red</strong>: Delayed / Dropped frame with the root cause (e.g. <code>App Deadline Missed</code> or <code>Display HAL Deadline Missed</code>).</li>
</ul>
</li>
</ul>
</li>
</ol>
<hr>
<h2 id="5-what-not-advanced-power-features">5. What Not: Advanced Power Features</h2>
<h3 id="querying-traces-with-sql-trace-processor">Querying Traces with SQL (Trace Processor)</h3>
<p>One of Perfetto&rsquo;s greatest strengths is the <strong>Trace Processor</strong>. Instead of manually panning around a massive trace looking for spikes, you can write SQL queries directly in the UI query console (or in a Python script).</p>
<p>Click the <strong>Query (SQL)</strong> tab in the left sidebar of the Perfetto UI:</p>
<h4 id="1-find-the-10-longest-slices-in-your-app">1. Find the 10 longest slices in your app:</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">SELECT</span> 
</span></span><span style="display:flex;"><span>    name, 
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">CAST</span>(dur <span style="color:#f92672">/</span> <span style="color:#ae81ff">1</span>e6 <span style="color:#66d9ef">AS</span> INT) <span style="color:#66d9ef">AS</span> duration_ms,
</span></span><span style="display:flex;"><span>    track.name <span style="color:#66d9ef">AS</span> thread_name
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">FROM</span> slice
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">JOIN</span> track <span style="color:#66d9ef">ON</span> slice.track_id <span style="color:#f92672">=</span> track.id
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">WHERE</span> dur <span style="color:#f92672">&gt;</span> <span style="color:#ae81ff">10000000</span> <span style="color:#75715e">-- greater than 10ms
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">ORDER</span> <span style="color:#66d9ef">BY</span> dur <span style="color:#66d9ef">DESC</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">LIMIT</span> <span style="color:#ae81ff">10</span>;
</span></span></code></pre></div><h4 id="2-calculate-time-spent-in-runnable-state-cpu-starvation">2. Calculate time spent in <code>Runnable</code> state (CPU Starvation):</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">SELECT</span> 
</span></span><span style="display:flex;"><span>    thread.name,
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">SUM</span>(dur) <span style="color:#f92672">/</span> <span style="color:#ae81ff">1</span>e6 <span style="color:#66d9ef">AS</span> total_runnable_delay_ms
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">FROM</span> thread_state
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">JOIN</span> thread <span style="color:#66d9ef">USING</span> (utid)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">WHERE</span> <span style="color:#66d9ef">state</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;R&#39;</span> <span style="color:#75715e">-- Runnable (waiting for CPU)
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">GROUP</span> <span style="color:#66d9ef">BY</span> thread.name
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">ORDER</span> <span style="color:#66d9ef">BY</span> total_runnable_delay_ms <span style="color:#66d9ef">DESC</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">LIMIT</span> <span style="color:#ae81ff">5</span>;
</span></span></code></pre></div><h4 id="3-analyze-lock-contention">3. Analyze Lock Contention:</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">SELECT</span> 
</span></span><span style="display:flex;"><span>    name,
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">COUNT</span>(<span style="color:#f92672">*</span>) <span style="color:#66d9ef">AS</span> contention_count,
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">SUM</span>(dur) <span style="color:#f92672">/</span> <span style="color:#ae81ff">1</span>e6 <span style="color:#66d9ef">AS</span> total_wait_time_ms
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">FROM</span> slice
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">WHERE</span> name <span style="color:#66d9ef">LIKE</span> <span style="color:#e6db74">&#39;monitor contention%&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">GROUP</span> <span style="color:#66d9ef">BY</span> name
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">ORDER</span> <span style="color:#66d9ef">BY</span> total_wait_time_ms <span style="color:#66d9ef">DESC</span>;
</span></span></code></pre></div><hr>
<h3 id="memory-profiling-with-heapprofd">Memory Profiling with <code>heapprofd</code></h3>
<p>Perfetto includes a native heap profiler called <strong><code>heapprofd</code></strong> that samples allocations with zero code modifications:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># Capture native memory allocations of your app</span>
</span></span><span style="display:flex;"><span>./record_android_trace <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -o heap_trace.perfetto-trace <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -t 20s <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -b 128mb <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  heapprofd <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -a com.example.myapp
</span></span></code></pre></div><p>When opened in <a href="https://ui.perfetto.dev">ui.perfetto.dev</a>, it displays flame graphs and allocation breakdown trees showing exact callstacks that allocated native memory.</p>
<hr>
<h2 id="common-pitfalls--best-practices">Common Pitfalls &amp; Best Practices</h2>
<table>
  <thead>
      <tr>
          <th>Do&rsquo;s</th>
          <th>Don&rsquo;ts</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Use <code>&lt;profileable android:shell=&quot;true&quot; /&gt;</code> on Release builds.</td>
          <td>Never run benchmarks or record traces on <code>debuggable=true</code> builds.</td>
      </tr>
      <tr>
          <td>Use specific categories to keep buffer usage manageable.</td>
          <td>Don&rsquo;t enable every single data source unless you&rsquo;re diagnosing kernel-level bugs.</td>
      </tr>
      <tr>
          <td>Use <code>trace(&quot;SectionName&quot;) { ... }</code> around suspect blocks.</td>
          <td>Don&rsquo;t put trace sections inside micro-loops (e.g. iterating 100,000 items).</td>
      </tr>
      <tr>
          <td>Look at thread states (<code>Runnable</code> vs <code>Sleeping</code>) to understand <em>why</em> code was slow.</td>
          <td>Don&rsquo;t assume slow function execution is always bad code; it could be CPU frequency throttling or lock contention.</td>
      </tr>
  </tbody>
</table>
<hr>
<h2 id="summary">Summary</h2>
<p>Perfetto is the gold standard for performance engineering on Android and Linux systems. Whether you are hunting down a 100ms frame drop during a Compose scroll gesture, profiling memory leaks with <code>heapprofd</code>, or automating performance regressions in CI using SQL metrics, Perfetto provides unprecedented observability.</p>
<h3 id="useful-links--further-reading">Useful Links &amp; Further Reading</h3>
<ul>
<li><a href="https://github.com/google/perfetto">Official Perfetto GitHub Repository</a></li>
<li><a href="https://perfetto.dev/docs/">Perfetto Documentation &amp; Reference Guides</a></li>
<li><a href="https://ui.perfetto.dev">Perfetto Web UI Visualizer</a></li>
<li><a href="https://developer.android.com/topic/performance/tracing">Android Developers Guide on System Tracing</a></li>
<li><a href="https://developer.android.com/topic/performance/tracing/custom-events">Jetpack Tracing Guide</a></li>
</ul>
<hr>
<p><em>Found this guide helpful? Have questions or favorite Perfetto SQL queries? Feel free to reach out via my social handles!</em></p>
]]></content:encoded></item></channel></rss>