Skip to content

Commit e2f3e35

Browse files
romtsnclaude
andcommitted
chore: Update btrace-perfetto skill with debug builds and trace comparison
Prefer debug builds for richer tracing instrumentation (Handler, MessageQueue, Lock slices). Add trace_processor prerequisite for local querying. Add Step 6 with SQL queries and comparison table generation. Include sampling rate reference and additional troubleshooting entries. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 9d1f94f commit e2f3e35

1 file changed

Lines changed: 102 additions & 20 deletions

File tree

.claude/skills/btrace-perfetto/SKILL.md

Lines changed: 102 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ argument-hint: "[branch1] [branch2] [duration] [sql-query]"
77

88
# btrace Perfetto Trace Capture
99

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.
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. After capture, query traces locally with `trace_processor` to compute comparison stats.
1111

1212
## Prerequisites
1313

@@ -20,7 +20,11 @@ Before starting, verify:
2020
curl -sL "https://repo1.maven.org/maven2/com/bytedance/btrace/rhea-trace-processor/3.0.0/rhea-trace-processor-3.0.0.jar" \
2121
-o tools/btrace/rhea-trace-shell.jar
2222
```
23-
3. **Device ABI**: Run `adb shell getprop ro.product.cpu.abi` — btrace only supports arm64-v8a and armeabi-v7a (no x86/x86_64)
23+
3. **Perfetto trace_processor**: Check if `/tmp/trace_processor` exists. If not, download it:
24+
```bash
25+
curl -sL "https://get.perfetto.dev/trace_processor" -o /tmp/trace_processor && chmod +x /tmp/trace_processor
26+
```
27+
4. **Device ABI**: Run `adb shell getprop ro.product.cpu.abi` — btrace only supports arm64-v8a and armeabi-v7a (no x86/x86_64)
2428

2529
## Step 1: Parse Arguments
2630

@@ -73,9 +77,9 @@ protected void attachBaseContext(Context base) {
7377

7478
**Important**: The package is `com.bytedance.rheatrace`, not `com.bytedance.btrace`.
7579

76-
### 2d: Add ProGuard keep rule for release builds
80+
### 2d: Add ProGuard keep rules (release builds only)
7781

78-
In `sentry-samples/sentry-samples-android/proguard-rules.pro`, add:
82+
Only needed when building release. In `sentry-samples/sentry-samples-android/proguard-rules.pro`, add:
7983

8084
```
8185
-keep class com.bytedance.rheatrace.** { *; }
@@ -86,7 +90,13 @@ The first rule prevents R8 from stripping btrace's HTTP server classes (fails wi
8690

8791
## Step 3: Build and Install
8892

89-
Always use release builds for tracing — debug builds have StrictMode, debuggable overhead, and no R8 optimizations, which skew results.
93+
Prefer **debug builds** — they provide richer tracing instrumentation (Handler, MessageQueue, Monitor:Lock slices visible) which is essential for comparing internal SDK behavior. Use the default 1kHz btrace sampling rate for debug builds.
94+
95+
```bash
96+
./gradlew :sentry-samples:sentry-samples-android:installDebug
97+
```
98+
99+
**Release builds** are useful when you need to measure real-world performance without StrictMode/debuggable overhead or with R8 optimizations. Require the ProGuard keep rules from step 2d. Use `-sampleInterval 333000` (333μs / 3kHz) for finer granularity since release code runs faster.
90100

91101
```bash
92102
./gradlew :sentry-samples:sentry-samples-android:installRelease
@@ -98,40 +108,40 @@ For each branch to trace:
98108

99109
### 4a: Set btrace properties and launch app
100110

111+
Clear any stale port files, set properties, and launch:
112+
101113
```bash
114+
adb shell "rm -rf /storage/emulated/0/Android/data/io.sentry.samples.android/files/rhea-port"
102115
adb shell setprop debug.rhea3.startWhenAppLaunch 1
103116
adb shell setprop debug.rhea3.waitTraceTimeout 60
104117
adb shell am force-stop io.sentry.samples.android
105-
sleep 1
118+
sleep 2
106119
adb shell am start -n io.sentry.samples.android/.MainActivity
120+
sleep 5
107121
```
108122

109-
The app must be started AFTER `debug.rhea3.startWhenAppLaunch` is set, otherwise the trace server won't initialize.
123+
The app must be started AFTER `debug.rhea3.startWhenAppLaunch` is set, otherwise the trace server won't initialize. The 5s sleep after launch gives the btrace HTTP server time to start.
110124

111125
### 4b: Play a sound to signal the user, then capture
112126

113127
Play a sound when tracing actually starts so the user knows to begin interacting. Pipe btrace output through a loop that triggers the sound on the "start tracing" line:
114128

115-
```bash
116-
java -jar tools/btrace/rhea-trace-shell.jar ... 2>&1 | while IFS= read -r line; do
117-
echo "$line"
118-
if [[ "$line" == *"start tracing"* ]]; then
119-
afplay -v 1.5 /System/Library/Sounds/Ping.aiff &
120-
fi
121-
done
122-
```
123-
124-
This ensures the sound is synchronized with the actual trace start, not an estimated delay.
125-
126129
```bash
127130
java -jar tools/btrace/rhea-trace-shell.jar \
128131
-a io.sentry.samples.android \
129132
-t ${duration} \
130133
-waitTraceTimeout 60 \
131134
-o tools/btrace/traces/${branch_name}.pb \
132-
sched
135+
sched 2>&1 | while IFS= read -r line; do
136+
echo "$line"
137+
if [[ "$line" == *"start tracing"* ]]; then
138+
afplay -v 1.5 /System/Library/Sounds/Ping.aiff &
139+
fi
140+
done
133141
```
134142

143+
For release builds with finer sampling, add `-sampleInterval 333000`.
144+
135145
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.
136146

137147
### 4c: Switch branches for comparison
@@ -147,7 +157,7 @@ When capturing a second branch:
147157
```
148158
2. Checkout the other branch
149159
3. Pop the stash: `git stash pop`
150-
4. Rebuild and install: `./gradlew :sentry-samples:sentry-samples-android:installRelease`
160+
4. Rebuild and install (same variant — debug or release — as the first branch)
151161
5. Repeat steps 4a and 4b with a different output filename
152162
6. Switch back to the original branch and restore files
153163

@@ -185,6 +195,76 @@ WHERE s.name GLOB '*SentryWindowCallback.dispatch*'
185195
ORDER BY s.ts
186196
```
187197

198+
## Step 6: Query and Compare Traces
199+
200+
After capturing both branches, use `trace_processor` to compute comparison stats locally.
201+
202+
### Basic stats query
203+
204+
For each trace file, run:
205+
206+
```bash
207+
/tmp/trace_processor -Q "
208+
WITH events AS (
209+
SELECT s.dur / 1e6 as dur_ms FROM slice s
210+
WHERE s.name GLOB '*${METHOD_GLOB}*' AND s.dur > 0
211+
ORDER BY s.dur
212+
)
213+
SELECT COUNT(*) as count,
214+
ROUND(AVG(dur_ms), 4) as avg_ms,
215+
ROUND((SELECT dur_ms FROM events LIMIT 1 OFFSET (SELECT COUNT(*)/2 FROM events)), 4) as median_ms,
216+
ROUND(MIN(dur_ms), 4) as min_ms,
217+
ROUND(MAX(dur_ms), 4) as max_ms
218+
FROM events
219+
" tools/btrace/traces/${trace_file}.pb
220+
```
221+
222+
Replace `${METHOD_GLOB}` with the method pattern to compare (e.g. `SentryGestureDetector.onTouchEvent`, `SentryWindowCallback.dispatchTouchEvent`).
223+
224+
### Finding child calls (debug builds)
225+
226+
To find what happens inside a method (e.g. Handler calls, lock acquisitions):
227+
228+
```bash
229+
/tmp/trace_processor -Q "
230+
WITH RECURSIVE descendants(id, depth) AS (
231+
SELECT s.id, 0 FROM slice s WHERE s.name GLOB '*${PARENT_METHOD}*'
232+
UNION ALL
233+
SELECT s.id, d.depth + 1 FROM slice s JOIN descendants d ON s.parent_id = d.id WHERE d.depth < 10
234+
)
235+
SELECT s.name, COUNT(*) as count, ROUND(AVG(s.dur / 1e6), 3) as avg_ms
236+
FROM slice s JOIN descendants d ON s.id = d.id
237+
WHERE d.depth > 0
238+
GROUP BY s.name ORDER BY count DESC
239+
LIMIT 20
240+
" tools/btrace/traces/${trace_file}.pb
241+
```
242+
243+
### Build the comparison table
244+
245+
Run the stats query on both trace files, then present a markdown table:
246+
247+
```
248+
| Metric | Branch A | Branch B | Delta |
249+
|--------|----------|----------|-------|
250+
| Count | ... | ... | |
251+
| Average| ... | ... | -X% |
252+
| Median | ... | ... | -X% |
253+
| Max | ... | ... | -X% |
254+
```
255+
256+
Compute delta as `(branchA - branchB) / branchB * 100`. Negative means branch A is faster.
257+
258+
### Sampling rate reference
259+
260+
| Rate | Interval | `-sampleInterval` | Use case |
261+
|------|----------|-------------------|----------|
262+
| 1 kHz | 1ms | `1000000` (default) | Debug builds, general profiling |
263+
| 3 kHz | 333μs | `333000` | Release builds, finer granularity |
264+
| 10 kHz | 100μs | `100000` | Maximum detail, higher overhead |
265+
266+
Higher sampling rates capture shorter method calls but add CPU overhead which can skew results. For most comparisons, the default 1kHz is sufficient.
267+
188268
## Cleanup
189269

190270
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.
@@ -200,3 +280,5 @@ After tracing is complete, remind the user that the btrace integration changes t
200280
| Empty jar file (0 bytes) | Download from Maven Central (`repo1.maven.org`), not `oss.sonatype.org` |
201281
| `FileNotFoundException` on sampling download | App was already running when properties were set; force-stop and relaunch |
202282
| `SocketException: Unexpected end of file` in release builds | R8 stripped btrace classes; add `-keep class com.bytedance.rheatrace.** { *; }` to proguard-rules.pro |
283+
| Stale port from previous session | Run `adb shell "rm -rf /storage/emulated/0/Android/data/io.sentry.samples.android/files/rhea-port"` before launching |
284+
| Most `onTouchEvent` durations are 0ms | Increase sampling rate with `-sampleInterval 333000` (3kHz) |

0 commit comments

Comments
 (0)