Skip to content

Commit d0753d4

Browse files
committed
Rebuild profiling request for every profiling start
1 parent 4f0483e commit d0753d4

File tree

2 files changed

+67
-12
lines changed

2 files changed

+67
-12
lines changed

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

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import android.os.Build
1111
import android.os.CancellationSignal
1212
import android.os.ProfilingResult
1313
import androidx.annotation.RequiresApi
14+
import androidx.core.os.ProfilingRequest
1415
import androidx.core.os.StackSamplingRequestBuilder
1516
import androidx.core.os.requestProfiling
1617
import com.datadog.android.api.InternalLogger
@@ -38,10 +39,7 @@ internal class PerfettoProfiler(
3839
private val profilingExecutor: ExecutorService = Executors.newSingleThreadExecutor()
3940
) : Profiler {
4041

41-
private val requestBuilder: StackSamplingRequestBuilder
42-
43-
private val stopSignal = CancellationSignal()
44-
42+
private var stopSignal: CancellationSignal? = null
4543
private val resultCallback: Consumer<ProfilingResult>
4644

4745
// This flag represents which instance of this class is working for.
@@ -55,12 +53,6 @@ internal class PerfettoProfiler(
5553
private val callbackMap: MutableMap<String, ProfilerCallback> = ConcurrentHashMap()
5654

5755
init {
58-
requestBuilder = StackSamplingRequestBuilder()
59-
.setCancellationSignal(stopSignal)
60-
.setTag(PROFILING_TAG_APPLICATION_LAUNCH)
61-
.setSamplingFrequencyHz(PROFILING_SAMPLING_RATE)
62-
.setBufferSizeKb(BUFFER_SIZE_KB)
63-
.setDurationMs(PROFILING_MAX_DURATION_MS)
6456

6557
resultCallback = Consumer<ProfilingResult> { result ->
6658
val endTime = timeProvider.getDeviceTimestamp()
@@ -80,6 +72,19 @@ internal class PerfettoProfiler(
8072
}
8173
}
8274

75+
private fun buildStackSamplingRequest(): ProfilingRequest {
76+
return CancellationSignal().let {
77+
this.stopSignal = it
78+
StackSamplingRequestBuilder()
79+
.setCancellationSignal(it)
80+
.setTag(PROFILING_TAG_APPLICATION_LAUNCH)
81+
.setSamplingFrequencyHz(PROFILING_SAMPLING_RATE)
82+
.setBufferSizeKb(BUFFER_SIZE_KB)
83+
.setDurationMs(PROFILING_MAX_DURATION_MS)
84+
.build()
85+
}
86+
}
87+
8388
private fun notifyAllCallbacks(result: PerfettoResult) {
8489
callbackMap.filter { runningInstances.get().contains(it.key) }.forEach { callback ->
8590
callback.value.onSuccess(result)
@@ -92,13 +97,19 @@ internal class PerfettoProfiler(
9297
if (runningInstances.compareAndSet(emptySet(), sdkInstanceNames)) {
9398
sendProfilingStartTelemetry()
9499
profilingStartTime = timeProvider.getDeviceTimestamp()
95-
requestProfiling(appContext, requestBuilder.build(), profilingExecutor, resultCallback)
100+
requestProfiling(
101+
appContext,
102+
buildStackSamplingRequest(),
103+
profilingExecutor,
104+
resultCallback
105+
)
96106
}
97107
}
98108

99109
override fun stop(sdkInstanceName: String) {
100110
if (runningInstances.get().contains(sdkInstanceName)) {
101-
stopSignal.cancel()
111+
stopSignal?.cancel()
112+
stopSignal = null
102113
}
103114
}
104115

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

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import org.mockito.kotlin.any
3030
import org.mockito.kotlin.argumentCaptor
3131
import org.mockito.kotlin.doReturn
3232
import org.mockito.kotlin.eq
33+
import org.mockito.kotlin.inOrder
3334
import org.mockito.kotlin.isNull
3435
import org.mockito.kotlin.mock
3536
import org.mockito.kotlin.verify
@@ -395,6 +396,49 @@ class PerfettoProfilerTest {
395396
verifyNoInteractions(mockProfilerCallback)
396397
}
397398

399+
@Test
400+
fun `M not have the same stop signal W start&stop multiple times`() {
401+
val inOrder = inOrder(mockService)
402+
403+
// When
404+
// First request
405+
testedProfiler.start(mockContext, setOf(fakeInstanceName))
406+
val firstStopSignalCaptor = argumentCaptor<CancellationSignal>()
407+
testedProfiler.stop(fakeInstanceName)
408+
409+
// Then
410+
inOrder.verify(mockService).requestProfiling(
411+
eq(ProfilingManager.PROFILING_TYPE_STACK_SAMPLING),
412+
any<Bundle>(),
413+
any<String>(),
414+
firstStopSignalCaptor.capture(),
415+
any(),
416+
callbackCaptor.capture()
417+
)
418+
419+
val successResult = mock<ProfilingResult> {
420+
on { errorCode } doReturn ProfilingResult.ERROR_NONE
421+
on { resultFilePath } doReturn fakePath
422+
}
423+
callbackCaptor.firstValue.accept(successResult)
424+
val captor = argumentCaptor<PerfettoResult>()
425+
verify(mockProfilerCallback).onSuccess(captor.capture())
426+
427+
// Second request
428+
val secondStopSignalCaptor = argumentCaptor<CancellationSignal>()
429+
testedProfiler.start(mockContext, setOf(fakeInstanceName))
430+
inOrder.verify(mockService).requestProfiling(
431+
eq(ProfilingManager.PROFILING_TYPE_STACK_SAMPLING),
432+
any<Bundle>(),
433+
any<String>(),
434+
secondStopSignalCaptor.capture(),
435+
any(),
436+
callbackCaptor.capture()
437+
)
438+
439+
assertThat(firstStopSignalCaptor.firstValue).isNotSameAs(secondStopSignalCaptor.firstValue)
440+
}
441+
398442
private class StubTimeProvider : TimeProvider {
399443
var startTime: Long = 0L
400444
var endTime: Long = 0L

0 commit comments

Comments
 (0)