From 6509ceb6989edf96c9992e9399612630e252a9c9 Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Thu, 15 Oct 2026 14:20:57 +0200 Subject: [PATCH] chore(android): [DO NOT MERGE] Strip Android 17 trace header keys unknown to Relay Reference experiment for a Relay issue. Android 17 (API 37) ART method traces add header lines (is_streaming, is_precise_trace, end_time, end_tsc, compilation-filter) that Relay's android_trace_log 0.3.0 parser rejects, so Android 17 transaction profiles are dropped as profiling_invalid_sampled_profile. Removing only those lines before upload makes the profiles ingest. Co-Authored-By: Claude Opus 5.5 --- .../io/sentry/react/RNSentryModuleImpl.java | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/packages/core/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java b/packages/core/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java index d100f388f1..e55932f018 100644 --- a/packages/core/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java +++ b/packages/core/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java @@ -1072,6 +1072,8 @@ public WritableMap stopProfiling() { WritableMap androidProfile = new WritableNativeMap(); byte[] androidProfileBytes = FileUtils.readBytesFromFile(end.traceFile.getPath(), maxTraceFileSize); + // TEMP EXPERIMENT (do not commit): strip Android 17 trace header keys unknown to Relay. + androidProfileBytes = tempStripUnknownTraceHeaderLines(androidProfileBytes); String base64AndroidProfile = Base64.encodeToString(androidProfileBytes, NO_WRAP | NO_PADDING); @@ -1220,6 +1222,77 @@ List stopPerformanceCollection() { } } + // TEMP EXPERIMENT (do not commit): header keys android_trace_log 0.3.0 (used by Relay) accepts. + private static final java.util.Set TEMP_KNOWN_TRACE_HEADER_KEYS = + new java.util.HashSet<>( + java.util.Arrays.asList( + "data-file-overflow", + "clock", + "elapsed-time-usec", + "num-method-calls", + "clock-call-overhead-nsec", + "vm", + "pid", + "alloc-count", + "alloc-size", + "gc-count")); + + // TEMP EXPERIMENT (do not commit): drops `key=value` lines with unknown keys from the + // `*version` section. Returns the input unchanged when there is nothing to drop. + private byte[] tempStripUnknownTraceHeaderLines(final byte[] trace) { + final byte[] magic = "*version\n".getBytes(UTF_8); + if (trace.length < magic.length) { + return trace; + } + for (int i = 0; i < magic.length; i++) { + if (trace[i] != magic[i]) { + return trace; + } + } + final ByteArrayOutputStream out = new ByteArrayOutputStream(trace.length); + int pos = magic.length; + int nl = indexOf(trace, (byte) '\n', pos); // version number line + if (nl < 0) { + return trace; + } + out.write(trace, 0, nl + 1); + pos = nl + 1; + final List dropped = new ArrayList<>(); + while (true) { + nl = indexOf(trace, (byte) '\n', pos); + if (nl < 0) { + return trace; + } + if (trace[pos] == '*') { + out.write(trace, pos, trace.length - pos); + break; + } + final String line = new String(trace, pos, nl - pos, UTF_8); + final int eq = line.indexOf('='); + final String key = eq < 0 ? line : line.substring(0, eq); + if (TEMP_KNOWN_TRACE_HEADER_KEYS.contains(key)) { + out.write(trace, pos, nl - pos + 1); + } else { + dropped.add(line); + } + pos = nl + 1; + } + if (dropped.isEmpty()) { + return trace; + } + logger.log(SentryLevel.INFO, "TEMP stripped trace header lines: " + dropped); + return out.toByteArray(); + } + + private static int indexOf(final byte[] bytes, final byte b, final int from) { + for (int i = from; i < bytes.length; i++) { + if (bytes[i] == b) { + return i; + } + } + return -1; + } + private @Nullable String getProguardUuid() { if (isProguardDebugMetaLoaded) { return proguardUuid;