Skip to content

Commit 5f33489

Browse files
committed
fix: remove the dependency on protobuf-lite for tombstones
1 parent a415905 commit 5f33489

File tree

6 files changed

+249
-477
lines changed

6 files changed

+249
-477
lines changed

gradle/libs.versions.toml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ spotless = "7.0.4"
4141
gummyBears = "0.12.0"
4242
camerax = "1.3.0"
4343
openfeature = "1.18.2"
44-
protobuf = "3.25.8"
4544

4645
[plugins]
4746
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
@@ -61,7 +60,6 @@ spotless = { id = "com.diffplug.spotless", version.ref = "spotless" }
6160
detekt = { id = "io.gitlab.arturbosch.detekt", version = "1.23.8" }
6261
jacoco-android = { id = "com.mxalbert.gradle.jacoco-android", version = "0.2.0" }
6362
kover = { id = "org.jetbrains.kotlinx.kover", version = "0.7.3" }
64-
protobuf = { id = "com.google.protobuf", version = "0.9.5" }
6563
vanniktech-maven-publish = { id = "com.vanniktech.maven.publish", version = "0.30.0" }
6664
springboot2 = { id = "org.springframework.boot", version.ref = "springboot2" }
6765
springboot3 = { id = "org.springframework.boot", version.ref = "springboot3" }
@@ -145,8 +143,7 @@ otel-javaagent-extension-api = { module = "io.opentelemetry.javaagent:openteleme
145143
otel-semconv = { module = "io.opentelemetry.semconv:opentelemetry-semconv", version.ref = "otelSemanticConventions" }
146144
otel-semconv-incubating = { module = "io.opentelemetry.semconv:opentelemetry-semconv-incubating", version.ref = "otelSemanticConventionsAlpha" }
147145
p6spy = { module = "p6spy:p6spy", version = "3.9.1" }
148-
protobuf-javalite = { module = "com.google.protobuf:protobuf-javalite", version.ref = "protobuf"}
149-
protoc = { module = "com.google.protobuf:protoc", version.ref = "protobuf" }
146+
epitaph = { module = "com.abovevacant:epitaph", version = "0.1.0" }
150147
quartz = { module = "org.quartz-scheduler:quartz", version = "2.3.0" }
151148
reactor-core = { module = "io.projectreactor:reactor-core", version = "3.5.3" }
152149
retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }

sentry-android-core/build.gradle.kts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ plugins {
88
alias(libs.plugins.jacoco.android)
99
alias(libs.plugins.errorprone)
1010
alias(libs.plugins.gradle.versions)
11-
alias(libs.plugins.protobuf)
1211
}
1312

1413
android {
@@ -84,7 +83,7 @@ dependencies {
8483
implementation(libs.androidx.lifecycle.common.java8)
8584
implementation(libs.androidx.lifecycle.process)
8685
implementation(libs.androidx.core)
87-
implementation(libs.protobuf.javalite)
86+
implementation(libs.epitaph)
8887

8988
errorprone(libs.errorprone.core)
9089
errorprone(libs.nopen.checker)
@@ -113,10 +112,3 @@ dependencies {
113112
testRuntimeOnly(libs.androidx.fragment.ktx)
114113
testRuntimeOnly(libs.timber)
115114
}
116-
117-
protobuf {
118-
protoc { artifact = libs.protoc.get().toString() }
119-
generateProtoTasks {
120-
all().forEach { task -> task.builtins { create("java") { option("lite") } } }
121-
}
122-
}

sentry-android-core/proguard-rules.pro

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,6 @@
5454

5555
-keepnames class io.sentry.android.core.ApplicationNotResponding
5656

57-
# protobuf-java lite
58-
# https://github.com/protocolbuffers/protobuf/blob/5d876c9fec1a6f2feb0750694f803f89312bffff/java/lite.md#r8-rule-to-make-production-app-builds-work
59-
-keep class * extends com.google.protobuf.GeneratedMessageLite { *; }
6057

6158
##---------------End: proguard configuration for android-core ----------
6259

sentry-android-core/src/main/java/io/sentry/android/core/internal/tombstone/TombstoneParser.java

Lines changed: 75 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
package io.sentry.android.core.internal.tombstone;
22

33
import androidx.annotation.NonNull;
4+
import com.abovevacant.epitaph.core.BacktraceFrame;
5+
import com.abovevacant.epitaph.core.MemoryMapping;
6+
import com.abovevacant.epitaph.core.Register;
7+
import com.abovevacant.epitaph.core.Signal;
8+
import com.abovevacant.epitaph.core.Tombstone;
9+
import com.abovevacant.epitaph.core.TombstoneThread;
10+
import com.abovevacant.epitaph.wire.TombstoneDecoder;
411
import io.sentry.SentryEvent;
512
import io.sentry.SentryLevel;
613
import io.sentry.SentryStackTraceFactory;
@@ -27,7 +34,7 @@
2734

2835
public class TombstoneParser implements Closeable {
2936

30-
private final InputStream tombstoneStream;
37+
@Nullable private final InputStream tombstoneStream;
3138
@NotNull private final List<String> inAppIncludes;
3239
@NotNull private final List<String> inAppExcludes;
3340
@Nullable private final String nativeLibraryDir;
@@ -38,7 +45,14 @@ private static String formatHex(long value) {
3845
}
3946

4047
public TombstoneParser(
41-
@NonNull final InputStream tombstoneStream,
48+
@NotNull List<String> inAppIncludes,
49+
@NotNull List<String> inAppExcludes,
50+
@Nullable String nativeLibraryDir) {
51+
this(null, inAppIncludes, inAppExcludes, nativeLibraryDir);
52+
}
53+
54+
public TombstoneParser(
55+
@Nullable final InputStream tombstoneStream,
4256
@NotNull List<String> inAppIncludes,
4357
@NotNull List<String> inAppExcludes,
4458
@Nullable String nativeLibraryDir) {
@@ -58,10 +72,14 @@ public TombstoneParser(
5872

5973
@NonNull
6074
public SentryEvent parse() throws IOException {
61-
@NonNull
62-
final TombstoneProtos.Tombstone tombstone =
63-
TombstoneProtos.Tombstone.parseFrom(tombstoneStream);
75+
if (tombstoneStream == null) {
76+
throw new IOException("No InputStream provided; use parse(Tombstone) instead.");
77+
}
78+
return parse(TombstoneDecoder.decode(tombstoneStream));
79+
}
6480

81+
@NonNull
82+
public SentryEvent parse(@NonNull final Tombstone tombstone) {
6583
final SentryEvent event = new SentryEvent();
6684
event.setLevel(SentryLevel.FATAL);
6785

@@ -79,19 +97,18 @@ public SentryEvent parse() throws IOException {
7997

8098
@NonNull
8199
private List<SentryThread> createThreads(
82-
@NonNull final TombstoneProtos.Tombstone tombstone, @NonNull final SentryException exc) {
100+
@NonNull final Tombstone tombstone, @NonNull final SentryException exc) {
83101
final List<SentryThread> threads = new ArrayList<>();
84-
for (Map.Entry<Integer, TombstoneProtos.Thread> threadEntry :
85-
tombstone.getThreadsMap().entrySet()) {
86-
final TombstoneProtos.Thread threadEntryValue = threadEntry.getValue();
102+
for (Map.Entry<Integer, TombstoneThread> threadEntry : tombstone.threads.entrySet()) {
103+
final TombstoneThread threadEntryValue = threadEntry.getValue();
87104

88105
final SentryThread thread = new SentryThread();
89106
thread.setId(Long.valueOf(threadEntry.getKey()));
90-
thread.setName(threadEntryValue.getName());
107+
thread.setName(threadEntryValue.name);
91108

92109
final SentryStackTrace stacktrace = createStackTrace(threadEntryValue);
93110
thread.setStacktrace(stacktrace);
94-
if (tombstone.getTid() == threadEntryValue.getId()) {
111+
if (tombstone.tid == threadEntryValue.id) {
95112
thread.setCrashed(true);
96113
// even though we refer to the thread_id from the exception,
97114
// the backend currently requires a stack-trace in exception
@@ -104,38 +121,38 @@ private List<SentryThread> createThreads(
104121
}
105122

106123
@NonNull
107-
private SentryStackTrace createStackTrace(@NonNull final TombstoneProtos.Thread thread) {
124+
private SentryStackTrace createStackTrace(@NonNull final TombstoneThread thread) {
108125
final List<SentryStackFrame> frames = new ArrayList<>();
109126

110-
for (TombstoneProtos.BacktraceFrame frame : thread.getCurrentBacktraceList()) {
111-
if (frame.getFileName().endsWith("libart.so")) {
127+
for (BacktraceFrame frame : thread.backtrace) {
128+
if (frame.fileName.endsWith("libart.so")) {
112129
// We ignore all ART frames for time being because they aren't actionable for app developers
113130
continue;
114131
}
115-
if (frame.getFileName().startsWith("<anonymous") && frame.getFunctionName().isEmpty()) {
132+
if (frame.fileName.startsWith("<anonymous") && frame.functionName.isEmpty()) {
116133
// Code in anonymous VMAs that does not resolve to a function name, cannot be symbolicated
117134
// in the backend either, and thus has no value in the UI.
118135
continue;
119136
}
120137
final SentryStackFrame stackFrame = new SentryStackFrame();
121-
stackFrame.setPackage(frame.getFileName());
122-
stackFrame.setFunction(frame.getFunctionName());
123-
stackFrame.setInstructionAddr(formatHex(frame.getPc()));
138+
stackFrame.setPackage(frame.fileName);
139+
stackFrame.setFunction(frame.functionName);
140+
stackFrame.setInstructionAddr(formatHex(frame.pc));
124141

125142
// inAppIncludes/inAppExcludes filter by Java/Kotlin package names, which don't overlap
126143
// with native C/C++ function names (e.g., "crash", "__libc_init"). For native frames,
127144
// isInApp() returns null, making nativeLibraryDir the effective in-app check.
128-
// Protobuf returns "" for unset function names, which would incorrectly return true
145+
// epitaph returns "" for unset function names, which would incorrectly return true
129146
// from isInApp(), so we treat empty as false to let nativeLibraryDir decide.
130-
final String functionName = frame.getFunctionName();
147+
final String functionName = frame.functionName;
131148
@Nullable
132149
Boolean inApp =
133150
functionName.isEmpty()
134151
? Boolean.FALSE
135152
: SentryStackTraceFactory.isInApp(functionName, inAppIncludes, inAppExcludes);
136153

137154
final boolean isInNativeLibraryDir =
138-
nativeLibraryDir != null && frame.getFileName().startsWith(nativeLibraryDir);
155+
nativeLibraryDir != null && frame.fileName.startsWith(nativeLibraryDir);
139156
inApp = (inApp != null && inApp) || isInNativeLibraryDir;
140157

141158
stackFrame.setInApp(inApp);
@@ -151,74 +168,73 @@ private SentryStackTrace createStackTrace(@NonNull final TombstoneProtos.Thread
151168
stacktrace.setInstructionAddressAdjustment(SentryStackTrace.InstructionAddressAdjustment.NONE);
152169

153170
final Map<String, String> registers = new HashMap<>();
154-
for (TombstoneProtos.Register register : thread.getRegistersList()) {
155-
registers.put(register.getName(), formatHex(register.getU64()));
171+
for (Register register : thread.registers) {
172+
registers.put(register.name, formatHex(register.value));
156173
}
157174
stacktrace.setRegisters(registers);
158175

159176
return stacktrace;
160177
}
161178

162179
@NonNull
163-
private List<SentryException> createException(@NonNull TombstoneProtos.Tombstone tombstone) {
180+
private List<SentryException> createException(@NonNull Tombstone tombstone) {
164181
final SentryException exception = new SentryException();
165182

166-
if (tombstone.hasSignalInfo()) {
167-
final TombstoneProtos.Signal signalInfo = tombstone.getSignalInfo();
168-
exception.setType(signalInfo.getName());
169-
exception.setValue(excTypeValueMap.get(signalInfo.getName()));
183+
if (tombstone.hasSignal()) {
184+
final Signal signalInfo = tombstone.signal;
185+
exception.setType(signalInfo.name);
186+
exception.setValue(excTypeValueMap.get(signalInfo.name));
170187
exception.setMechanism(createMechanismFromSignalInfo(signalInfo));
171188
}
172189

173-
exception.setThreadId((long) tombstone.getTid());
190+
exception.setThreadId((long) tombstone.tid);
174191
final List<SentryException> exceptions = new ArrayList<>(1);
175192
exceptions.add(exception);
176193

177194
return exceptions;
178195
}
179196

180197
@NonNull
181-
private static Mechanism createMechanismFromSignalInfo(
182-
@NonNull final TombstoneProtos.Signal signalInfo) {
198+
private static Mechanism createMechanismFromSignalInfo(@NonNull final Signal signalInfo) {
183199

184200
final Mechanism mechanism = new Mechanism();
185201
mechanism.setType(NativeExceptionMechanism.TOMBSTONE.getValue());
186202
mechanism.setHandled(false);
187203
mechanism.setSynthetic(true);
188204

189205
final Map<String, Object> meta = new HashMap<>();
190-
meta.put("number", signalInfo.getNumber());
191-
meta.put("name", signalInfo.getName());
192-
meta.put("code", signalInfo.getCode());
193-
meta.put("code_name", signalInfo.getCodeName());
206+
meta.put("number", signalInfo.number);
207+
meta.put("name", signalInfo.name);
208+
meta.put("code", signalInfo.code);
209+
meta.put("code_name", signalInfo.codeName);
194210
mechanism.setMeta(meta);
195211

196212
return mechanism;
197213
}
198214

199215
@NonNull
200-
private Message constructMessage(@NonNull final TombstoneProtos.Tombstone tombstone) {
216+
private Message constructMessage(@NonNull final Tombstone tombstone) {
201217
final Message message = new Message();
202-
final TombstoneProtos.Signal signalInfo = tombstone.getSignalInfo();
218+
final Signal signalInfo = tombstone.signal;
203219

204220
// reproduce the message `debuggerd` would use to dump the stack trace in logcat
205-
String command = String.join(" ", tombstone.getCommandLineList());
206-
if (tombstone.hasSignalInfo()) {
207-
String abortMessage = tombstone.getAbortMessage();
221+
String command = String.join(" ", tombstone.commandLine);
222+
if (tombstone.hasSignal()) {
223+
String abortMessage = tombstone.abortMessage;
208224
message.setFormatted(
209225
String.format(
210226
Locale.ROOT,
211227
"%sFatal signal %s (%d), %s (%d), pid = %d (%s)",
212228
!abortMessage.isEmpty() ? abortMessage + ": " : "",
213-
signalInfo.getName(),
214-
signalInfo.getNumber(),
215-
signalInfo.getCodeName(),
216-
signalInfo.getCode(),
217-
tombstone.getPid(),
229+
signalInfo.name,
230+
signalInfo.number,
231+
signalInfo.codeName,
232+
signalInfo.code,
233+
tombstone.pid,
218234
command));
219235
} else {
220236
message.setFormatted(
221-
String.format(Locale.ROOT, "Fatal exit pid = %d (%s)", tombstone.getPid(), command));
237+
String.format(Locale.ROOT, "Fatal exit pid = %d (%s)", tombstone.pid, command));
222238
}
223239

224240
return message;
@@ -236,11 +252,11 @@ private static class ModuleAccumulator {
236252
long beginAddress;
237253
long endAddress;
238254

239-
ModuleAccumulator(TombstoneProtos.MemoryMapping mapping) {
240-
this.mappingName = mapping.getMappingName();
241-
this.buildId = mapping.getBuildId();
242-
this.beginAddress = mapping.getBeginAddress();
243-
this.endAddress = mapping.getEndAddress();
255+
ModuleAccumulator(MemoryMapping mapping) {
256+
this.mappingName = mapping.mappingName;
257+
this.buildId = mapping.buildId;
258+
this.beginAddress = mapping.beginAddress;
259+
this.endAddress = mapping.endAddress;
244260
}
245261

246262
void extendTo(long newEndAddress) {
@@ -266,7 +282,7 @@ DebugImage toDebugImage() {
266282
}
267283
}
268284

269-
private DebugMeta createDebugMeta(@NonNull final TombstoneProtos.Tombstone tombstone) {
285+
private DebugMeta createDebugMeta(@NonNull final Tombstone tombstone) {
270286
final List<DebugImage> images = new ArrayList<>();
271287

272288
// Coalesce memory mappings into modules similar to how sentry-native does it.
@@ -277,27 +293,27 @@ private DebugMeta createDebugMeta(@NonNull final TombstoneProtos.Tombstone tombs
277293
// combined with non-empty build_id as a proxy for this check.
278294
ModuleAccumulator currentModule = null;
279295

280-
for (TombstoneProtos.MemoryMapping mapping : tombstone.getMemoryMappingsList()) {
296+
for (MemoryMapping mapping : tombstone.memoryMappings) {
281297
// Skip mappings that are not readable
282-
if (!mapping.getRead()) {
298+
if (!mapping.read) {
283299
continue;
284300
}
285301

286302
// Skip mappings with empty name or in /dev/
287-
final String mappingName = mapping.getMappingName();
303+
final String mappingName = mapping.mappingName;
288304
if (mappingName.isEmpty() || mappingName.startsWith("/dev/")) {
289305
continue;
290306
}
291307

292-
final boolean hasBuildId = !mapping.getBuildId().isEmpty();
293-
final boolean isFileStart = mapping.getOffset() == 0;
308+
final boolean hasBuildId = !mapping.buildId.isEmpty();
309+
final boolean isFileStart = mapping.offset == 0;
294310

295311
if (hasBuildId && isFileStart) {
296312
// Check for duplicated mappings: On Android, the same ELF can have multiple
297313
// mappings at offset 0 with different permissions (r--p, r-xp, r--p).
298314
// If it's the same file as the current module, just extend it.
299315
if (currentModule != null && mappingName.equals(currentModule.mappingName)) {
300-
currentModule.extendTo(mapping.getEndAddress());
316+
currentModule.extendTo(mapping.endAddress);
301317
continue;
302318
}
303319

@@ -313,7 +329,7 @@ private DebugMeta createDebugMeta(@NonNull final TombstoneProtos.Tombstone tombs
313329
currentModule = new ModuleAccumulator(mapping);
314330
} else if (currentModule != null && mappingName.equals(currentModule.mappingName)) {
315331
// Extend the current module with this mapping (same file, continuation)
316-
currentModule.extendTo(mapping.getEndAddress());
332+
currentModule.extendTo(mapping.endAddress);
317333
}
318334
}
319335

0 commit comments

Comments
 (0)