diff --git a/features/dd-sdk-android-profiling/src/main/java/com/datadog/android/profiling/internal/perfetto/PerfettoProfiler.kt b/features/dd-sdk-android-profiling/src/main/java/com/datadog/android/profiling/internal/perfetto/PerfettoProfiler.kt index a6b0189396..18c70dbca1 100644 --- a/features/dd-sdk-android-profiling/src/main/java/com/datadog/android/profiling/internal/perfetto/PerfettoProfiler.kt +++ b/features/dd-sdk-android-profiling/src/main/java/com/datadog/android/profiling/internal/perfetto/PerfettoProfiler.kt @@ -77,6 +77,9 @@ internal class PerfettoProfiler( @Volatile private var profilingAppStartInfo: String? = null + @Volatile + private var profilingSamplingRateHz: Int = PROFILING_SAMPLING_RATE_APP_LAUNCH + @Volatile override var internalLogger: InternalLogger? = null set(value) { @@ -138,7 +141,7 @@ internal class PerfettoProfiler( resultCallbackDelayMs = resultCallbackDelayMs, stopReason = resolveStopReason(result.errorCode), bufferSizeKb = BUFFER_SIZE_KB, - samplingFrequencyHz = PROFILING_SAMPLING_RATE + samplingFrequencyHz = profilingSamplingRateHz ) ) } @@ -148,18 +151,28 @@ internal class PerfettoProfiler( startReason: ProfilingStartReason, durationMs: Int ): ProfilingRequest { + val samplingRateHz = getSamplingRateHz(startReason) + profilingSamplingRateHz = samplingRateHz return CancellationSignal().let { this.stopSignal = it StackSamplingRequestBuilder() .setCancellationSignal(it) .setTag(startReason.value) - .setSamplingFrequencyHz(PROFILING_SAMPLING_RATE) + .setSamplingFrequencyHz(samplingRateHz) .setBufferSizeKb(BUFFER_SIZE_KB) .setDurationMs(durationMs) .build() } } + private fun getSamplingRateHz(startReason: ProfilingStartReason): Int { + return if (startReason == ProfilingStartReason.APPLICATION_LAUNCH) { + PROFILING_SAMPLING_RATE_APP_LAUNCH + } else { + PROFILING_SAMPLING_RATE_CONTINUOUS + } + } + private fun notifyCallbacks(dispatch: ProfilerCallback.() -> Unit) { val running = runningInstances.get() callbackMap.forEach { (key, callback) -> @@ -291,9 +304,11 @@ internal class PerfettoProfiler( // increased or configurable if needed. private const val BUFFER_SIZE_KB = 5120 // 5MB - // Currently we give 201HZ frequency to balance the sampling accuracy and performance - // overhead also to avoid lockstep sampling, it can be updated or configurable if needed. - internal const val PROFILING_SAMPLING_RATE = 201 // 201Hz + // 201Hz for app launch: higher accuracy to capture startup behavior. + internal const val PROFILING_SAMPLING_RATE_APP_LAUNCH = 201 + + // 101Hz for continuous profiling: lower overhead for sustained background recording. + internal const val PROFILING_SAMPLING_RATE_CONTINUOUS = 101 // Re-exported from ProfilingTelemetry so external callers (e.g. content provider) // keep using the same property key when passing app-start info as an additional attribute. diff --git a/features/dd-sdk-android-profiling/src/test/kotlin/com/datadog/android/profiling/internal/PerfettoProfilerTest.kt b/features/dd-sdk-android-profiling/src/test/kotlin/com/datadog/android/profiling/internal/PerfettoProfilerTest.kt index d33a5f5d52..6509470dff 100644 --- a/features/dd-sdk-android-profiling/src/test/kotlin/com/datadog/android/profiling/internal/PerfettoProfilerTest.kt +++ b/features/dd-sdk-android-profiling/src/test/kotlin/com/datadog/android/profiling/internal/PerfettoProfilerTest.kt @@ -20,7 +20,8 @@ import com.datadog.android.profiling.forge.Configurator import com.datadog.android.profiling.internal.anr.AnrTriggerRegistrar import com.datadog.android.profiling.internal.perfetto.PerfettoProfiler import com.datadog.android.profiling.internal.perfetto.PerfettoProfiler.Companion.APP_LAUNCH_PROFILING_MAX_DURATION_MS -import com.datadog.android.profiling.internal.perfetto.PerfettoProfiler.Companion.PROFILING_SAMPLING_RATE +import com.datadog.android.profiling.internal.perfetto.PerfettoProfiler.Companion.PROFILING_SAMPLING_RATE_APP_LAUNCH +import com.datadog.android.profiling.internal.perfetto.PerfettoProfiler.Companion.PROFILING_SAMPLING_RATE_CONTINUOUS import com.datadog.android.profiling.internal.perfetto.PerfettoResult import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.annotation.Forgery @@ -237,7 +238,7 @@ class PerfettoProfilerTest { ), "profiling_config" to mapOf( "buffer_size" to 5120, - "sampling_frequency" to PROFILING_SAMPLING_RATE + "sampling_frequency" to PROFILING_SAMPLING_RATE_APP_LAUNCH ) ) verify(mockInternalLogger) @@ -299,7 +300,7 @@ class PerfettoProfilerTest { ), "profiling_config" to mapOf( "buffer_size" to 5120, - "sampling_frequency" to PROFILING_SAMPLING_RATE + "sampling_frequency" to PROFILING_SAMPLING_RATE_APP_LAUNCH ) ) verify(mockInternalLogger) @@ -362,7 +363,7 @@ class PerfettoProfilerTest { ), "profiling_config" to mapOf( "buffer_size" to 5120, - "sampling_frequency" to PROFILING_SAMPLING_RATE + "sampling_frequency" to PROFILING_SAMPLING_RATE_APP_LAUNCH ) ) verify(mockInternalLogger) @@ -766,7 +767,7 @@ class PerfettoProfilerTest { ), "profiling_config" to mapOf( "buffer_size" to 5120, - "sampling_frequency" to PROFILING_SAMPLING_RATE + "sampling_frequency" to PROFILING_SAMPLING_RATE_APP_LAUNCH ) ) verify(mockInternalLogger) @@ -811,6 +812,11 @@ class PerfettoProfilerTest { callbackCaptor.firstValue.accept(mockResult) val messageCaptor = argumentCaptor<() -> String>() + val expectedSamplingRate = if (startReason == ProfilingStartReason.APPLICATION_LAUNCH) { + PROFILING_SAMPLING_RATE_APP_LAUNCH + } else { + PROFILING_SAMPLING_RATE_CONTINUOUS + } val expectedProps = mapOf( "metric_type" to "profiling session", "profiling_session" to mapOf( @@ -825,7 +831,7 @@ class PerfettoProfilerTest { ), "profiling_config" to mapOf( "buffer_size" to 5120, - "sampling_frequency" to PROFILING_SAMPLING_RATE + "sampling_frequency" to expectedSamplingRate ) ) verify(mockInternalLogger) @@ -892,7 +898,7 @@ class PerfettoProfilerTest { ), "profiling_config" to mapOf( "buffer_size" to 5120, - "sampling_frequency" to PROFILING_SAMPLING_RATE + "sampling_frequency" to PROFILING_SAMPLING_RATE_APP_LAUNCH ) ) verify(mockInternalLogger) @@ -985,7 +991,7 @@ class PerfettoProfilerTest { ), "profiling_config" to mapOf( "buffer_size" to 5120, - "sampling_frequency" to PROFILING_SAMPLING_RATE + "sampling_frequency" to PROFILING_SAMPLING_RATE_CONTINUOUS ) ) verify(mockInternalLogger).logMetric(