Skip to content

Commit 2b635ae

Browse files
authored
Merge 258d533 into 35e1010
2 parents 35e1010 + 258d533 commit 2b635ae

File tree

4 files changed

+117
-102
lines changed

4 files changed

+117
-102
lines changed

sentry-android-core/src/test/java/io/sentry/android/core/internal/tombstone/TombstoneParserTest.kt

Lines changed: 108 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -57,83 +57,13 @@ class TombstoneParserTest {
5757
"/data/app/~~gu-2hA9_Zg6tfIuDAbLpKA==/io.sentry.samples.android-MFqmKAMnl9AjNlHcO3mejA==/lib/arm64"
5858

5959
@Test
60-
fun `parses a snapshot tombstone into Event`() {
61-
val tombstoneStream =
62-
GZIPInputStream(TombstoneParserTest::class.java.getResourceAsStream("/tombstone.pb.gz"))
63-
val parser = TombstoneParser(tombstoneStream, inAppIncludes, inAppExcludes, nativeLibraryDir)
64-
val event = parser.parse()
65-
66-
// top-level data
67-
assertNotNull(event.eventId)
68-
assertEquals(
69-
"Fatal signal SIGSEGV (11), SEGV_MAPERR (1), pid = 21891 (io.sentry.samples.android)",
70-
event.message!!.formatted,
71-
)
72-
assertEquals("native", event.platform)
73-
assertEquals("FATAL", event.level!!.name)
74-
75-
// exception
76-
// we only track one native exception (no nesting, one crashed thread)
77-
assertEquals(1, event.exceptions!!.size)
78-
val exception = event.exceptions!![0]
79-
assertEquals("SIGSEGV", exception.type)
80-
assertEquals("Segfault", exception.value)
81-
val crashedThreadId = exception.threadId
82-
assertNotNull(crashedThreadId)
83-
84-
val mechanism = exception.mechanism
85-
assertEquals("Tombstone", mechanism!!.type)
86-
assertEquals(false, mechanism.isHandled)
87-
assertEquals(true, mechanism.synthetic)
88-
assertEquals("SIGSEGV", mechanism.meta!!["name"])
89-
assertEquals(11, mechanism.meta!!["number"])
90-
assertEquals("SEGV_MAPERR", mechanism.meta!!["code_name"])
91-
assertEquals(1, mechanism.meta!!["code"])
92-
93-
// threads
94-
assertEquals(62, event.threads!!.size)
95-
for (thread in event.threads!!) {
96-
assertNotNull(thread.id)
97-
if (thread.id == crashedThreadId) {
98-
assert(thread.isCrashed == true)
99-
}
100-
assert(thread.stacktrace!!.frames!!.isNotEmpty())
101-
102-
for (frame in thread.stacktrace!!.frames!!) {
103-
assertNotNull(frame.function)
104-
if (frame.platform == "java") {
105-
// Java frames have module instead of package/instructionAddr
106-
assertNotNull(frame.module)
107-
} else {
108-
assertNotNull(frame.`package`)
109-
assertNotNull(frame.instructionAddr)
110-
}
111-
112-
if (thread.id == crashedThreadId) {
113-
if (frame.isInApp!!) {
114-
assert(
115-
frame.module?.startsWith(inAppIncludes[0]) == true ||
116-
frame.function!!.startsWith(inAppIncludes[0]) ||
117-
frame.`package`?.startsWith(nativeLibraryDir) == true
118-
)
119-
}
120-
}
121-
}
122-
123-
assert(thread.stacktrace!!.registers!!.keys.containsAll(expectedRegisters))
124-
}
60+
fun `parses tombstone into Event`() {
61+
assertTombstoneParsesCorrectly("/tombstone.pb.gz")
62+
}
12563

126-
// debug-meta
127-
assertEquals(352, event.debugMeta!!.images!!.size)
128-
for (image in event.debugMeta!!.images!!) {
129-
assertEquals("elf", image.type)
130-
assertNotNull(image.debugId)
131-
assertNotNull(image.codeId)
132-
assertNotNull(image.codeFile)
133-
val imageAddress = image.imageAddr!!.removePrefix("0x").toLong(16)
134-
assert(imageAddress > 0)
135-
assert(image.imageSize!! > 0)
136-
}
64+
@Test
65+
fun `parses tombstone_r8 with OAT frames into Event`() {
66+
assertTombstoneParsesCorrectly("/tombstone_r8.pb.gz")
13767
}
13868

13969
@Test
@@ -436,32 +366,13 @@ class TombstoneParserTest {
436366
}
437367

438368
@Test
439-
fun `java frames snapshot test for all threads`() {
440-
val tombstoneStream =
441-
GZIPInputStream(TombstoneParserTest::class.java.getResourceAsStream("/tombstone.pb.gz"))
442-
val parser = TombstoneParser(tombstoneStream, inAppIncludes, inAppExcludes, nativeLibraryDir)
443-
val event = parser.parse()
444-
445-
val logger = mock<ILogger>()
446-
val writer = StringWriter()
447-
val jsonWriter = JsonObjectWriter(writer, 100)
448-
jsonWriter.beginObject()
449-
for (thread in event.threads!!) {
450-
val javaFrames = thread.stacktrace!!.frames!!.filter { it.platform == "java" }
451-
if (javaFrames.isEmpty()) continue
452-
jsonWriter.name(thread.id.toString())
453-
jsonWriter.beginArray()
454-
for (frame in javaFrames) {
455-
frame.serialize(jsonWriter, logger)
456-
}
457-
jsonWriter.endArray()
458-
}
459-
jsonWriter.endObject()
460-
461-
val actualJson = writer.toString()
462-
val expectedJson = readGzippedResourceFile("/tombstone_java_frames.json.gz")
369+
fun `java frames snapshot test`() {
370+
assertJavaFramesSnapshot("/tombstone.pb.gz", "/tombstone_java_frames.json.gz")
371+
}
463372

464-
assertEquals(expectedJson, actualJson)
373+
@Test
374+
fun `tombstone_r8 java frames snapshot test`() {
375+
assertJavaFramesSnapshot("/tombstone_r8.pb.gz", "/tombstone_r8_java_frames.json.gz")
465376
}
466377

467378
@Test
@@ -577,6 +488,102 @@ class TombstoneParserTest {
577488
return parser.parse()
578489
}
579490

491+
private fun assertTombstoneParsesCorrectly(tombstoneResource: String) {
492+
val tombstoneStream =
493+
GZIPInputStream(TombstoneParserTest::class.java.getResourceAsStream(tombstoneResource))
494+
val parser = TombstoneParser(tombstoneStream, inAppIncludes, inAppExcludes, nativeLibraryDir)
495+
val event = parser.parse()
496+
497+
// top-level data
498+
assertNotNull(event.eventId)
499+
assertNotNull(event.message!!.formatted)
500+
assertEquals("native", event.platform)
501+
assertEquals("FATAL", event.level!!.name)
502+
503+
// exception
504+
assertEquals(1, event.exceptions!!.size)
505+
val exception = event.exceptions!![0]
506+
assertNotNull(exception.type)
507+
val crashedThreadId = exception.threadId
508+
assertNotNull(crashedThreadId)
509+
510+
val mechanism = exception.mechanism
511+
assertNotNull(mechanism)
512+
assertEquals("Tombstone", mechanism.type)
513+
assertEquals(false, mechanism.isHandled)
514+
assertEquals(true, mechanism.synthetic)
515+
516+
// threads
517+
assert(event.threads!!.isNotEmpty())
518+
var hasCrashedThread = false
519+
for (thread in event.threads!!) {
520+
assertNotNull(thread.id)
521+
if (thread.id == crashedThreadId) {
522+
assert(thread.isCrashed == true)
523+
hasCrashedThread = true
524+
}
525+
assert(thread.stacktrace!!.frames!!.isNotEmpty())
526+
527+
for (frame in thread.stacktrace!!.frames!!) {
528+
assertNotNull(frame.function)
529+
if (frame.platform == "java") {
530+
assertNotNull(frame.module)
531+
assert(frame.function!!.isNotEmpty()) {
532+
"Java frame has empty function name in thread ${thread.id}"
533+
}
534+
assertNotNull(frame.isInApp)
535+
} else {
536+
assertNotNull(frame.`package`)
537+
assertNotNull(frame.instructionAddr)
538+
}
539+
}
540+
541+
assertNotNull(thread.stacktrace!!.registers)
542+
}
543+
assert(hasCrashedThread) { "No crashed thread found matching exception threadId" }
544+
545+
// debug-meta
546+
assertNotNull(event.debugMeta)
547+
assert(event.debugMeta!!.images!!.isNotEmpty())
548+
for (image in event.debugMeta!!.images!!) {
549+
assertEquals("elf", image.type)
550+
assertNotNull(image.debugId)
551+
assertNotNull(image.codeId)
552+
assertNotNull(image.codeFile)
553+
val imageAddress = image.imageAddr!!.removePrefix("0x").toLong(16)
554+
assert(imageAddress > 0)
555+
assert(image.imageSize!! > 0)
556+
}
557+
}
558+
559+
private fun assertJavaFramesSnapshot(tombstoneResource: String, snapshotResource: String) {
560+
val tombstoneStream =
561+
GZIPInputStream(TombstoneParserTest::class.java.getResourceAsStream(tombstoneResource))
562+
val parser = TombstoneParser(tombstoneStream, inAppIncludes, inAppExcludes, nativeLibraryDir)
563+
val event = parser.parse()
564+
565+
val logger = mock<ILogger>()
566+
val writer = StringWriter()
567+
val jsonWriter = JsonObjectWriter(writer, 100)
568+
jsonWriter.beginObject()
569+
for (thread in event.threads!!) {
570+
val javaFrames = thread.stacktrace!!.frames!!.filter { it.platform == "java" }
571+
if (javaFrames.isEmpty()) continue
572+
jsonWriter.name(thread.id.toString())
573+
jsonWriter.beginArray()
574+
for (frame in javaFrames) {
575+
frame.serialize(jsonWriter, logger)
576+
}
577+
jsonWriter.endArray()
578+
}
579+
jsonWriter.endObject()
580+
581+
val actualJson = writer.toString()
582+
val expectedJson = readGzippedResourceFile(snapshotResource)
583+
584+
assertEquals(expectedJson, actualJson)
585+
}
586+
580587
private fun serializeDebugMeta(debugMeta: DebugMeta): String {
581588
val logger = mock<ILogger>()
582589
val writer = StringWriter()
64.4 KB
Binary file not shown.
Binary file not shown.

sentry-samples/sentry-samples-android/build.gradle.kts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ plugins {
77
id("com.android.application")
88
alias(libs.plugins.kotlin.android)
99
alias(libs.plugins.kotlin.compose)
10+
alias(libs.plugins.sentry)
1011
}
1112

1213
android {
@@ -116,6 +117,12 @@ android {
116117
@Suppress("UnstableApiUsage") packagingOptions { jniLibs { useLegacyPackaging = true } }
117118
}
118119

120+
sentry {
121+
autoUploadProguardMapping = false
122+
autoUploadNativeSymbols = false
123+
autoUploadSourceContext = false
124+
}
125+
119126
dependencies {
120127
implementation(
121128
kotlin(Config.kotlinStdLib, org.jetbrains.kotlin.config.KotlinCompilerVersion.VERSION)
@@ -124,7 +131,8 @@ dependencies {
124131
implementation(projects.sentryAndroid)
125132
implementation(projects.sentryAndroidFragment)
126133
implementation(projects.sentryAndroidTimber)
127-
implementation(projects.sentryCompose)
134+
// implementation(projects.sentryCompose) // TODO: re-enable, disabled due to R8 duplicate class
135+
// error with KMP module
128136
implementation(projects.sentryKotlinExtensions)
129137
implementation(projects.sentryOkhttp)
130138
implementation(projects.sentrySpotlight)

0 commit comments

Comments
 (0)