Skip to content

Commit 5ebb536

Browse files
authored
Instantiate continuous profiling v8 (p3) (#3725)
* added profile context to SentryTracer * removed isProfilingEnabled from AndroidContinuousProfiler, as it's useless * added continuous profiler to SentryOptions * added DefaultTransactionPerformanceCollector to AndroidContinuousProfiler * updated DefaultTransactionPerformanceCollector to work with string ids other than transactions * fixed ProfileChunk measurements being modifiable from other code * added thread id and name to SpanContext.data * added profiler_id to span data * close continuous profiler on scopes close * renamed TransactionPerformanceCollector to CompositePerformanceCollector * added SpanContext.data ser/deser Handle App Start Continuous Profiling v8 (p4) (#3730) * create app start continuous profiler instead of transaction profiler, based on config * updated SentryAppStartProfilingOptions with isContinuousProfilingEnabled flag * updated SentryOptions with isContinuousProfilingEnabled() method * cut profiler setup out in a specific function to improve readability of AndroidOptionsInitializer Add new APIs for Continuous Profiling v8 (p5) (#3844) * AndroidContinuousProfiler now retrieve the scopes on start() * removed profilesSampleRate from sample app to enable continuous profiling * added Sentry.startProfiler and Sentry.stopProfiler APIs
1 parent 3c9a35a commit 5ebb536

File tree

72 files changed

+1217
-249
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+1217
-249
lines changed

sentry-android-core/api/sentry-android-core.api

+4-2
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ public final class io/sentry/android/core/ActivityLifecycleIntegration : android
3737
}
3838

3939
public class io/sentry/android/core/AndroidContinuousProfiler : io/sentry/IContinuousProfiler {
40-
public fun <init> (Lio/sentry/android/core/BuildInfoProvider;Lio/sentry/android/core/internal/util/SentryFrameMetricsCollector;Lio/sentry/ILogger;Ljava/lang/String;ZILio/sentry/ISentryExecutorService;)V
40+
public fun <init> (Lio/sentry/android/core/BuildInfoProvider;Lio/sentry/android/core/internal/util/SentryFrameMetricsCollector;Lio/sentry/ILogger;Ljava/lang/String;ILio/sentry/ISentryExecutorService;)V
4141
public fun close ()V
42+
public fun getProfilerId ()Lio/sentry/protocol/SentryId;
4243
public fun isRunning ()Z
43-
public fun setScopes (Lio/sentry/IScopes;)V
4444
public fun start ()V
4545
public fun stop ()V
4646
}
@@ -447,6 +447,7 @@ public class io/sentry/android/core/performance/AppStartMetrics : io/sentry/andr
447447
public fun addActivityLifecycleTimeSpans (Lio/sentry/android/core/performance/ActivityLifecycleTimeSpan;)V
448448
public fun clear ()V
449449
public fun getActivityLifecycleTimeSpans ()Ljava/util/List;
450+
public fun getAppStartContinuousProfiler ()Lio/sentry/IContinuousProfiler;
450451
public fun getAppStartProfiler ()Lio/sentry/ITransactionProfiler;
451452
public fun getAppStartSamplingDecision ()Lio/sentry/TracesSamplingDecision;
452453
public fun getAppStartTimeSpan ()Lio/sentry/android/core/performance/TimeSpan;
@@ -465,6 +466,7 @@ public class io/sentry/android/core/performance/AppStartMetrics : io/sentry/andr
465466
public static fun onContentProviderPostCreate (Landroid/content/ContentProvider;)V
466467
public fun registerApplicationForegroundCheck (Landroid/app/Application;)V
467468
public fun setAppLaunchedInForeground (Z)V
469+
public fun setAppStartContinuousProfiler (Lio/sentry/IContinuousProfiler;)V
468470
public fun setAppStartProfiler (Lio/sentry/ITransactionProfiler;)V
469471
public fun setAppStartSamplingDecision (Lio/sentry/TracesSamplingDecision;)V
470472
public fun setAppStartType (Lio/sentry/android/core/performance/AppStartMetrics$AppStartType;)V

sentry-android-core/src/main/java/io/sentry/android/core/AndroidContinuousProfiler.java

+34-19
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,15 @@
44

55
import android.annotation.SuppressLint;
66
import android.os.Build;
7+
import io.sentry.CompositePerformanceCollector;
78
import io.sentry.IContinuousProfiler;
89
import io.sentry.ILogger;
910
import io.sentry.IScopes;
1011
import io.sentry.ISentryExecutorService;
12+
import io.sentry.NoOpScopes;
13+
import io.sentry.PerformanceCollectionData;
1114
import io.sentry.ProfileChunk;
15+
import io.sentry.Sentry;
1216
import io.sentry.SentryLevel;
1317
import io.sentry.SentryOptions;
1418
import io.sentry.android.core.internal.util.SentryFrameMetricsCollector;
@@ -28,7 +32,6 @@ public class AndroidContinuousProfiler implements IContinuousProfiler {
2832

2933
private final @NotNull ILogger logger;
3034
private final @Nullable String profilingTracesDirPath;
31-
private final boolean isProfilingEnabled;
3235
private final int profilingTracesHz;
3336
private final @NotNull ISentryExecutorService executorService;
3437
private final @NotNull BuildInfoProvider buildInfoProvider;
@@ -37,7 +40,8 @@ public class AndroidContinuousProfiler implements IContinuousProfiler {
3740
private @Nullable AndroidProfiler profiler = null;
3841
private boolean isRunning = false;
3942
private @Nullable IScopes scopes;
40-
private @Nullable Future<?> closeFuture;
43+
private @Nullable Future<?> stopFuture;
44+
private @Nullable CompositePerformanceCollector performanceCollector;
4145
private final @NotNull List<ProfileChunk.Builder> payloadBuilders = new ArrayList<>();
4246
private @NotNull SentryId profilerId = SentryId.EMPTY_ID;
4347
private @NotNull SentryId chunkId = SentryId.EMPTY_ID;
@@ -47,14 +51,12 @@ public AndroidContinuousProfiler(
4751
final @NotNull SentryFrameMetricsCollector frameMetricsCollector,
4852
final @NotNull ILogger logger,
4953
final @Nullable String profilingTracesDirPath,
50-
final boolean isProfilingEnabled,
5154
final int profilingTracesHz,
5255
final @NotNull ISentryExecutorService executorService) {
5356
this.logger = logger;
5457
this.frameMetricsCollector = frameMetricsCollector;
5558
this.buildInfoProvider = buildInfoProvider;
5659
this.profilingTracesDirPath = profilingTracesDirPath;
57-
this.isProfilingEnabled = isProfilingEnabled;
5860
this.profilingTracesHz = profilingTracesHz;
5961
this.executorService = executorService;
6062
}
@@ -65,10 +67,6 @@ private void init() {
6567
return;
6668
}
6769
isInitialized = true;
68-
if (!isProfilingEnabled) {
69-
logger.log(SentryLevel.INFO, "Profiling is disabled in options.");
70-
return;
71-
}
7270
if (profilingTracesDirPath == null) {
7371
logger.log(
7472
SentryLevel.WARNING,
@@ -92,11 +90,14 @@ private void init() {
9290
logger);
9391
}
9492

95-
public synchronized void setScopes(final @NotNull IScopes scopes) {
96-
this.scopes = scopes;
97-
}
98-
9993
public synchronized void start() {
94+
if ((scopes == null || scopes != NoOpScopes.getInstance())
95+
&& Sentry.getCurrentScopes() != NoOpScopes.getInstance()) {
96+
this.scopes = Sentry.getCurrentScopes();
97+
this.performanceCollector =
98+
Sentry.getCurrentScopes().getOptions().getCompositePerformanceCollector();
99+
}
100+
100101
// Debug.startMethodTracingSampling() is only available since Lollipop, but Android Profiler
101102
// causes crashes on api 21 -> https://github.com/getsentry/sentry-java/issues/3392
102103
if (buildInfoProvider.getSdkInfoVersion() < Build.VERSION_CODES.LOLLIPOP_MR1) return;
@@ -124,8 +125,12 @@ public synchronized void start() {
124125
chunkId = new SentryId();
125126
}
126127

128+
if (performanceCollector != null) {
129+
performanceCollector.start(chunkId.toString());
130+
}
131+
127132
try {
128-
closeFuture = executorService.schedule(() -> stop(true), MAX_CHUNK_DURATION_MILLIS);
133+
stopFuture = executorService.schedule(() -> stop(true), MAX_CHUNK_DURATION_MILLIS);
129134
} catch (RejectedExecutionException e) {
130135
logger.log(
131136
SentryLevel.ERROR,
@@ -140,8 +145,8 @@ public synchronized void stop() {
140145

141146
@SuppressLint("NewApi")
142147
private synchronized void stop(final boolean restartProfiler) {
143-
if (closeFuture != null) {
144-
closeFuture.cancel(true);
148+
if (stopFuture != null) {
149+
stopFuture.cancel(true);
145150
}
146151
// check if profiler was created and it's running
147152
if (profiler == null || !isRunning) {
@@ -154,8 +159,13 @@ private synchronized void stop(final boolean restartProfiler) {
154159
return;
155160
}
156161

157-
// todo add PerformanceCollectionData
158-
final AndroidProfiler.ProfileEndData endData = profiler.endAndCollect(false, null);
162+
List<PerformanceCollectionData> performanceCollectionData = null;
163+
if (performanceCollector != null) {
164+
performanceCollectionData = performanceCollector.stop(chunkId.toString());
165+
}
166+
167+
final AndroidProfiler.ProfileEndData endData =
168+
profiler.endAndCollect(false, performanceCollectionData);
159169

160170
// check if profiler end successfully
161171
if (endData == null) {
@@ -195,6 +205,11 @@ public synchronized void close() {
195205
stop();
196206
}
197207

208+
@Override
209+
public @NotNull SentryId getProfilerId() {
210+
return profilerId;
211+
}
212+
198213
private void sendChunks(final @NotNull IScopes scopes, final @NotNull SentryOptions options) {
199214
try {
200215
options
@@ -224,7 +239,7 @@ public boolean isRunning() {
224239

225240
@VisibleForTesting
226241
@Nullable
227-
Future<?> getCloseFuture() {
228-
return closeFuture;
242+
Future<?> getStopFuture() {
243+
return stopFuture;
229244
}
230245
}

sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java

+70-17
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,14 @@
66
import android.content.Context;
77
import android.content.pm.PackageInfo;
88
import io.sentry.DeduplicateMultithreadedEventProcessor;
9-
import io.sentry.DefaultTransactionPerformanceCollector;
9+
import io.sentry.DefaultCompositePerformanceCollector;
10+
import io.sentry.IContinuousProfiler;
1011
import io.sentry.ILogger;
1112
import io.sentry.ISentryLifecycleToken;
1213
import io.sentry.ITransactionProfiler;
1314
import io.sentry.NoOpConnectionStatusProvider;
15+
import io.sentry.NoOpContinuousProfiler;
16+
import io.sentry.NoOpTransactionProfiler;
1417
import io.sentry.ScopeType;
1518
import io.sentry.SendFireAndForgetEnvelopeSender;
1619
import io.sentry.SendFireAndForgetOutboxSender;
@@ -159,23 +162,23 @@ static void initializeIntegrationsAndProcessors(
159162
// Check if the profiler was already instantiated in the app start.
160163
// We use the Android profiler, that uses a global start/stop api, so we need to preserve the
161164
// state of the profiler, and it's only possible retaining the instance.
165+
final @NotNull AppStartMetrics appStartMetrics = AppStartMetrics.getInstance();
166+
final @Nullable ITransactionProfiler appStartTransactionProfiler;
167+
final @Nullable IContinuousProfiler appStartContinuousProfiler;
162168
try (final @NotNull ISentryLifecycleToken ignored = AppStartMetrics.staticLock.acquire()) {
163-
final @Nullable ITransactionProfiler appStartProfiler =
164-
AppStartMetrics.getInstance().getAppStartProfiler();
165-
if (appStartProfiler != null) {
166-
options.setTransactionProfiler(appStartProfiler);
167-
AppStartMetrics.getInstance().setAppStartProfiler(null);
168-
} else {
169-
options.setTransactionProfiler(
170-
new AndroidTransactionProfiler(
171-
context,
172-
options,
173-
buildInfoProvider,
174-
Objects.requireNonNull(
175-
options.getFrameMetricsCollector(),
176-
"options.getFrameMetricsCollector is required")));
177-
}
169+
appStartTransactionProfiler = appStartMetrics.getAppStartProfiler();
170+
appStartContinuousProfiler = appStartMetrics.getAppStartContinuousProfiler();
171+
appStartMetrics.setAppStartProfiler(null);
172+
appStartMetrics.setAppStartContinuousProfiler(null);
178173
}
174+
175+
setupProfiler(
176+
options,
177+
context,
178+
buildInfoProvider,
179+
appStartTransactionProfiler,
180+
appStartContinuousProfiler);
181+
179182
options.setModulesLoader(new AssetsModulesLoader(context, options.getLogger()));
180183
options.setDebugMetaLoader(new AssetsDebugMetaLoader(context, options.getLogger()));
181184

@@ -223,7 +226,7 @@ static void initializeIntegrationsAndProcessors(
223226
"options.getFrameMetricsCollector is required")));
224227
}
225228
}
226-
options.setTransactionPerformanceCollector(new DefaultTransactionPerformanceCollector(options));
229+
options.setCompositePerformanceCollector(new DefaultCompositePerformanceCollector(options));
227230

228231
if (options.getCacheDirPath() != null) {
229232
if (options.isEnableScopePersistence()) {
@@ -233,6 +236,56 @@ static void initializeIntegrationsAndProcessors(
233236
}
234237
}
235238

239+
/** Setup the correct profiler (transaction or continuous) based on the options. */
240+
private static void setupProfiler(
241+
final @NotNull SentryAndroidOptions options,
242+
final @NotNull Context context,
243+
final @NotNull BuildInfoProvider buildInfoProvider,
244+
final @Nullable ITransactionProfiler appStartTransactionProfiler,
245+
final @Nullable IContinuousProfiler appStartContinuousProfiler) {
246+
if (options.isProfilingEnabled() || options.getProfilesSampleRate() != null) {
247+
options.setContinuousProfiler(NoOpContinuousProfiler.getInstance());
248+
// This is a safeguard, but it should never happen, as the app start profiler should be the
249+
// continuous one.
250+
if (appStartContinuousProfiler != null) {
251+
appStartContinuousProfiler.close();
252+
}
253+
if (appStartTransactionProfiler != null) {
254+
options.setTransactionProfiler(appStartTransactionProfiler);
255+
} else {
256+
options.setTransactionProfiler(
257+
new AndroidTransactionProfiler(
258+
context,
259+
options,
260+
buildInfoProvider,
261+
Objects.requireNonNull(
262+
options.getFrameMetricsCollector(),
263+
"options.getFrameMetricsCollector is required")));
264+
}
265+
} else {
266+
options.setTransactionProfiler(NoOpTransactionProfiler.getInstance());
267+
// This is a safeguard, but it should never happen, as the app start profiler should be the
268+
// transaction one.
269+
if (appStartTransactionProfiler != null) {
270+
appStartTransactionProfiler.close();
271+
}
272+
if (appStartContinuousProfiler != null) {
273+
options.setContinuousProfiler(appStartContinuousProfiler);
274+
} else {
275+
options.setContinuousProfiler(
276+
new AndroidContinuousProfiler(
277+
buildInfoProvider,
278+
Objects.requireNonNull(
279+
options.getFrameMetricsCollector(),
280+
"options.getFrameMetricsCollector is required"),
281+
options.getLogger(),
282+
options.getProfilingTracesDirPath(),
283+
options.getProfilingTracesHz(),
284+
options.getExecutorService()));
285+
}
286+
}
287+
}
288+
236289
static void installDefaultIntegrations(
237290
final @NotNull Context context,
238291
final @NotNull SentryAndroidOptions options,

0 commit comments

Comments
 (0)