Skip to content

Commit

Permalink
Merge 997f4e3 into 544da32
Browse files Browse the repository at this point in the history
  • Loading branch information
stefanosiano committed Apr 28, 2023
2 parents 544da32 + 997f4e3 commit 06cb003
Show file tree
Hide file tree
Showing 26 changed files with 383 additions and 35 deletions.
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# Changelog

## Unreleased
### Unreleased

### Fixes
## Fixes

- Fix crash on double SDK init ([#2679](https://github.com/getsentry/sentry-java/pull/2679))
- Track a ttfd span per Activity ([#2673](https://github.com/getsentry/sentry-java/pull/2673))

## 6.18.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand Down Expand Up @@ -250,11 +251,20 @@ private void startTracing(final @NotNull Activity activity) {
final @NotNull ISpan ttfdSpan =
transaction.startChild(
TTFD_OP, getTtfdDesc(activityName), ttidStartTime, Instrumenter.SENTRY);
ttfdSpanMap.put(activity, ttfdSpan);
ttfdAutoCloseFuture =
options
.getExecutorService()
.schedule(() -> finishExceededTtfdSpan(ttfdSpan, ttidSpan), TTFD_TIMEOUT_MILLIS);
try {
ttfdSpanMap.put(activity, ttfdSpan);
ttfdAutoCloseFuture =
options
.getExecutorService()
.schedule(() -> finishExceededTtfdSpan(ttfdSpan, ttidSpan), TTFD_TIMEOUT_MILLIS);
} catch (RejectedExecutionException e) {
options
.getLogger()
.log(
SentryLevel.ERROR,
"Failed to call the executor. Time to full display span will not be finished automatically. Did you call Sentry.close()?",
e);
}
}

// lets bind to the scope so other integrations can pick it up
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;

final class AndroidTransactionProfiler implements ITransactionProfiler {

Expand Down Expand Up @@ -77,6 +79,8 @@ final class AndroidTransactionProfiler implements ITransactionProfiler {
new ArrayDeque<>();
private final @NotNull Map<String, ProfileMeasurement> measurementsMap = new HashMap<>();

private @Nullable ITransaction currentTransaction = null;

public AndroidTransactionProfiler(
final @NotNull Context context,
final @NotNull SentryAndroidOptions sentryAndroidOptions,
Expand Down Expand Up @@ -140,7 +144,16 @@ private void init() {

@Override
public synchronized void onTransactionStart(final @NotNull ITransaction transaction) {
options.getExecutorService().submit(() -> onTransactionStartSafe(transaction));
try {
options.getExecutorService().submit(() -> onTransactionStartSafe(transaction));
} catch (RejectedExecutionException e) {
options
.getLogger()
.log(
SentryLevel.ERROR,
"Failed to call the executor. Profiling will not be started. Did you call Sentry.close()?",
e);
}
}

@SuppressLint("NewApi")
Expand Down Expand Up @@ -235,13 +248,24 @@ public void onFrameMetricCollected(
}
});

currentTransaction = transaction;

// We stop profiling after a timeout to avoid huge profiles to be sent
scheduledFinish =
options
.getExecutorService()
.schedule(
() -> timedOutProfilingData = onTransactionFinish(transaction, true, null),
PROFILING_TIMEOUT_MILLIS);
try {
scheduledFinish =
options
.getExecutorService()
.schedule(
() -> timedOutProfilingData = onTransactionFinish(transaction, true, null),
PROFILING_TIMEOUT_MILLIS);
} catch (RejectedExecutionException e) {
options
.getLogger()
.log(
SentryLevel.ERROR,
"Failed to call the executor. Profiling will not be automatically finished. Did you call Sentry.close()?",
e);
}

transactionStartNanos = SystemClock.elapsedRealtimeNanos();
profileStartCpuMillis = Process.getElapsedCpuTime();
Expand All @@ -261,10 +285,19 @@ public void onFrameMetricCollected(
.getExecutorService()
.submit(() -> onTransactionFinish(transaction, false, performanceCollectionData))
.get();
} catch (ExecutionException e) {
options.getLogger().log(SentryLevel.ERROR, "Error finishing profiling: ", e);
} catch (InterruptedException e) {
} catch (ExecutionException | InterruptedException e) {
options.getLogger().log(SentryLevel.ERROR, "Error finishing profiling: ", e);
// We stop profiling even on exceptions, so profiles don't run indefinitely
onTransactionFinish(transaction, false, null);
} catch (RejectedExecutionException e) {
options
.getLogger()
.log(
SentryLevel.ERROR,
"Failed to call the executor. Profiling could not be finished. Did you call Sentry.close()?",
e);
// We stop profiling even on exceptions, so profiles don't run indefinitely
onTransactionFinish(transaction, false, null);
}
return null;
}
Expand Down Expand Up @@ -349,6 +382,7 @@ public void onFrameMetricCollected(
currentProfilingTransactionData = null;
// We clear the counter in case of a timeout
transactionsCounter = 0;
currentTransaction = null;

if (scheduledFinish != null) {
scheduledFinish.cancel(true);
Expand Down Expand Up @@ -483,6 +517,19 @@ private void putPerformanceCollectionDataInMeasurements(
}
}

@Override
public void close() {
// we cancel any scheduled work
if (scheduledFinish != null) {
scheduledFinish.cancel(true);
scheduledFinish = null;
}
// we stop profiling
if (currentTransaction != null) {
onTransactionFinish(currentTransaction, true, null);
}
}

/**
* Get MemoryInfo object representing the memory state of the application.
*
Expand All @@ -503,4 +550,10 @@ private void putPerformanceCollectionDataInMeasurements(
return null;
}
}

@TestOnly
@Nullable
ITransaction getCurrentTransaction() {
return currentTransaction;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import io.sentry.SentryOptions;
import io.sentry.util.Objects;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.jetbrains.annotations.NotNull;
Expand Down Expand Up @@ -74,6 +75,13 @@ public void register(@NotNull IHub hub, @NotNull SentryOptions options) {
}
}
androidOptions.getLogger().log(SentryLevel.DEBUG, "SendCachedEnvelopeIntegration installed.");
} catch (RejectedExecutionException e) {
androidOptions
.getLogger()
.log(
SentryLevel.ERROR,
"Failed to call the executor. Cached events will not be sent. Did you call Sentry.close()?",
e);
} catch (Throwable e) {
androidOptions
.getLogger()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1100,6 +1100,7 @@ class ActivityLifecycleIntegrationTest {
return FutureTask {}
}
override fun close(timeoutMillis: Long) {}
override fun isClosed() = false
}
fixture.options.tracesSampleRate = 1.0
fixture.options.isEnableTimeToFullDisplayTracing = true
Expand Down Expand Up @@ -1326,6 +1327,7 @@ class ActivityLifecycleIntegrationTest {
return FutureTask {}
}
override fun close(timeoutMillis: Long) {}
override fun isClosed() = false
}
fixture.options.tracesSampleRate = 1.0
fixture.options.isEnableTimeToFullDisplayTracing = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import io.sentry.TransactionContext
import io.sentry.android.core.internal.util.SentryFrameMetricsCollector
import io.sentry.profilemeasurements.ProfileMeasurement
import io.sentry.test.getCtor
import io.sentry.test.getProperty
import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
Expand Down Expand Up @@ -75,6 +76,7 @@ class AndroidTransactionProfilerTest {
return FutureTask {}
}
override fun close(timeoutMillis: Long) {}
override fun isClosed() = false
}

val options = spy(SentryAndroidOptions()).apply {
Expand Down Expand Up @@ -150,8 +152,11 @@ class AndroidTransactionProfilerTest {
@Test
fun `profiler profiles current transaction`() {
val profiler = fixture.getSut(context)
assertNull(profiler.currentTransaction)
profiler.onTransactionStart(fixture.transaction1)
assertNotNull(profiler.currentTransaction)
val profilingTraceData = profiler.onTransactionFinish(fixture.transaction1, null)
assertNull(profiler.currentTransaction)

assertNotNull(profilingTraceData)
assertEquals(profilingTraceData.transactionId, fixture.transaction1.eventId.toString())
Expand Down Expand Up @@ -401,4 +406,22 @@ class AndroidTransactionProfilerTest {
data.measurementsMap[ProfileMeasurement.ID_MEMORY_NATIVE_FOOTPRINT]!!.values.map { it.value }
)
}

@Test
fun `profiler stops profiling, clear current transaction and scheduled job on close`() {
val profiler = fixture.getSut(context)
profiler.onTransactionStart(fixture.transaction1)
assertNotNull(profiler.currentTransaction)

profiler.close()
assertNull(profiler.currentTransaction)

// The timeout scheduled job should be cleared
val scheduledJob = profiler.getProperty<Future<*>?>("scheduledFinish")
assertNull(scheduledJob)

// Calling transaction finish returns null, as the profiler was stopped
val profilingTraceData = profiler.onTransactionFinish(fixture.transaction1, null)
assertNull(profilingTraceData)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -115,5 +115,7 @@ class SentryAndroidOptionsTest {
transaction: ITransaction,
performanceCollectionData: List<PerformanceCollectionData>?
): ProfilingTraceData? = null

override fun close() {}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package io.sentry.uitest.android

import androidx.lifecycle.Lifecycle
import androidx.test.core.app.launchActivity
import androidx.test.ext.junit.runners.AndroidJUnit4
import io.sentry.ProfilingTraceData
import io.sentry.Sentry
import io.sentry.android.core.SentryAndroidOptions
import io.sentry.assertEnvelopeItem
import io.sentry.protocol.SentryTransaction
import org.junit.runner.RunWith
import kotlin.test.Test
import kotlin.test.assertEquals

@RunWith(AndroidJUnit4::class)
class SdkInitTests : BaseUiTest() {

@Test
fun doubleInitDoesNotThrow() {
initSentry(false) { options: SentryAndroidOptions ->
options.tracesSampleRate = 1.0
options.profilesSampleRate = 1.0
}
val transaction = Sentry.startTransaction("e2etests", "testInit")
val sampleScenario = launchActivity<EmptyActivity>()
initSentry(false) { options: SentryAndroidOptions ->
options.tracesSampleRate = 1.0
options.profilesSampleRate = 1.0
}
transaction.finish()
sampleScenario.moveToState(Lifecycle.State.DESTROYED)
val transaction2 = Sentry.startTransaction("e2etests", "testInit")
transaction2.finish()
}

@Test
fun doubleInitWithSameOptionsDoesNotThrow() {
val options = SentryAndroidOptions()

initSentry(true) {
it.tracesSampleRate = 1.0
it.profilesSampleRate = 1.0
// We use the same executorService before and after closing the SDK
it.executorService = options.executorService
it.isDebug = true
}
val transaction = Sentry.startTransaction("e2etests", "testInit")
val sampleScenario = launchActivity<EmptyActivity>()
initSentry(true) {
it.tracesSampleRate = 1.0
it.profilesSampleRate = 1.0
// We use the same executorService before and after closing the SDK
it.executorService = options.executorService
it.isDebug = true
}
relayIdlingResource.increment()
transaction.finish()
sampleScenario.moveToState(Lifecycle.State.DESTROYED)
val transaction2 = Sentry.startTransaction("e2etests2", "testInit")
transaction2.finish()

relay.assert {
findEnvelope {
assertEnvelopeItem<SentryTransaction>(it.items.toList()).transaction == "e2etests2"
}.assert {
val transactionItem: SentryTransaction = it.assertItem()
// Profiling uses executorService, so if the executorService is shutdown it would fail
val profilingTraceData: ProfilingTraceData = it.assertItem()
it.assertNoOtherItems()
assertEquals("e2etests2", transactionItem.transaction)
assertEquals("e2etests2", profilingTraceData.transactionName)
}
assertNoOtherEnvelopes()
assertNoOtherRequests()
}
}
}
Loading

0 comments on commit 06cb003

Please sign in to comment.