77import io .sentry .NoOpSpan ;
88import io .sentry .NoOpTransaction ;
99import io .sentry .SentryDate ;
10- import io .sentry .SentryLongDate ;
1110import io .sentry .SentryNanotimeDate ;
12- import io .sentry .SentryTracer ;
1311import io .sentry .SpanDataConvention ;
1412import io .sentry .android .core .internal .util .SentryFrameMetricsCollector ;
1513import io .sentry .protocol .MeasurementValue ;
@@ -33,7 +31,7 @@ public class SpanFrameMetricsCollector
3331 // grow indefinitely in case of a long running span
3432 private static final int MAX_FRAMES_COUNT = 3600 ;
3533 private static final long ONE_SECOND_NANOS = TimeUnit .SECONDS .toNanos (1 );
36- private static final SentryNanotimeDate UNIX_START_DATE = new SentryNanotimeDate (new Date (0 ), 0 );
34+ private static final SentryNanotimeDate EMPTY_NANO_TIME = new SentryNanotimeDate (new Date (0 ), 0 );
3735
3836 private final boolean enabled ;
3937 private final @ NotNull Object lock = new Object ();
@@ -125,7 +123,7 @@ public void onSpanFinished(final @NotNull ISpan span) {
125123 } else {
126124 // otherwise only remove old/irrelevant frames
127125 final @ NotNull ISpan oldestSpan = runningSpans .first ();
128- frames .headSet (new Frame (realNanos (oldestSpan .getStartDate ()))).clear ();
126+ frames .headSet (new Frame (toNanoTime (oldestSpan .getStartDate ()))).clear ();
129127 }
130128 }
131129 }
@@ -138,22 +136,20 @@ private void captureFrameMetrics(@NotNull final ISpan span) {
138136 return ;
139137 }
140138
141- // Ignore spans with no finish date, but SentryTracer is not finished when executing this
142- // callback, yet, so in that case we use the current timestamp.
143- final @ Nullable SentryDate spanFinishDate =
144- span instanceof SentryTracer ? new SentryNanotimeDate () : span .getFinishDate ();
139+ final @ Nullable SentryDate spanFinishDate = span .getFinishDate ();
145140 if (spanFinishDate == null ) {
146141 return ;
147142 }
148- final long spanEndNanos = realNanos (spanFinishDate );
149143
150- final @ NotNull SentryFrameMetrics frameMetrics = new SentryFrameMetrics ();
151- final long spanStartNanos = realNanos (span .getStartDate ());
152- if (spanStartNanos >= spanEndNanos ) {
144+ final long spanStartNanos = toNanoTime (span .getStartDate ());
145+ final long spanEndNanos = toNanoTime (spanFinishDate );
146+ final long spanDurationNanos = spanEndNanos - spanStartNanos ;
147+ if (spanDurationNanos <= 0 ) {
153148 return ;
154149 }
155150
156- final long spanDurationNanos = spanEndNanos - spanStartNanos ;
151+ final @ NotNull SentryFrameMetrics frameMetrics = new SentryFrameMetrics ();
152+
157153 long frameDurationNanos = lastKnownFrameDurationNanos ;
158154
159155 if (!frames .isEmpty ()) {
@@ -199,11 +195,15 @@ private void captureFrameMetrics(@NotNull final ISpan span) {
199195 int totalFrameCount = frameMetrics .getTotalFrameCount ();
200196
201197 final long nextScheduledFrameNanos = frameMetricsCollector .getLastKnownFrameStartTimeNanos ();
202- totalFrameCount +=
203- addPendingFrameDelay (
204- frameMetrics , frameDurationNanos , spanEndNanos , nextScheduledFrameNanos );
205- totalFrameCount += interpolateFrameCount (frameMetrics , frameDurationNanos , spanDurationNanos );
206-
198+ // nextScheduledFrameNanos might be -1 if no frames have been scheduled for drawing yet
199+ // e.g. can happen during early app start
200+ if (nextScheduledFrameNanos != -1 ) {
201+ totalFrameCount +=
202+ addPendingFrameDelay (
203+ frameMetrics , frameDurationNanos , spanEndNanos , nextScheduledFrameNanos );
204+ totalFrameCount +=
205+ interpolateFrameCount (frameMetrics , frameDurationNanos , spanDurationNanos );
206+ }
207207 final long frameDelayNanos =
208208 frameMetrics .getSlowFrameDelayNanos () + frameMetrics .getFrozenFrameDelayNanos ();
209209 final double frameDelayInSeconds = frameDelayNanos / 1e9d ;
@@ -305,19 +305,20 @@ private static int addPendingFrameDelay(
305305 * diff does ¯\_(ツ)_/¯
306306 *
307307 * @param date the input date
308- * @return a timestamp in nano precision
308+ * @return a non-unix timestamp in nano precision, similar to {@link System#nanoTime()}.
309309 */
310- private static long realNanos (final @ NotNull SentryDate date ) {
311- // SentryNanotimeDate nanotime is based on System.nanotime(), like UNIX_START_DATE
310+ private static long toNanoTime (final @ NotNull SentryDate date ) {
311+ // SentryNanotimeDate nanotime is based on System.nanotime(), like EMPTY_NANO_TIME,
312+ // thus diff will simply return the System.nanotime() value of date
312313 if (date instanceof SentryNanotimeDate ) {
313- return date .diff (UNIX_START_DATE );
314+ return date .diff (EMPTY_NANO_TIME );
314315 }
315316
316- // SentryLongDate nanotime is based on current date converted to nanoseconds, which is a
317- // different order than frames based System.nanotime(). So we have to convert the nanotime of
318- // the SentryLongDate to a System.nanotime() compatible one.
319- return date .diff ( new SentryLongDate ( DateUtils . millisToNanos ( System . currentTimeMillis ())))
320- + System .nanoTime ();
317+ // e.g. SentryLongDate is unix time based - upscaled to nanos,
318+ // we need to project it back to System.nanotime() format
319+ long nowUnixInNanos = DateUtils . millisToNanos ( System .currentTimeMillis ());
320+ long shiftInNanos = nowUnixInNanos - date .nanoTimestamp ();
321+ return System .nanoTime () - shiftInNanos ;
321322 }
322323
323324 private static class Frame implements Comparable <Frame > {
0 commit comments