Skip to content

Commit b3c71ce

Browse files
committed
fix(profiling): Fix meta_length not serialized in Perfetto envelope header
SentryEnvelopeItemHeader.serialize() checked the raw metaLength field instead of calling getMetaLength(), so the callable path used by Perfetto profile chunks was never invoked and meta_length was never written to the envelope header JSON. Refactor SentryEnvelopeItemHeader to remove the metaLength field entirely — all constructors now store a single calculateMetaLength callable. Eager constructors (deserializer) wrap the Integer in a lambda. All constructors delegate to one private primary constructor. In fromPerfettoProfileChunk, replace the round-trip through ProfileChunk.setMetaLength/getMetaLength with a local AtomicInteger shared between the CachedItem lambda and the header callable, keeping meta_length as an envelope transport concern rather than in ProfileChunk
1 parent c2a4442 commit b3c71ce

File tree

3 files changed

+62
-73
lines changed

3 files changed

+62
-73
lines changed

sentry/src/main/java/io/sentry/ProfileChunk.java

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -138,20 +138,6 @@ public double getTimestamp() {
138138
return contentType;
139139
}
140140

141-
/**
142-
* The length of the JSON metadata prefix in the binary envelope payload. Set during lazy
143-
* serialization of Perfetto profile chunks so the envelope item header can read it.
144-
*/
145-
private transient int metaLength = 0;
146-
147-
public int getMetaLength() {
148-
return metaLength;
149-
}
150-
151-
public void setMetaLength(final int metaLength) {
152-
this.metaLength = metaLength;
153-
}
154-
155141
public @Nullable SentryProfile getSentryProfile() {
156142
return sentryProfile;
157143
}

sentry/src/main/java/io/sentry/SentryEnvelopeItem.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.util.LinkedHashMap;
2929
import java.util.Map;
3030
import java.util.concurrent.Callable;
31+
import java.util.concurrent.atomic.AtomicInteger;
3132
import org.jetbrains.annotations.ApiStatus;
3233
import org.jetbrains.annotations.NotNull;
3334
import org.jetbrains.annotations.Nullable;
@@ -385,6 +386,12 @@ private static void ensureAttachmentSizeLimit(
385386
traceFile != null ? traceFile.getName() : "null"));
386387
}
387388

389+
// Perfetto profile chunks are serialized as [JSON metadata][raw .pftrace bytes] with no
390+
// delimiter. The server needs meta_length in the envelope header to know where the JSON
391+
// ends and the binary begins. meta_length is not known until the CachedItem payload lambda
392+
// runs, so we track it here as an envelope serialization concern rather than on ProfileChunk.
393+
final AtomicInteger metaLength = new AtomicInteger(-1);
394+
388395
final CachedItem cachedItem =
389396
new CachedItem(
390397
() -> {
@@ -394,6 +401,7 @@ private static void ensureAttachmentSizeLimit(
394401
final Writer writer = new BufferedWriter(new OutputStreamWriter(stream, UTF_8))) {
395402
serializer.serialize(profileChunk, writer);
396403
metadataBytes = stream.toByteArray();
404+
metaLength.set(metadataBytes.length);
397405
} catch (IOException e) {
398406
throw new SentryEnvelopeException(
399407
String.format(
@@ -416,9 +424,6 @@ private static void ensureAttachmentSizeLimit(
416424
System.arraycopy(metadataBytes, 0, payload, 0, metadataBytes.length);
417425
System.arraycopy(traceBytes, 0, payload, metadataBytes.length, traceBytes.length);
418426

419-
// Store metaLength so the header callable can read it after lazy evaluation
420-
profileChunk.setMetaLength(metadataBytes.length);
421-
422427
return payload;
423428
});
424429

@@ -431,7 +436,7 @@ private static void ensureAttachmentSizeLimit(
431436
null,
432437
profileChunk.getPlatform(),
433438
null,
434-
(Callable<Integer>) () -> profileChunk.getMetaLength());
439+
(Callable<Integer>) metaLength::get);
435440

436441
// avoid method refs on Android due to some issues with older AGP setups
437442
// noinspection Convert2MethodRef

sentry/src/main/java/io/sentry/SentryEnvelopeItemHeader.java

Lines changed: 53 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@ public final class SentryEnvelopeItemHeader implements JsonSerializable, JsonUnk
2121
private final int length;
2222
@Nullable private final Callable<Integer> getLength;
2323
private final @Nullable String attachmentType;
24-
private final @Nullable Integer metaLength;
25-
@Nullable private final Callable<Integer> getMetaLength;
24+
@Nullable private final Callable<Integer> calculateMetaLength;
2625

2726
private @Nullable Map<String, Object> unknown;
2827

@@ -54,31 +53,42 @@ public int getLength() {
5453
return platform;
5554
}
5655

57-
public @Nullable Integer getMetaLength() {
58-
if (getMetaLength != null) {
56+
@Nullable
57+
Integer getMetaLength() {
58+
if (calculateMetaLength != null) {
5959
try {
60-
return getMetaLength.call();
60+
return calculateMetaLength.call();
6161
} catch (Throwable ignored) {
6262
return null;
6363
}
6464
}
65-
return metaLength;
65+
return null;
6666
}
6767

68-
@ApiStatus.Internal
69-
public SentryEnvelopeItemHeader(
68+
private SentryEnvelopeItemHeader(
7069
final @NotNull SentryItemType type,
7170
int length,
71+
final @Nullable Callable<Integer> getLength,
7272
final @Nullable String contentType,
7373
final @Nullable String fileName,
7474
final @Nullable String attachmentType,
7575
final @Nullable String platform,
76-
final @Nullable Integer itemCount) {
77-
this(type, length, contentType, fileName, attachmentType, platform, itemCount, null);
76+
final @Nullable Integer itemCount,
77+
final @Nullable Callable<Integer> calculateMetaLength) {
78+
this.type = Objects.requireNonNull(type, "type is required");
79+
this.contentType = contentType;
80+
this.length = length;
81+
this.fileName = fileName;
82+
this.getLength = getLength;
83+
this.attachmentType = attachmentType;
84+
this.platform = platform;
85+
this.itemCount = itemCount;
86+
this.calculateMetaLength = calculateMetaLength;
7887
}
7988

89+
/** Eager constructor used internally by {@link Deserializer#deserialize}. */
8090
@ApiStatus.Internal
81-
public SentryEnvelopeItemHeader(
91+
private SentryEnvelopeItemHeader(
8292
final @NotNull SentryItemType type,
8393
int length,
8494
final @Nullable String contentType,
@@ -87,44 +97,53 @@ public SentryEnvelopeItemHeader(
8797
final @Nullable String platform,
8898
final @Nullable Integer itemCount,
8999
final @Nullable Integer metaLength) {
90-
this.type = Objects.requireNonNull(type, "type is required");
91-
this.contentType = contentType;
92-
this.length = length;
93-
this.fileName = fileName;
94-
this.getLength = null;
95-
this.attachmentType = attachmentType;
96-
this.platform = platform;
97-
this.itemCount = itemCount;
98-
this.metaLength = metaLength;
99-
this.getMetaLength = null;
100+
this(
101+
type,
102+
length,
103+
null,
104+
contentType,
105+
fileName,
106+
attachmentType,
107+
platform,
108+
itemCount,
109+
metaLength != null ? () -> metaLength : null);
100110
}
101111

102-
SentryEnvelopeItemHeader(
112+
@ApiStatus.Internal
113+
public SentryEnvelopeItemHeader(
103114
final @NotNull SentryItemType type,
104-
final @Nullable Callable<Integer> getLength,
115+
int length,
105116
final @Nullable String contentType,
106117
final @Nullable String fileName,
107-
final @Nullable String attachmentType) {
108-
this(type, getLength, contentType, fileName, attachmentType, null, null);
118+
final @Nullable String attachmentType,
119+
final @Nullable String platform,
120+
final @Nullable Integer itemCount) {
121+
this(type, length, contentType, fileName, attachmentType, platform, itemCount, null);
109122
}
110123

124+
/**
125+
* Lazy constructor. Both length and metaLength are computed lazily as these depend on the Item
126+
* having been evaluated.
127+
*/
111128
SentryEnvelopeItemHeader(
112129
final @NotNull SentryItemType type,
113130
final @Nullable Callable<Integer> getLength,
114131
final @Nullable String contentType,
115132
final @Nullable String fileName,
116133
final @Nullable String attachmentType,
117134
final @Nullable String platform,
118-
final @Nullable Integer itemCount) {
135+
final @Nullable Integer itemCount,
136+
final @Nullable Callable<Integer> calculateMetaLength) {
119137
this(
120138
type,
139+
-1,
121140
getLength,
122141
contentType,
123142
fileName,
124143
attachmentType,
125144
platform,
126145
itemCount,
127-
(Integer) null);
146+
calculateMetaLength);
128147
}
129148

130149
SentryEnvelopeItemHeader(
@@ -134,39 +153,17 @@ public SentryEnvelopeItemHeader(
134153
final @Nullable String fileName,
135154
final @Nullable String attachmentType,
136155
final @Nullable String platform,
137-
final @Nullable Integer itemCount,
138-
final @Nullable Integer metaLength) {
139-
this.type = Objects.requireNonNull(type, "type is required");
140-
this.contentType = contentType;
141-
this.length = -1;
142-
this.fileName = fileName;
143-
this.getLength = getLength;
144-
this.attachmentType = attachmentType;
145-
this.platform = platform;
146-
this.itemCount = itemCount;
147-
this.metaLength = metaLength;
148-
this.getMetaLength = null;
156+
final @Nullable Integer itemCount) {
157+
this(type, getLength, contentType, fileName, attachmentType, platform, itemCount, null);
149158
}
150159

151160
SentryEnvelopeItemHeader(
152161
final @NotNull SentryItemType type,
153162
final @Nullable Callable<Integer> getLength,
154163
final @Nullable String contentType,
155164
final @Nullable String fileName,
156-
final @Nullable String attachmentType,
157-
final @Nullable String platform,
158-
final @Nullable Integer itemCount,
159-
final @Nullable Callable<Integer> getMetaLength) {
160-
this.type = Objects.requireNonNull(type, "type is required");
161-
this.contentType = contentType;
162-
this.length = -1;
163-
this.fileName = fileName;
164-
this.getLength = getLength;
165-
this.attachmentType = attachmentType;
166-
this.platform = platform;
167-
this.itemCount = itemCount;
168-
this.metaLength = null;
169-
this.getMetaLength = getMetaLength;
165+
final @Nullable String attachmentType) {
166+
this(type, getLength, contentType, fileName, attachmentType, null, null);
170167
}
171168

172169
SentryEnvelopeItemHeader(
@@ -219,8 +216,9 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger
219216
if (itemCount != null) {
220217
writer.name(JsonKeys.ITEM_COUNT).value(itemCount);
221218
}
222-
if (metaLength != null) {
223-
writer.name(JsonKeys.META_LENGTH).value(metaLength);
219+
final @Nullable Integer resolvedMetaLength = getMetaLength();
220+
if (resolvedMetaLength != null) {
221+
writer.name(JsonKeys.META_LENGTH).value(resolvedMetaLength);
224222
}
225223
writer.name(JsonKeys.LENGTH).value(getLength());
226224
if (unknown != null) {

0 commit comments

Comments
 (0)