Skip to content

Commit bd29d30

Browse files
committed
RUM-18142: Add setAnrTriggerEnabled opt-out API for ANR-triggered profiling
1 parent 25d5fef commit bd29d30

12 files changed

Lines changed: 135 additions & 7 deletions

File tree

features/dd-sdk-android-profiling/api/apiSurface

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ data class com.datadog.android.profiling.ProfilingConfiguration
1414
fun setApplicationLaunchSampleRate(Float): Builder
1515
fun setContinuousSampleRate(Float): Builder
1616
fun useCustomEndpoint(String): Builder
17+
fun setAnrTriggerEnabled(Boolean): Builder
1718
fun build(): ProfilingConfiguration
1819
companion object
1920
val DEFAULT: ProfilingConfiguration

features/dd-sdk-android-profiling/api/dd-sdk-android-profiling.api

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ public final class com/datadog/android/profiling/Profiling {
2323

2424
public final class com/datadog/android/profiling/ProfilingConfiguration {
2525
public static final field Companion Lcom/datadog/android/profiling/ProfilingConfiguration$Companion;
26-
public final fun copy (Ljava/lang/String;FF)Lcom/datadog/android/profiling/ProfilingConfiguration;
27-
public static synthetic fun copy$default (Lcom/datadog/android/profiling/ProfilingConfiguration;Ljava/lang/String;FFILjava/lang/Object;)Lcom/datadog/android/profiling/ProfilingConfiguration;
26+
public final fun copy (Ljava/lang/String;FFZ)Lcom/datadog/android/profiling/ProfilingConfiguration;
27+
public static synthetic fun copy$default (Lcom/datadog/android/profiling/ProfilingConfiguration;Ljava/lang/String;FFZILjava/lang/Object;)Lcom/datadog/android/profiling/ProfilingConfiguration;
2828
public fun equals (Ljava/lang/Object;)Z
2929
public fun hashCode ()I
3030
public fun toString ()Ljava/lang/String;
@@ -33,6 +33,7 @@ public final class com/datadog/android/profiling/ProfilingConfiguration {
3333
public final class com/datadog/android/profiling/ProfilingConfiguration$Builder {
3434
public fun <init> ()V
3535
public final fun build ()Lcom/datadog/android/profiling/ProfilingConfiguration;
36+
public final fun setAnrTriggerEnabled (Z)Lcom/datadog/android/profiling/ProfilingConfiguration$Builder;
3637
public final fun setApplicationLaunchSampleRate (F)Lcom/datadog/android/profiling/ProfilingConfiguration$Builder;
3738
public final fun setContinuousSampleRate (F)Lcom/datadog/android/profiling/ProfilingConfiguration$Builder;
3839
public final fun useCustomEndpoint (Ljava/lang/String;)Lcom/datadog/android/profiling/ProfilingConfiguration$Builder;

features/dd-sdk-android-profiling/src/main/java/com/datadog/android/profiling/ProfilingConfiguration.kt

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
package com.datadog.android.profiling
88

9+
import android.os.ProfilingTrigger
910
import androidx.annotation.FloatRange
1011

1112
/**
@@ -15,7 +16,8 @@ import androidx.annotation.FloatRange
1516
data class ProfilingConfiguration internal constructor(
1617
internal val customEndpointUrl: String?,
1718
internal val applicationLaunchSampleRate: Float,
18-
internal val continuousSampleRate: Float
19+
internal val continuousSampleRate: Float,
20+
internal val anrTriggerEnabled: Boolean = DEFAULT_ANR_TRIGGER_ENABLED
1921
) {
2022

2123
/**
@@ -26,6 +28,7 @@ data class ProfilingConfiguration internal constructor(
2628
private var customEndpointUrl: String? = null
2729
private var applicationLaunchSampleRate: Float = DEFAULT_APPLICATION_LAUNCH_SAMPLE_RATE
2830
private var continuousSampleRate: Float = DEFAULT_CONTINUOUS_SAMPLE_RATE
31+
private var anrTriggerEnabled: Boolean = DEFAULT_ANR_TRIGGER_ENABLED
2932

3033
/**
3134
* Sets the sampling rate for Application Launch profiling. It will be applied on the next application launch.
@@ -65,14 +68,28 @@ data class ProfilingConfiguration internal constructor(
6568
return this
6669
}
6770

71+
/**
72+
* Enables or disables the ANR triggered profiling.
73+
*
74+
* When enabled, the SDK registers [ProfilingTrigger.TRIGGER_TYPE_ANR] so that a
75+
* profile is captured automatically when an ANR occurs.
76+
*
77+
* @param enabled `true` to enable ANR-triggered profiling (default), `false` to disable it.
78+
*/
79+
fun setAnrTriggerEnabled(enabled: Boolean): Builder {
80+
this.anrTriggerEnabled = enabled
81+
return this
82+
}
83+
6884
/**
6985
* Builds a [ProfilingConfiguration] based on the current state of this Builder.
7086
*/
7187
fun build(): ProfilingConfiguration {
7288
return ProfilingConfiguration(
7389
customEndpointUrl = customEndpointUrl,
7490
applicationLaunchSampleRate = applicationLaunchSampleRate,
75-
continuousSampleRate = continuousSampleRate
91+
continuousSampleRate = continuousSampleRate,
92+
anrTriggerEnabled = anrTriggerEnabled
7693
)
7794
}
7895
}
@@ -86,6 +103,12 @@ data class ProfilingConfiguration internal constructor(
86103
*/
87104
internal const val DEFAULT_CONTINUOUS_SAMPLE_RATE: Float = 15f
88105

106+
/**
107+
* ANR-triggered profiling is enabled by default to preserve the existing behavior,
108+
* making this an opt-out capability.
109+
*/
110+
internal const val DEFAULT_ANR_TRIGGER_ENABLED: Boolean = true
111+
89112
/**
90113
* A default configuration for the Profiling feature.
91114
*/

features/dd-sdk-android-profiling/src/main/java/com/datadog/android/profiling/internal/NoOpProfiler.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ internal class NoOpProfiler : Profiler {
3939

4040
override fun unregisterProfilingCallback(appContext: Context) = Unit
4141

42+
override fun setAnrTriggerEnabled(enabled: Boolean) = Unit
43+
4244
override fun setExtendLaunchSession(extend: Boolean) = Unit
4345

4446
override fun resolveProfilingPackageVersionCode(appContext: Context) = Unit

features/dd-sdk-android-profiling/src/main/java/com/datadog/android/profiling/internal/Profiler.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ internal interface Profiler {
3434

3535
fun unregisterProfilingCallback(appContext: Context)
3636

37+
fun setAnrTriggerEnabled(enabled: Boolean)
38+
3739
/**
3840
* Controls whether an app launch profiling session should extend past the 10-second
3941
* TTID threshold. Set to `true` when continuous profiling is enabled for the session

features/dd-sdk-android-profiling/src/main/java/com/datadog/android/profiling/internal/ProfilingFeature.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ internal class ProfilingFeature(
9595
this.timeProvider.delegate = sdkCore.timeProvider
9696
resolveProfilingPackageVersionCode(appContext)
9797
this.internalLogger = sdkCore.internalLogger
98+
setAnrTriggerEnabled(configuration.anrTriggerEnabled)
9899
registerProfilingCallback(appContext, this@ProfilingFeature)
99100
}
100101
ProfilingStorage.setSampleRate(appContext, configuration.applicationLaunchSampleRate)

features/dd-sdk-android-profiling/src/main/java/com/datadog/android/profiling/internal/perfetto/PerfettoProfiler.kt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ internal class PerfettoProfiler(
9696
profilingTelemetry.internalLogger = value
9797
}
9898

99+
@Volatile
100+
internal var anrTriggerEnabled: Boolean = true
101+
99102
internal val anrListener = AnrListener { event ->
100103
callback?.onAnrDetected(event)
101104
}
@@ -253,7 +256,7 @@ internal class PerfettoProfiler(
253256
) {
254257
synchronized(this) {
255258
this.callback = callback
256-
if (buildSdkVersionProvider.isAtLeastBaklava) {
259+
if (buildSdkVersionProvider.isAtLeastBaklava && anrTriggerEnabled) {
257260
anrTriggerRegistrar.register(appContext, anrListener)
258261
}
259262
}
@@ -262,7 +265,7 @@ internal class PerfettoProfiler(
262265
override fun unregisterProfilingCallback(appContext: Context) {
263266
synchronized(this) {
264267
callback = null
265-
if (buildSdkVersionProvider.isAtLeastBaklava) {
268+
if (buildSdkVersionProvider.isAtLeastBaklava && anrTriggerEnabled) {
266269
anrTriggerRegistrar.unregister(appContext)
267270
}
268271
}
@@ -272,6 +275,10 @@ internal class PerfettoProfiler(
272275
this.extendLaunchSession = extend
273276
}
274277

278+
override fun setAnrTriggerEnabled(enabled: Boolean) {
279+
this.anrTriggerEnabled = enabled
280+
}
281+
275282
override fun resolveProfilingPackageVersionCode(appContext: Context) {
276283
profilingPackageVersionCode(appContext)
277284
}

features/dd-sdk-android-profiling/src/test/kotlin/com/datadog/android/profiling/ProfilingFeatureTest.kt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,32 @@ internal class ProfilingFeatureTest {
477477
verify(mockSdkCore).setContextUpdateReceiver(testedFeature)
478478
}
479479

480+
@Test
481+
fun `M propagate ANR trigger enabled flag W onInitialize()`() {
482+
// Given
483+
val config = fakeConfiguration.copy(anrTriggerEnabled = false)
484+
testedFeature = ProfilingFeature(mockSdkCore, config, mockProfiler)
485+
486+
// When
487+
testedFeature.onInitialize(mockContext)
488+
489+
// Then
490+
verify(mockProfiler).setAnrTriggerEnabled(false)
491+
}
492+
493+
@Test
494+
fun `M propagate ANR trigger enabled flag W onInitialize {enabled}`() {
495+
// Given
496+
val config = fakeConfiguration.copy(anrTriggerEnabled = true)
497+
testedFeature = ProfilingFeature(mockSdkCore, config, mockProfiler)
498+
499+
// When
500+
testedFeature.onInitialize(mockContext)
501+
502+
// Then
503+
verify(mockProfiler).setAnrTriggerEnabled(true)
504+
}
505+
480506
@Test
481507
fun `M ignore context update W onContextUpdate {non-RUM feature}`(
482508
@StringForgery fakeOtherFeatureName: String,

features/dd-sdk-android-profiling/src/test/kotlin/com/datadog/android/profiling/forge/ProfilingConfigurationForgeryFactory.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ class ProfilingConfigurationForgeryFactory :
2020
continuousSampleRate = forge.aFloat(min = 0f, max = 100f),
2121
customEndpointUrl = forge.aNullable {
2222
aStringMatching("http(s?)://[a-z]+\\.com/\\w+")
23-
}
23+
},
24+
anrTriggerEnabled = forge.aBool()
2425
)
2526
}
2627
}

features/dd-sdk-android-profiling/src/test/kotlin/com/datadog/android/profiling/internal/PerfettoProfilerTest.kt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1083,6 +1083,32 @@ class PerfettoProfilerTest {
10831083
verify(mockAnrRegistrar, never()).register(any(), any())
10841084
}
10851085

1086+
@Test
1087+
fun `M not delegate to registrar W registerProfilingCallback {ANR trigger disabled}`() {
1088+
// Given
1089+
// Drop interactions recorded by the set-up call (which used the default enabled state).
1090+
reset(mockAnrRegistrar)
1091+
testedProfiler.setAnrTriggerEnabled(false)
1092+
1093+
// When
1094+
testedProfiler.registerProfilingCallback(mockContext, mockProfilerCallback)
1095+
1096+
// Then
1097+
verify(mockAnrRegistrar, never()).register(any(), any())
1098+
}
1099+
1100+
@Test
1101+
fun `M not delegate to registrar W unregisterProfilingCallback {ANR trigger disabled}`() {
1102+
// Given
1103+
testedProfiler.setAnrTriggerEnabled(false)
1104+
1105+
// When
1106+
testedProfiler.unregisterProfilingCallback(mockContext)
1107+
1108+
// Then
1109+
verify(mockAnrRegistrar, never()).unregister(any())
1110+
}
1111+
10861112
@Test
10871113
fun `M not delegate to registrar W unregisterProfilingCallback {SDK below BAKLAVA}`() {
10881114
// Given

0 commit comments

Comments
 (0)