diff --git a/CHANGELOG.md b/CHANGELOG.md index 598bee0eee..8f2234f1b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Features - Add `lock` attribute to the `SentryStackFrame` protocol to better highlight offending frames in the UI ([#2761](https://github.com/getsentry/sentry-java/pull/2761)) +- Enrich database spans with blocked main thread info ([#2760](https://github.com/getsentry/sentry-java/pull/2760)) ## 6.21.0 diff --git a/sentry-android-sqlite/src/main/java/io/sentry/android/sqlite/SQLiteSpanManager.kt b/sentry-android-sqlite/src/main/java/io/sentry/android/sqlite/SQLiteSpanManager.kt index e91c35022a..64f45c8339 100644 --- a/sentry-android-sqlite/src/main/java/io/sentry/android/sqlite/SQLiteSpanManager.kt +++ b/sentry-android-sqlite/src/main/java/io/sentry/android/sqlite/SQLiteSpanManager.kt @@ -4,11 +4,13 @@ import android.database.SQLException import io.sentry.HubAdapter import io.sentry.IHub import io.sentry.SentryIntegrationPackageStorage +import io.sentry.SentryStackTraceFactory import io.sentry.SpanStatus internal class SQLiteSpanManager( private val hub: IHub = HubAdapter.getInstance() ) { + private val stackTraceFactory = SentryStackTraceFactory(hub.options) init { SentryIntegrationPackageStorage.getInstance().addIntegration("SQLite") @@ -34,7 +36,14 @@ internal class SQLiteSpanManager( span?.throwable = e throw e } finally { - span?.finish() + span?.apply { + val isMainThread: Boolean = hub.options.mainThreadChecker.isMainThread + setData("blocked_main_thread", isMainThread) + if (isMainThread) { + setData("call_stack", stackTraceFactory.inAppCallStack) + } + finish() + } } } } diff --git a/sentry-android-sqlite/src/test/java/io/sentry/android/sqlite/SQLiteSpanManagerTest.kt b/sentry-android-sqlite/src/test/java/io/sentry/android/sqlite/SQLiteSpanManagerTest.kt index 5df0d891f9..7ab11254e5 100644 --- a/sentry-android-sqlite/src/test/java/io/sentry/android/sqlite/SQLiteSpanManagerTest.kt +++ b/sentry-android-sqlite/src/test/java/io/sentry/android/sqlite/SQLiteSpanManagerTest.kt @@ -7,12 +7,15 @@ import io.sentry.SentryOptions import io.sentry.SentryTracer import io.sentry.SpanStatus import io.sentry.TransactionContext +import io.sentry.util.thread.IMainThreadChecker +import org.junit.Before import org.mockito.kotlin.mock import org.mockito.kotlin.whenever import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertNotNull +import kotlin.test.assertNull import kotlin.test.assertTrue class SQLiteSpanManagerTest { @@ -38,6 +41,11 @@ class SQLiteSpanManagerTest { private val fixture = Fixture() + @Before + fun setup() { + SentryIntegrationPackageStorage.getInstance().clearStorage() + } + @Test fun `add SQLite to the list of integrations`() { assertFalse(SentryIntegrationPackageStorage.getInstance().integrations.contains("SQLite")) @@ -81,4 +89,32 @@ class SQLiteSpanManagerTest { assertEquals(e, span.throwable) assertTrue(span.isFinished) } + + @Test + fun `when performSql runs in background blocked_main_thread is false and no stack trace is attached`() { + val sut = fixture.getSut() + + fixture.options.mainThreadChecker = mock() + whenever(fixture.options.mainThreadChecker.isMainThread).thenReturn(false) + + sut.performSql("sql") {} + val span = fixture.sentryTracer.children.first() + + assertFalse(span.getData("blocked_main_thread") as Boolean) + assertNull(span.getData("call_stack")) + } + + @Test + fun `when performSql runs in foreground blocked_main_thread is true and a stack trace is attached`() { + val sut = fixture.getSut() + + fixture.options.mainThreadChecker = mock() + whenever(fixture.options.mainThreadChecker.isMainThread).thenReturn(true) + + sut.performSql("sql") {} + val span = fixture.sentryTracer.children.first() + + assertTrue(span.getData("blocked_main_thread") as Boolean) + assertNotNull(span.getData("call_stack")) + } }