feat(profiling): Add Perfetto trace format support#5659
feat(profiling): Add Perfetto trace format support#5659
Conversation
Add support for ingesting binary Perfetto traces (.pftrace) as profile chunks. The SDK sends an envelope with a ProfileChunk metadata item paired with a ProfileChunkData item containing the raw Perfetto protobuf. Relay decodes the Perfetto trace, extracts CPU profiling samples (PerfSample and StreamingProfilePacket), converts them to the internal Sample v2 format, and forwards both the expanded JSON and the original binary blob to Kafka for downstream processing. Key changes: - New `perfetto` module in relay-profiling for protobuf decoding and conversion to Sample v2 - New `ProfileChunkData` envelope item type for binary profile payloads - Pairing logic to associate ProfileChunk metadata with ProfileChunkData - Raw profile blob preserved through to Kafka for further processing Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tto-profiling-support
# Conflicts: # relay-server/src/envelope/item.rs
|
@sentry review |
…fer main thread Separate the shared `strings` intern table into distinct `function_names`, `mapping_paths`, and `build_ids` tables matching the Perfetto spec where each InternedData field has its own ID namespace. Also infer the main thread name when tid equals pid and no explicit name is provided. Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: First timestamp delta skipped in StreamingProfilePacket processing
- Removed the 'i > 0' guard so that timestamp_delta_us[0] is now correctly applied to the first sample's timestamp.
Or push these changes by commenting:
@cursor push ab5f3ec796
Preview (ab5f3ec796)
diff --git a/relay-profiling/src/perfetto/mod.rs b/relay-profiling/src/perfetto/mod.rs
--- a/relay-profiling/src/perfetto/mod.rs
+++ b/relay-profiling/src/perfetto/mod.rs
@@ -229,9 +229,7 @@
Some(Data::StreamingProfilePacket(spp)) => {
let mut ts = packet.timestamp.unwrap_or(0);
for (i, &cs_iid) in spp.callstack_iid.iter().enumerate() {
- if i > 0
- && let Some(&delta) = spp.timestamp_delta_us.get(i)
- {
+ if let Some(&delta) = spp.timestamp_delta_us.get(i) {
// `delta` is i64 (can be negative for out-of-order samples).
// Casting to u64 wraps negative values, which is correct because
// `wrapping_add` of a wrapped negative value subtracts as expected.This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.
Replace `get().is_none()` with `!contains_key()` to satisfy clippy and add a CHANGELOG entry for the Perfetto interning changes. Co-Authored-By: Claude <noreply@anthropic.com>
The first delta in timestamp_delta_us was skipped due to an i > 0 guard, but per the Perfetto spec the first sample's timestamp should be TracePacket.timestamp + timestamp_delta_us[0]. Update tests to use non-zero first deltas to verify the fix. Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
| // `wrapping_add` of a wrapped negative value subtracts as expected. | ||
| ts = ts.wrapping_add((delta * 1000) as u64); | ||
| } | ||
| raw_samples.push((ts, 0, cs_iid, seq_id)); |
There was a problem hiding this comment.
Streaming profile samples all assigned thread ID zero
Medium Severity
All StreamingProfilePacket samples are pushed with tid = 0, losing the actual thread identity. Perfetto associates each StreamingProfilePacket with a thread via the trusted_packet_sequence_id → TrackDescriptor → ThreadDescriptor chain, but this code never resolves that mapping. Every streaming sample ends up with thread_id: "0", which collapses multi-thread profiles into one thread and produces incorrect output for the profiling UI.



This PR adds the ability to ingest binary Perfetto traces (.pftrace) and convert them into the existing Sample v2 JSON format used by Sentry's profiling pipeline. This targets Android
profiling, where Perfetto is the native tracing format.
Key Changes
organizations:continuous-profiling-perfetto— gates all Perfetto processing.meta_length— profile chunk items can now carry[JSON metadata][binary blob], split atmeta_length. This is how Perfetto traces arrive: metadata describes theprofile, the binary blob is the raw .pftrace.
relay-profiling/src/perfetto/module (proto definitions + conversion logic) parses binary Perfetto traces and produces the existing Sample v2JSON format.
raw_profileandraw_profile_content_type.- Frame.package — library/container path for native/Java frames
- ProfileMetadata.content_type — carries "perfetto" through the pipeline
- DebugImage::native_image() constructor — creates debug images from Perfetto mapping data