Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change payload for Continuous Profiling (p2) #3695

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@
import io.sentry.IHub;
import io.sentry.ILogger;
import io.sentry.ISentryExecutorService;
import io.sentry.ProfileChunk;
import io.sentry.SentryLevel;
import io.sentry.SentryOptions;
import io.sentry.android.core.internal.util.SentryFrameMetricsCollector;
import io.sentry.protocol.SentryId;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Future;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
Expand All @@ -32,6 +37,9 @@ public class AndroidContinuousProfiler implements IContinuousProfiler {
private boolean isRunning = false;
private @Nullable IHub hub;
private @Nullable Future<?> closeFuture;
private final @NotNull List<ProfileChunk.Builder> payloadBuilders = new ArrayList<>();
private @NotNull SentryId profilerId = SentryId.EMPTY_ID;
private @NotNull SentryId chunkId = SentryId.EMPTY_ID;

public AndroidContinuousProfiler(
final @NotNull BuildInfoProvider buildInfoProvider,
Expand Down Expand Up @@ -105,8 +113,17 @@ public synchronized void start() {
if (startData == null) {
return;
}

isRunning = true;

if (profilerId == SentryId.EMPTY_ID) {
profilerId = new SentryId();
}

if (chunkId == SentryId.EMPTY_ID) {
chunkId = new SentryId();
}

closeFuture = executorService.schedule(() -> stop(true), MAX_CHUNK_DURATION_MILLIS);
}

Expand Down Expand Up @@ -138,14 +155,29 @@ private synchronized void stop(final boolean restartProfiler) {
return;
}

// The hub can be null if the profiler is started before the SDK is initialized (app start
// profiling), meaning there's no hub to send the chunks. In that case, we store the data in a
// list and send it when the next chunk is finished.
synchronized (payloadBuilders) {
payloadBuilders.add(
new ProfileChunk.Builder(
profilerId, chunkId, endData.measurementsMap, endData.traceFile));
}

isRunning = false;
// A chunk is finished. Next chunk will have a different id.
chunkId = SentryId.EMPTY_ID;

// todo schedule capture profile chunk envelope
if (hub != null) {
sendChunks(hub, hub.getOptions());
}

if (restartProfiler) {
logger.log(SentryLevel.DEBUG, "Profile chunk finished. Starting a new one.");
start();
} else {
// When the profiler is stopped manually, we have to reset its id
profilerId = SentryId.EMPTY_ID;
logger.log(SentryLevel.DEBUG, "Profile chunk finished.");
}
}
Expand All @@ -157,6 +189,28 @@ public synchronized void close() {
stop();
}

private void sendChunks(final @NotNull IHub hub, final @NotNull SentryOptions options) {
try {
options
.getExecutorService()
.submit(
() -> {
final ArrayList<ProfileChunk> payloads = new ArrayList<>(payloadBuilders.size());
synchronized (payloadBuilders) {
for (ProfileChunk.Builder builder : payloadBuilders) {
payloads.add(builder.build(options));
}
payloadBuilders.clear();
}
for (ProfileChunk payload : payloads) {
hub.captureProfileChunk(payload);
}
});
} catch (Throwable e) {
options.getLogger().log(SentryLevel.DEBUG, "Failed to send profile chunks.", e);
}
}

@Override
public boolean isRunning() {
return isRunning;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import io.sentry.IPerformanceSnapshotCollector;
import io.sentry.PerformanceCollectionData;
import io.sentry.SentryLevel;
import io.sentry.SentryNanotimeDate;
import io.sentry.util.FileUtils;
import io.sentry.util.Objects;
import java.io.File;
Expand Down Expand Up @@ -85,7 +86,9 @@ public void collect(final @NotNull PerformanceCollectionData performanceCollecti

CpuCollectionData cpuData =
new CpuCollectionData(
System.currentTimeMillis(), (cpuUsagePercentage / (double) numCores) * 100.0);
System.currentTimeMillis(),
(cpuUsagePercentage / (double) numCores) * 100.0,
new SentryNanotimeDate());

performanceCollectionData.addCpuData(cpuData);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import io.sentry.IPerformanceSnapshotCollector;
import io.sentry.MemoryCollectionData;
import io.sentry.PerformanceCollectionData;
import io.sentry.SentryNanotimeDate;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;

Expand All @@ -18,7 +19,8 @@ public void collect(final @NotNull PerformanceCollectionData performanceCollecti
long now = System.currentTimeMillis();
long usedMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
long usedNativeMemory = Debug.getNativeHeapSize() - Debug.getNativeHeapFreeSize();
MemoryCollectionData memoryData = new MemoryCollectionData(now, usedMemory, usedNativeMemory);
MemoryCollectionData memoryData =
new MemoryCollectionData(now, usedMemory, usedNativeMemory, new SentryNanotimeDate());
performanceCollectionData.addMemoryData(memoryData);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
import io.sentry.ISentryExecutorService;
import io.sentry.MemoryCollectionData;
import io.sentry.PerformanceCollectionData;
import io.sentry.SentryDate;
import io.sentry.SentryLevel;
import io.sentry.SentryNanotimeDate;
import io.sentry.android.core.internal.util.SentryFrameMetricsCollector;
import io.sentry.profilemeasurements.ProfileMeasurement;
import io.sentry.profilemeasurements.ProfileMeasurementValue;
Expand Down Expand Up @@ -158,6 +160,7 @@ public void onFrameMetricCollected(
// profileStartNanos is calculated through SystemClock.elapsedRealtimeNanos(),
// but frameEndNanos uses System.nanotime(), so we convert it to get the timestamp
// relative to profileStartNanos
final SentryDate timestamp = new SentryNanotimeDate();
final long frameTimestampRelativeNanos =
frameEndNanos
- System.nanoTime()
Expand All @@ -171,15 +174,18 @@ public void onFrameMetricCollected(
}
if (isFrozen) {
frozenFrameRenderMeasurements.addLast(
new ProfileMeasurementValue(frameTimestampRelativeNanos, durationNanos));
new ProfileMeasurementValue(
frameTimestampRelativeNanos, durationNanos, timestamp));
} else if (isSlow) {
slowFrameRenderMeasurements.addLast(
new ProfileMeasurementValue(frameTimestampRelativeNanos, durationNanos));
new ProfileMeasurementValue(
frameTimestampRelativeNanos, durationNanos, timestamp));
}
if (refreshRate != lastRefreshRate) {
lastRefreshRate = refreshRate;
screenFrameRateMeasurements.addLast(
new ProfileMeasurementValue(frameTimestampRelativeNanos, refreshRate));
new ProfileMeasurementValue(
frameTimestampRelativeNanos, refreshRate, timestamp));
}
}
});
Expand Down Expand Up @@ -326,19 +332,22 @@ private void putPerformanceCollectionDataInMeasurements(
cpuUsageMeasurements.add(
new ProfileMeasurementValue(
TimeUnit.MILLISECONDS.toNanos(cpuData.getTimestampMillis()) + timestampDiff,
cpuData.getCpuUsagePercentage()));
cpuData.getCpuUsagePercentage(),
cpuData.getTimestamp()));
}
if (memoryData != null && memoryData.getUsedHeapMemory() > -1) {
memoryUsageMeasurements.add(
new ProfileMeasurementValue(
TimeUnit.MILLISECONDS.toNanos(memoryData.getTimestampMillis()) + timestampDiff,
memoryData.getUsedHeapMemory()));
memoryData.getUsedHeapMemory(),
memoryData.getTimestamp()));
}
if (memoryData != null && memoryData.getUsedNativeMemory() > -1) {
nativeMemoryUsageMeasurements.add(
new ProfileMeasurementValue(
TimeUnit.MILLISECONDS.toNanos(memoryData.getTimestampMillis()) + timestampDiff,
memoryData.getUsedNativeMemory()));
memoryData.getUsedNativeMemory(),
memoryData.getTimestamp()));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class AndroidCpuCollectorTest {
assertNotNull(cpuData)
assertNotEquals(0.0, cpuData.cpuUsagePercentage)
assertNotEquals(0, cpuData.timestampMillis)
assertNotEquals(0, cpuData.timestamp.nanoTimestamp())
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@ class AndroidMemoryCollectorTest {
assertEquals(usedNativeMemory, memoryData.usedNativeMemory)
assertEquals(usedMemory, memoryData.usedHeapMemory)
assertNotEquals(0, memoryData.timestampMillis)
assertNotEquals(0, memoryData.timestamp.nanoTimestamp())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import io.sentry.ILogger
import io.sentry.ISentryExecutorService
import io.sentry.MemoryCollectionData
import io.sentry.PerformanceCollectionData
import io.sentry.SentryDate
import io.sentry.SentryExecutorService
import io.sentry.SentryLevel
import io.sentry.android.core.internal.util.SentryFrameMetricsCollector
Expand Down Expand Up @@ -278,12 +279,14 @@ class AndroidProfilerTest {
val profiler = fixture.getSut()
val performanceCollectionData = ArrayList<PerformanceCollectionData>()
var singleData = PerformanceCollectionData()
singleData.addMemoryData(MemoryCollectionData(1, 2, 3))
singleData.addCpuData(CpuCollectionData(1, 1.4))
val t1 = mock<SentryDate>()
val t2 = mock<SentryDate>()
singleData.addMemoryData(MemoryCollectionData(1, 2, 3, t1))
singleData.addCpuData(CpuCollectionData(1, 1.4, t1))
performanceCollectionData.add(singleData)

singleData = PerformanceCollectionData()
singleData.addMemoryData(MemoryCollectionData(2, 3, 4))
singleData.addMemoryData(MemoryCollectionData(2, 3, 4, t2))
performanceCollectionData.add(singleData)

profiler.start()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -459,12 +459,12 @@ class AndroidTransactionProfilerTest {
val profiler = fixture.getSut(context)
val performanceCollectionData = ArrayList<PerformanceCollectionData>()
var singleData = PerformanceCollectionData()
singleData.addMemoryData(MemoryCollectionData(1, 2, 3))
singleData.addCpuData(CpuCollectionData(1, 1.4))
singleData.addMemoryData(MemoryCollectionData(1, 2, 3, mock()))
singleData.addCpuData(CpuCollectionData(1, 1.4, mock()))
performanceCollectionData.add(singleData)

singleData = PerformanceCollectionData()
singleData.addMemoryData(MemoryCollectionData(2, 3, 4))
singleData.addMemoryData(MemoryCollectionData(2, 3, 4, mock()))
performanceCollectionData.add(singleData)

profiler.start()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import io.sentry.Hint
import io.sentry.IMetricsAggregator
import io.sentry.IScope
import io.sentry.ISentryClient
import io.sentry.ProfileChunk
import io.sentry.ProfilingTraceData
import io.sentry.Sentry
import io.sentry.SentryEnvelope
Expand Down Expand Up @@ -177,6 +178,10 @@ class SessionTrackingIntegrationTest {
TODO("Not yet implemented")
}

override fun captureProfileChunk(profileChunk: ProfileChunk, scope: IScope?): SentryId {
TODO("Not yet implemented")
}

override fun captureCheckIn(checkIn: CheckIn, scope: IScope?, hint: Hint?): SentryId {
TODO("Not yet implemented")
}
Expand Down
Loading
Loading