Skip to content

Commit

Permalink
Merge pull request #1903 from bugsnag/PLAT-10706/open-file-descriptor…
Browse files Browse the repository at this point in the history
…s-to-metadata

Adding open file descriptors to metadata
  • Loading branch information
lemnik authored Sep 20, 2023
2 parents 461bb50 + 00a141f commit fa51e42
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 21 deletions.
1 change: 0 additions & 1 deletion bugsnag-plugin-android-exitinfo/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
<ID>NestedBlockDepth:TraceParser.kt$TraceParser$private fun parseThreadAttributes(line: String)</ID>
<ID>ReturnCount:TraceParser.kt$TraceParser$@VisibleForTesting internal fun parseNativeFrame(line: String): Stackframe?</ID>
<ID>SwallowedException:ExitInfoCallback.kt$ExitInfoCallback$exc: Throwable</ID>
<ID>UnusedPrivateProperty:BugsnagExitInfoPlugin.kt$BugsnagExitInfoPlugin$/** * Whether to add the list of open FDs to correlated reports */ private val listOpenFds: Boolean = true</ID>
<ID>UnusedPrivateProperty:BugsnagExitInfoPlugin.kt$BugsnagExitInfoPlugin$/** * Whether to report stored logcat messages metadata */ private val includeLogcat: Boolean = false</ID>
</CurrentIssues>
</SmellBaseline>
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,10 @@ class BugsnagExitInfoPlugin @JvmOverloads constructor(
}
)
}

exitInfoCallback = ExitInfoCallback(
client.appContext,
TombstoneEventEnhancer(client.logger),
TombstoneEventEnhancer(client.logger, listOpenFds),
TraceEventEnhancer(client.logger, client.immutableConfig.projectPackages)
)
client.addOnSend(exitInfoCallback)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,29 @@ import androidx.annotation.RequiresApi
import com.bugsnag.android.Thread as BugsnagThread

internal class TombstoneEventEnhancer(
private val logger: Logger
private val logger: Logger,
private val listOpenFds: Boolean
) : (Event, ApplicationExitInfo) -> Unit {
@RequiresApi(Build.VERSION_CODES.R)
override fun invoke(event: Event, exitInfo: ApplicationExitInfo) {
try {
TombstoneParser(logger).parse(exitInfo) { thread ->
mergeThreadIntoEvent(
thread,
event
)
}
TombstoneParser(logger).parse(
exitInfo,
listOpenFds,
threadConsumer = { thread ->
mergeThreadIntoEvent(
thread,
event
)
},
{ fd, path, owner ->
val fdInfo = if (owner.isNotEmpty()) mapOf(
"path" to path,
"owner" to owner
) else mapOf("path" to path)
event.addMetadata("Open FileDescriptors", fd.toString(), fdInfo)
}
)
} catch (ex: Exception) {
logger.w("could not parse tombstone file", ex)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,33 @@ internal class TombstoneParser(
@RequiresApi(Build.VERSION_CODES.R)
fun parse(
exitInfo: ApplicationExitInfo,
threadConsumer: (BugsnagThread) -> Unit
listOpenFds: Boolean,
threadConsumer: (BugsnagThread) -> Unit,
fileDescriptorConsumer: (Int, String, String) -> Unit
) {
try {
val trace: Tombstone = exitInfo.traceInputStream?.use {
Tombstone.newBuilder().mergeFrom(it).build()
} ?: return
extractTombstoneThreads(trace.threadsMap.values, threadConsumer)

if (listOpenFds) {
extractTombstoneFd(trace.openFdsList, fileDescriptorConsumer)
}
} catch (ex: Throwable) {
logger.w("Tombstone input stream threw an Exception", ex)
}
}

private fun extractTombstoneFd(
fdsList: List<TombstoneProtos.FD>,
fDConsumer: (Int, String, String) -> Unit
) {
fdsList.forEach { fd ->
fDConsumer(fd.fd, fd.path, fd.owner)
}
}

private fun extractTombstoneThreads(
values: Collection<TombstoneProtos.Thread>,
threadConsumer: (BugsnagThread) -> Unit
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.bugsnag.android
import android.app.ApplicationExitInfo
import com.bugsnag.android.internal.ImmutableConfig
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Test
import org.mockito.Mockito.mock
import org.mockito.Mockito.`when`
Expand All @@ -12,7 +13,7 @@ internal class TombstoneEventEnhancerTest {

private val logger = mock(Logger::class.java)

private val tombstoneEventEnhancer = TombstoneEventEnhancer(logger)
private val tombstoneEventEnhancer = TombstoneEventEnhancer(logger, true)

@Test
fun testTombstoneEnhancer() {
Expand Down Expand Up @@ -49,5 +50,9 @@ internal class TombstoneEventEnhancerTest {
assertEquals(667096L, testThread.stacktrace.first().lineNumber)
assertEquals("__rt_sigtimedwait", testThread.stacktrace.first().method)
assertEquals("__start_thread", testThread.stacktrace.last().method)

val firstFd = event.getMetadata("Open FileDescriptors")!!["0"] as Map<*, *>
assertEquals("/dev/null", firstFd["path"])
assertNull(firstFd["owner"])
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,18 @@ internal class TombstoneParserTest {
val file = this.javaClass.getResourceAsStream("/tombstone_01.pb")
`when`(exitInfo.traceInputStream).thenReturn(file)
val threads = mutableListOf<Thread>()
TombstoneParser(logger).parse(exitInfo) {
threads.add(it)
}
val fileDescriptors = ArrayList<Map<String, Any>>()
TombstoneParser(logger).parse(exitInfo, true, { thread ->
threads.add(thread)
}, { fd, path, owner ->
fileDescriptors.add(
mapOf(
"fd" to fd,
"path" to path,
"owner" to owner,
)
)
})

assertEquals("30640", threads.first().id)
assertEquals("30639", threads.last().id)
Expand All @@ -38,29 +47,54 @@ internal class TombstoneParserTest {
assertEquals(8L, firstStackFrame.first().symbolAddress)
assertEquals(0L, firstStackFrame.first().loadAddress)
assertEquals("01331f74b0bb2cb958bdc15282b8ec7b", firstStackFrame.first().codeIdentifier)

assertEquals(145, fileDescriptors.size)
val firstFileDescriptor = fileDescriptors.first()
assertEquals(0, firstFileDescriptor["fd"])
assertEquals("/dev/null", firstFileDescriptor["path"])
assertEquals("", firstFileDescriptor["owner"])
}

@Test
fun parseNullInputStream() {
`when`(exitInfo.traceInputStream).thenReturn(null)
val threads = mutableListOf<Thread>()
TombstoneParser(logger).parse(exitInfo) {
threads.add(it)
}
val fileDescriptors = ArrayList<Map<String, Any>>()
TombstoneParser(logger).parse(exitInfo, true, { thread ->
threads.add(thread)
}, { fd, path, owner ->
fileDescriptors.add(
mapOf(
"fd" to fd,
"path" to path,
"owner" to owner,
)
)
})
verify(exitInfo, times(1)).traceInputStream
assertEquals(0, threads.size)
assertEquals(0, fileDescriptors.size)
}

@Test
fun parseInvalidInputStream() {
val junkData = ByteArray(128) { it.toByte() }
`when`(exitInfo.traceInputStream).thenReturn(junkData.inputStream())
val threads = mutableListOf<Thread>()
TombstoneParser(logger).parse(exitInfo) {
threads.add(it)
}

val fileDescriptors = ArrayList<Map<String, Any>>()
TombstoneParser(logger).parse(exitInfo, true, { thread ->
threads.add(thread)
}, { fd, path, owner ->
fileDescriptors.add(
mapOf(
"fd" to fd,
"path" to path,
"owner" to owner,
)
)
})
verify(exitInfo, times(1)).traceInputStream
assertEquals(0, threads.size)
assertEquals(0, fileDescriptors.size)
}
}

0 comments on commit fa51e42

Please sign in to comment.