Skip to content

Commit f69769c

Browse files
authored
Merge pull request #3076 from DataDog/yl/profiling/support-multi-request
Rebuild profiling request for every profiling start
2 parents 82de848 + b9ae78a commit f69769c

File tree

2 files changed

+62
-12
lines changed

2 files changed

+62
-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: 39 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,44 @@ class PerfettoProfilerTest {
395396
verifyNoInteractions(mockProfilerCallback)
396397
}
397398

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

0 commit comments

Comments
 (0)