|
| 1 | +--- |
| 2 | +name: btrace-perfetto |
| 3 | +description: Capture and compare Perfetto traces using btrace 3.0 on an Android device. Use when asked to "profile", "capture trace", "perfetto trace", "btrace", "compare traces", "record perfetto", "trace touch events", "measure performance on device", or benchmark Android SDK changes between branches. |
| 4 | +allowed-tools: Bash, Read, Write, Edit, Glob, Grep, WebFetch, AskUserQuestion |
| 5 | +argument-hint: "[branch1] [branch2] [duration] [sql-query]" |
| 6 | +--- |
| 7 | + |
| 8 | +# btrace Perfetto Trace Capture |
| 9 | + |
| 10 | +Capture Perfetto traces with btrace 3.0 on a connected Android device, optionally comparing two branches. Opens results in Perfetto UI with a prefilled SQL query. |
| 11 | + |
| 12 | +## Prerequisites |
| 13 | + |
| 14 | +Before starting, verify: |
| 15 | + |
| 16 | +1. **Connected device**: `adb devices` shows a device (Android 8.0+, 64-bit) |
| 17 | +2. **btrace CLI jar**: Check if `tools/btrace/rhea-trace-shell.jar` exists. If not, download it: |
| 18 | + ```bash |
| 19 | + mkdir -p tools/btrace/traces |
| 20 | + curl -sL "https://repo1.maven.org/maven2/com/bytedance/btrace/rhea-trace-processor/3.0.0/rhea-trace-processor-3.0.0.jar" \ |
| 21 | + -o tools/btrace/rhea-trace-shell.jar |
| 22 | + ``` |
| 23 | +3. **Device ABI**: Run `adb shell getprop ro.product.cpu.abi` — btrace only supports arm64-v8a and armeabi-v7a (no x86/x86_64) |
| 24 | + |
| 25 | +## Step 1: Parse Arguments |
| 26 | + |
| 27 | +| Argument | Default | Description | |
| 28 | +|----------|---------|-------------| |
| 29 | +| branch1 | current branch | First branch to trace | |
| 30 | +| branch2 | `main` | Second branch to compare against | |
| 31 | +| duration | `20` | Trace duration in seconds | |
| 32 | +| sql-query | see below | SQL query to prefill in Perfetto UI | |
| 33 | + |
| 34 | +If no arguments are provided, ask the user what they want to trace and which branches to compare. If only one branch is given, capture only that branch (no comparison). |
| 35 | + |
| 36 | +## Step 2: Integrate btrace into Sample App |
| 37 | + |
| 38 | +The sample app is at `sentry-samples/sentry-samples-android/`. |
| 39 | + |
| 40 | +### 2a: Add btrace dependency |
| 41 | + |
| 42 | +In `sentry-samples/sentry-samples-android/build.gradle.kts`, add to the `dependencies` block: |
| 43 | + |
| 44 | +```kotlin |
| 45 | +implementation("com.bytedance.btrace:rhea-inhouse:3.0.0") |
| 46 | +``` |
| 47 | + |
| 48 | +### 2b: Restrict ABI to device architecture |
| 49 | + |
| 50 | +The btrace native library (shadowhook) does not support x86/x86_64. Replace the `ndk` abiFilters line in `defaultConfig` to match the connected device: |
| 51 | + |
| 52 | +```kotlin |
| 53 | +ndk { abiFilters.addAll(listOf("arm64-v8a")) } |
| 54 | +``` |
| 55 | + |
| 56 | +Adjust if the device reports a different ABI. |
| 57 | + |
| 58 | +### 2c: Initialize btrace in Application |
| 59 | + |
| 60 | +In `MyApplication.java`, add `attachBaseContext`: |
| 61 | + |
| 62 | +```java |
| 63 | +import android.content.Context; |
| 64 | +import com.bytedance.rheatrace.RheaTrace3; |
| 65 | + |
| 66 | +// Add before onCreate: |
| 67 | +@Override |
| 68 | +protected void attachBaseContext(Context base) { |
| 69 | + super.attachBaseContext(base); |
| 70 | + RheaTrace3.init(base); |
| 71 | +} |
| 72 | +``` |
| 73 | + |
| 74 | +**Important**: The package is `com.bytedance.rheatrace`, not `com.bytedance.btrace`. |
| 75 | + |
| 76 | +## Step 3: Build and Install |
| 77 | + |
| 78 | +```bash |
| 79 | +./gradlew :sentry-samples:sentry-samples-android:installDebug |
| 80 | +``` |
| 81 | + |
| 82 | +## Step 4: Capture Trace |
| 83 | + |
| 84 | +For each branch to trace: |
| 85 | + |
| 86 | +### 4a: Set btrace properties and launch app |
| 87 | + |
| 88 | +```bash |
| 89 | +adb shell setprop debug.rhea3.startWhenAppLaunch 1 |
| 90 | +adb shell setprop debug.rhea3.waitTraceTimeout 60 |
| 91 | +adb shell am force-stop io.sentry.samples.android |
| 92 | +sleep 1 |
| 93 | +adb shell am start -n io.sentry.samples.android/.MainActivity |
| 94 | +``` |
| 95 | + |
| 96 | +The app must be started AFTER `debug.rhea3.startWhenAppLaunch` is set, otherwise the trace server won't initialize. |
| 97 | + |
| 98 | +### 4b: Tell the user to interact with the app, then capture |
| 99 | + |
| 100 | +```bash |
| 101 | +java -jar tools/btrace/rhea-trace-shell.jar \ |
| 102 | + -a io.sentry.samples.android \ |
| 103 | + -t ${duration} \ |
| 104 | + -waitTraceTimeout 60 \ |
| 105 | + -o tools/btrace/traces/${branch_name}.pb \ |
| 106 | + sched |
| 107 | +``` |
| 108 | + |
| 109 | +Do NOT use the `-r` flag — it fails to resolve the launcher activity because LeakCanary registers a second one. Launch the app manually in step 4a instead. |
| 110 | + |
| 111 | +### 4c: Switch branches for comparison |
| 112 | + |
| 113 | +When capturing a second branch: |
| 114 | + |
| 115 | +1. Stash the btrace integration changes: |
| 116 | + ```bash |
| 117 | + git stash push -m "btrace integration" -- \ |
| 118 | + sentry-samples/sentry-samples-android/build.gradle.kts \ |
| 119 | + sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/MyApplication.java |
| 120 | + ``` |
| 121 | +2. Checkout the other branch |
| 122 | +3. Pop the stash: `git stash pop` |
| 123 | +4. Rebuild and install: `./gradlew :sentry-samples:sentry-samples-android:installDebug` |
| 124 | +5. Repeat steps 4a and 4b with a different output filename |
| 125 | +6. Switch back to the original branch and restore files |
| 126 | + |
| 127 | +## Step 5: Open in Perfetto UI |
| 128 | + |
| 129 | +Generate a viewer HTML and serve it locally. Use the template at `assets/viewer-template.html` as a base — copy it to `tools/btrace/traces/viewer.html` and replace the placeholder values: |
| 130 | + |
| 131 | +- `TRACE_FILES`: array of `{file, title}` objects for each captured trace |
| 132 | +- `SQL_QUERY`: the SQL query to prefill |
| 133 | + |
| 134 | +The SQL query is passed via the URL hash parameter: `https://ui.perfetto.dev/#!/?query=...` |
| 135 | + |
| 136 | +The trace data is sent via the postMessage API (required for local files — URL deep-linking does not work with `file://`). |
| 137 | + |
| 138 | +Start a local HTTP server and open the viewer: |
| 139 | + |
| 140 | +```bash |
| 141 | +cd tools/btrace/traces && python3 -m http.server 8008 & |
| 142 | +open http://localhost:8008/viewer.html |
| 143 | +``` |
| 144 | + |
| 145 | +### Default SQL Query |
| 146 | + |
| 147 | +If no custom query is provided, use: |
| 148 | + |
| 149 | +```sql |
| 150 | +SELECT |
| 151 | + s.name AS slice_name, |
| 152 | + s.dur / 1e6 AS dur_ms, |
| 153 | + s.ts, |
| 154 | + t.name AS track_name |
| 155 | +FROM slice s |
| 156 | +JOIN thread_track t ON s.track_id = t.id |
| 157 | +WHERE s.name GLOB '*SentryWindowCallback.dispatch*' |
| 158 | +ORDER BY s.ts |
| 159 | +``` |
| 160 | + |
| 161 | +## Cleanup |
| 162 | + |
| 163 | +After tracing is complete, remind the user that the btrace integration changes to the sample app should NOT be committed. The `tools/btrace/` directory is gitignored. |
| 164 | + |
| 165 | +## Troubleshooting |
| 166 | + |
| 167 | +| Problem | Solution | |
| 168 | +|---------|----------| |
| 169 | +| `No compatible library found [shadowhook]` | Restrict `ndk.abiFilters` to arm64-v8a only | |
| 170 | +| `package com.bytedance.btrace does not exist` | Use `com.bytedance.rheatrace` (not `btrace`) | |
| 171 | +| `ResolverActivity does not exist` with `-r` flag | Don't use `-r`; launch the app manually before capturing | |
| 172 | +| `wait for trace ready timeout` on download | Set `debug.rhea3.startWhenAppLaunch=1` BEFORE launching the app, and use `-waitTraceTimeout 60` | |
| 173 | +| Empty jar file (0 bytes) | Download from Maven Central (`repo1.maven.org`), not `oss.sonatype.org` | |
| 174 | +| `FileNotFoundException` on sampling download | App was already running when properties were set; force-stop and relaunch | |
0 commit comments