Skip to content

Commit

Permalink
Merge 7f60bc7 into 1141aed
Browse files Browse the repository at this point in the history
  • Loading branch information
markushi authored Dec 4, 2023
2 parents 1141aed + 7f60bc7 commit 5362676
Show file tree
Hide file tree
Showing 26 changed files with 359 additions and 94 deletions.
14 changes: 12 additions & 2 deletions sentry-android-core/api/sentry-android-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public final class io/sentry/android/core/ActivityLifecycleIntegration : android
public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V
}

public final class io/sentry/android/core/AndroidCpuCollector : io/sentry/ICollector {
public final class io/sentry/android/core/AndroidCpuCollector : io/sentry/IPerformanceSnapshotCollector {
public fun <init> (Lio/sentry/ILogger;Lio/sentry/android/core/BuildInfoProvider;)V
public fun collect (Lio/sentry/PerformanceCollectionData;)V
public fun setup ()V
Expand All @@ -43,7 +43,7 @@ public final class io/sentry/android/core/AndroidLogger : io/sentry/ILogger {
public fun log (Lio/sentry/SentryLevel;Ljava/lang/Throwable;Ljava/lang/String;[Ljava/lang/Object;)V
}

public class io/sentry/android/core/AndroidMemoryCollector : io/sentry/ICollector {
public class io/sentry/android/core/AndroidMemoryCollector : io/sentry/IPerformanceSnapshotCollector {
public fun <init> ()V
public fun collect (Lio/sentry/PerformanceCollectionData;)V
public fun setup ()V
Expand Down Expand Up @@ -71,6 +71,14 @@ public class io/sentry/android/core/AndroidProfiler$ProfileStartData {
public fun <init> (JJ)V
}

public class io/sentry/android/core/AndroidSlowFrozenFrameCollector : io/sentry/IPerformanceContinuousCollector, io/sentry/android/core/internal/util/SentryFrameMetricsCollector$FrameMetricsCollectorListener {
public fun <init> (Lio/sentry/android/core/SentryAndroidOptions;)V
public fun clear ()V
public fun onFrameMetricCollected (JJJF)V
public fun onSpanFinished (Lio/sentry/ISpan;)V
public fun onSpanStarted (Lio/sentry/ISpan;)V
}

public final class io/sentry/android/core/AnrIntegration : io/sentry/Integration, java/io/Closeable {
public fun <init> (Landroid/content/Context;)V
public fun close ()V
Expand Down Expand Up @@ -246,6 +254,7 @@ public final class io/sentry/android/core/SentryAndroidOptions : io/sentry/Sentr
public fun getBeforeScreenshotCaptureCallback ()Lio/sentry/android/core/SentryAndroidOptions$BeforeCaptureCallback;
public fun getBeforeViewHierarchyCaptureCallback ()Lio/sentry/android/core/SentryAndroidOptions$BeforeCaptureCallback;
public fun getDebugImagesLoader ()Lio/sentry/android/core/IDebugImagesLoader;
public fun getFrameMetricsCollector ()Lio/sentry/android/core/internal/util/SentryFrameMetricsCollector;
public fun getNativeSdkName ()Ljava/lang/String;
public fun getProfilingTracesHz ()I
public fun getProfilingTracesIntervalMillis ()I
Expand Down Expand Up @@ -291,6 +300,7 @@ public final class io/sentry/android/core/SentryAndroidOptions : io/sentry/Sentr
public fun setEnableScopeSync (Z)V
public fun setEnableStarfish (Z)V
public fun setEnableSystemEventBreadcrumbs (Z)V
public fun setFrameMetricsCollector (Lio/sentry/android/core/internal/util/SentryFrameMetricsCollector;)V
public fun setNativeSdkName (Ljava/lang/String;)V
public fun setProfilingTracesHz (I)V
public fun setProfilingTracesIntervalMillis (I)V
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
* A class that tracks slow and frozen frames using the FrameMetricsAggregator class from
* androidx.core package. It also checks if the FrameMetricsAggregator class is available at
* runtime.
*
* <p>If starfish is enabled, frame metrics are recorded using {@link
* io.sentry.android.core.internal.util.SentryFrameMetricsCollector} via {@link
* AndroidSlowFrozenFrameCollector} instead.
*/
public final class ActivityFramesTracker {

Expand All @@ -42,7 +46,8 @@ public ActivityFramesTracker(
final boolean androidXAvailable =
loadClass.isClassAvailable("androidx.core.app.FrameMetricsAggregator", options.getLogger());

if (androidXAvailable) {
/** */
if (androidXAvailable && !options.isEnableStarfish()) {
frameMetricsAggregator = new FrameMetricsAggregator();
}
this.options = options;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import android.system.Os;
import android.system.OsConstants;
import io.sentry.CpuCollectionData;
import io.sentry.ICollector;
import io.sentry.ILogger;
import io.sentry.IPerformanceSnapshotCollector;
import io.sentry.PerformanceCollectionData;
import io.sentry.SentryLevel;
import io.sentry.util.FileUtils;
Expand All @@ -22,7 +22,7 @@
// The content of the /proc/self/stat file is specified in
// https://man7.org/linux/man-pages/man5/proc.5.html
@ApiStatus.Internal
public final class AndroidCpuCollector implements ICollector {
public final class AndroidCpuCollector implements IPerformanceSnapshotCollector {

private long lastRealtimeNanos = 0;
private long lastCpuNanos = 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package io.sentry.android.core;

import android.os.Debug;
import io.sentry.ICollector;
import io.sentry.IPerformanceSnapshotCollector;
import io.sentry.MemoryCollectionData;
import io.sentry.PerformanceCollectionData;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;

@ApiStatus.Internal
public class AndroidMemoryCollector implements ICollector {
public class AndroidMemoryCollector implements IPerformanceSnapshotCollector {

@Override
public void setup() {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ static void loadDefaultAndMetadataOptions(
// set a lower flush timeout on Android to avoid ANRs
options.setFlushTimeoutMillis(DEFAULT_FLUSH_TIMEOUT_MS);

options.setFrameMetricsCollector(
new SentryFrameMetricsCollector(context, logger, buildInfoProvider));

ManifestMetadataReader.applyMetadata(context, options, buildInfoProvider);
initializeCacheDirs(context, options);

Expand Down Expand Up @@ -145,10 +148,8 @@ static void initializeIntegrationsAndProcessors(
options.addEventProcessor(new ViewHierarchyEventProcessor(options));
options.addEventProcessor(new AnrV2EventProcessor(context, options, buildInfoProvider));
options.setTransportGate(new AndroidTransportGate(options));
final SentryFrameMetricsCollector frameMetricsCollector =
new SentryFrameMetricsCollector(context, options, buildInfoProvider);
options.setTransactionProfiler(
new AndroidTransactionProfiler(context, options, buildInfoProvider, frameMetricsCollector));
new AndroidTransactionProfiler(context, options, buildInfoProvider));
options.setModulesLoader(new AssetsModulesLoader(context, options.getLogger()));
options.setDebugMetaLoader(new AssetsDebugMetaLoader(context, options.getLogger()));

Expand Down Expand Up @@ -183,9 +184,13 @@ static void initializeIntegrationsAndProcessors(
}

options.setMainThreadChecker(AndroidMainThreadChecker.getInstance());
if (options.getCollectors().isEmpty()) {
options.addCollector(new AndroidMemoryCollector());
options.addCollector(new AndroidCpuCollector(options.getLogger(), buildInfoProvider));
if (options.getPerformanceCollectors().isEmpty()) {
options.addPerformanceCollector(new AndroidMemoryCollector());
options.addPerformanceCollector(
new AndroidCpuCollector(options.getLogger(), buildInfoProvider));
}
if (options.isEnableStarfish()) {
options.addPerformanceCollector(new AndroidSlowFrozenFrameCollector(options));
}
options.setTransactionPerformanceCollector(new DefaultTransactionPerformanceCollector(options));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,10 @@ public AndroidProfiler(

@Override
public void onFrameMetricCollected(
final long frameEndNanos, final long durationNanos, float refreshRate) {
final long frameEndNanos,
final long durationNanos,
final long delayNanos,
float refreshRate) {
// transactionStartNanos is calculated through SystemClock.elapsedRealtimeNanos(),
// but frameEndNanos uses System.nanotime(), so we convert it to get the timestamp
// relative to transactionStartNanos
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package io.sentry.android.core;

import io.sentry.FrameMetrics;
import io.sentry.IPerformanceContinuousCollector;
import io.sentry.ISpan;
import io.sentry.SpanId;
import io.sentry.android.core.internal.util.SentryFrameMetricsCollector;
import java.util.concurrent.ConcurrentHashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public class AndroidSlowFrozenFrameCollector
implements IPerformanceContinuousCollector,
SentryFrameMetricsCollector.FrameMetricsCollectorListener {
private @NotNull final Object lock = new Object();
private @Nullable final SentryFrameMetricsCollector frameMetricsCollector;
private @Nullable volatile String listenerId;
private @NotNull final ConcurrentHashMap<SpanId, FrameMetrics> metricsPerSpan;

public AndroidSlowFrozenFrameCollector(@NotNull SentryAndroidOptions options) {
frameMetricsCollector = options.getFrameMetricsCollector();
metricsPerSpan = new ConcurrentHashMap<>();
}

@Override
public void onSpanStarted(@NotNull ISpan span) {
metricsPerSpan.put(span.getSpanContext().getSpanId(), new FrameMetrics());

synchronized (lock) {
if (listenerId == null) {
if (frameMetricsCollector != null) {
listenerId = frameMetricsCollector.startCollection(this);
}
}
}
}

@Override
public void onSpanFinished(@NotNull ISpan span) {
final FrameMetrics metrics = metricsPerSpan.remove(span.getSpanContext().getSpanId());
// TODO add data to span
}

@Override
public void clear() {
synchronized (lock) {
if (listenerId != null) {
if (frameMetricsCollector != null) {
frameMetricsCollector.stopCollection(listenerId);
}
listenerId = null;
}
}
}

@Override
public void onFrameMetricCollected(
long frameEndNanos, long durationNanos, long delayNanos, float refreshRate) {
for (final @NotNull FrameMetrics metrics : metricsPerSpan.values()) {
// TODO aggregate metrics for every running span
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
final class AndroidTransactionProfiler implements ITransactionProfiler {
private final @NotNull Context context;
private final @NotNull SentryAndroidOptions options;
private final @NotNull IHub hub;
private final @NotNull BuildInfoProvider buildInfoProvider;
private boolean isInitialized = false;
private int transactionsCounter = 0;
Expand All @@ -44,27 +43,20 @@ final class AndroidTransactionProfiler implements ITransactionProfiler {
public AndroidTransactionProfiler(
final @NotNull Context context,
final @NotNull SentryAndroidOptions sentryAndroidOptions,
final @NotNull BuildInfoProvider buildInfoProvider,
final @NotNull SentryFrameMetricsCollector frameMetricsCollector) {
this(
context,
sentryAndroidOptions,
buildInfoProvider,
frameMetricsCollector,
HubAdapter.getInstance());
final @NotNull BuildInfoProvider buildInfoProvider) {
this(context, sentryAndroidOptions, buildInfoProvider, HubAdapter.getInstance());
}

public AndroidTransactionProfiler(
final @NotNull Context context,
final @NotNull SentryAndroidOptions sentryAndroidOptions,
final @NotNull BuildInfoProvider buildInfoProvider,
final @NotNull SentryFrameMetricsCollector frameMetricsCollector,
final @NotNull IHub hub) {
this.context = Objects.requireNonNull(context, "The application context is required");
this.options = Objects.requireNonNull(sentryAndroidOptions, "SentryAndroidOptions is required");
this.hub = Objects.requireNonNull(hub, "Hub is required");
this.frameMetricsCollector =
Objects.requireNonNull(frameMetricsCollector, "SentryFrameMetricsCollector is required");
Objects.requireNonNull(
options.getFrameMetricsCollector(), "SentryFrameMetricsCollector is required");
this.buildInfoProvider =
Objects.requireNonNull(buildInfoProvider, "The BuildInfoProvider is required.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import io.sentry.SentryOptions;
import io.sentry.SpanStatus;
import io.sentry.android.core.internal.util.RootChecker;
import io.sentry.android.core.internal.util.SentryFrameMetricsCollector;
import io.sentry.protocol.Mechanism;
import io.sentry.protocol.SdkVersion;
import org.jetbrains.annotations.ApiStatus;
Expand Down Expand Up @@ -214,6 +215,8 @@ public interface BeforeCaptureCallback {

private boolean enableStarfish;

private @Nullable SentryFrameMetricsCollector frameMetricsCollector;

public SentryAndroidOptions() {
setSentryClientName(BuildConfig.SENTRY_ANDROID_SDK_NAME + "/" + BuildConfig.VERSION_NAME);
setSdkVersion(createSdkVersion());
Expand Down Expand Up @@ -601,4 +604,15 @@ public boolean isEnableStarfish() {
public void setEnableStarfish(final boolean enableStarfish) {
this.enableStarfish = enableStarfish;
}

@ApiStatus.Internal
public @Nullable SentryFrameMetricsCollector getFrameMetricsCollector() {
return frameMetricsCollector;
}

@ApiStatus.Internal
public void setFrameMetricsCollector(
final @Nullable SentryFrameMetricsCollector frameMetricsCollector) {
this.frameMetricsCollector = frameMetricsCollector;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,12 @@ public SentryFrameMetricsCollector(
buildInfoProvider.getSdkInfoVersion() >= Build.VERSION_CODES.R
? window.getContext().getDisplay().getRefreshRate()
: window.getWindowManager().getDefaultDisplay().getRefreshRate();
final long expectedFrameTimeNanos = (long) (1_000_000_000 / refreshRate);
final long totalDurationNanos = frameMetrics.getMetric(FrameMetrics.TOTAL_DURATION);

// if totalDurationNanos is smaller than expectedFrameTimeNanos,
// it means that the frame was drawn within it's time budget, thus 0 delay
final long delayNanos = Math.max(0, totalDurationNanos - expectedFrameTimeNanos);

final long cpuDuration = getFrameCpuDuration(frameMetrics);
long startTime = getFrameStartTimestamp(frameMetrics);
Expand All @@ -156,7 +162,7 @@ public SentryFrameMetricsCollector(
lastFrameEndNanos = startTime + cpuDuration;

for (FrameMetricsCollectorListener l : listenerMap.values()) {
l.onFrameMetricCollected(lastFrameEndNanos, cpuDuration, refreshRate);
l.onFrameMetricCollected(lastFrameEndNanos, cpuDuration, delayNanos, refreshRate);
}
};
}
Expand Down Expand Up @@ -302,10 +308,14 @@ public interface FrameMetricsCollectorListener {
* @param frameEndNanos End timestamp of a frame in nanoseconds relative to System.nanotime().
* @param durationNanos Duration in nanoseconds of the time spent from the cpu on the main
* thread to create the frame.
* @param delayNanos the frame delay, in nanoseconds.
* @param refreshRate Refresh rate of the screen.
*/
void onFrameMetricCollected(
final long frameEndNanos, final long durationNanos, final float refreshRate);
final long frameEndNanos,
final long durationNanos,
final long delayNanos,
final float refreshRate);
}

@ApiStatus.Internal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -570,14 +570,14 @@ class AndroidOptionsInitializerTest {
fun `AndroidMemoryCollector is set to options`() {
fixture.initSut()

assertTrue { fixture.sentryOptions.collectors.any { it is AndroidMemoryCollector } }
assertTrue { fixture.sentryOptions.performanceCollectors.any { it is AndroidMemoryCollector } }
}

@Test
fun `AndroidCpuCollector is set to options`() {
fixture.initSut()

assertTrue { fixture.sentryOptions.collectors.any { it is AndroidCpuCollector } }
assertTrue { fixture.sentryOptions.performanceCollectors.any { it is AndroidCpuCollector } }
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ class AndroidTransactionProfilerTest {
transaction1 = SentryTracer(TransactionContext("", ""), hub)
transaction2 = SentryTracer(TransactionContext("", ""), hub)
transaction3 = SentryTracer(TransactionContext("", ""), hub)
return AndroidTransactionProfiler(context, options, buildInfoProvider, frameMetricsCollector, hub)
return AndroidTransactionProfiler(context, options, buildInfoProvider, hub)
}
}

Expand Down
Loading

0 comments on commit 5362676

Please sign in to comment.