Skip to content

Commit c17f259

Browse files
authored
Add timestamp when a profile starts (#3442)
* added start timestamp in ProfileStartData when a profile starts * added the timestamp in ProfilingTraceData and send it in the json payload
1 parent 5ad752a commit c17f259

File tree

8 files changed

+48
-4
lines changed

8 files changed

+48
-4
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### Fixes
66

7+
- Add timestamp when a profile starts ([#3442](https://github.com/getsentry/sentry-java/pull/3442))
78
- Move fragment auto span finish to onFragmentStarted ([#3424](https://github.com/getsentry/sentry-java/pull/3424))
89
- Remove profiling timeout logic and disable profiling on API 21 ([#3478](https://github.com/getsentry/sentry-java/pull/3478))
910
- Properly reset metric flush flag on metric emission ([#3493](https://github.com/getsentry/sentry-java/pull/3493))

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@ public class io/sentry/android/core/AndroidProfiler$ProfileEndData {
8181
public class io/sentry/android/core/AndroidProfiler$ProfileStartData {
8282
public final field startCpuMillis J
8383
public final field startNanos J
84-
public fun <init> (JJ)V
84+
public final field startTimestamp Ljava/util/Date;
85+
public fun <init> (JJLjava/util/Date;)V
8586
}
8687

8788
public final class io/sentry/android/core/AnrIntegration : io/sentry/Integration, java/io/Closeable {

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import android.os.Process;
77
import android.os.SystemClock;
88
import io.sentry.CpuCollectionData;
9+
import io.sentry.DateUtils;
910
import io.sentry.ILogger;
1011
import io.sentry.ISentryExecutorService;
1112
import io.sentry.MemoryCollectionData;
@@ -17,6 +18,7 @@
1718
import io.sentry.util.Objects;
1819
import java.io.File;
1920
import java.util.ArrayDeque;
21+
import java.util.Date;
2022
import java.util.HashMap;
2123
import java.util.List;
2224
import java.util.Map;
@@ -33,10 +35,13 @@ public class AndroidProfiler {
3335
public static class ProfileStartData {
3436
public final long startNanos;
3537
public final long startCpuMillis;
38+
public final @NotNull Date startTimestamp;
3639

37-
public ProfileStartData(final long startNanos, final long startCpuMillis) {
40+
public ProfileStartData(
41+
final long startNanos, final long startCpuMillis, final @NotNull Date startTimestamp) {
3842
this.startNanos = startNanos;
3943
this.startCpuMillis = startCpuMillis;
44+
this.startTimestamp = startTimestamp;
4045
}
4146
}
4247

@@ -190,6 +195,7 @@ public void onFrameMetricCollected(
190195
}
191196

192197
profileStartNanos = SystemClock.elapsedRealtimeNanos();
198+
final @NotNull Date profileStartTimestamp = DateUtils.getCurrentDateTime();
193199
long profileStartCpuMillis = Process.getElapsedCpuTime();
194200

195201
// We don't make any check on the file existence or writeable state, because we don't want to
@@ -201,7 +207,7 @@ public void onFrameMetricCollected(
201207
// tests)
202208
Debug.startMethodTracingSampling(traceFile.getPath(), BUFFER_SIZE_BYTES, intervalUs);
203209
isRunning = true;
204-
return new ProfileStartData(profileStartNanos, profileStartCpuMillis);
210+
return new ProfileStartData(profileStartNanos, profileStartCpuMillis, profileStartTimestamp);
205211
} catch (Throwable e) {
206212
endAndCollect(false, null);
207213
logger.log(SentryLevel.ERROR, "Unable to start a profile: ", e);

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import android.os.Build;
1010
import android.os.Process;
1111
import android.os.SystemClock;
12+
import io.sentry.DateUtils;
1213
import io.sentry.HubAdapter;
1314
import io.sentry.IHub;
1415
import io.sentry.ILogger;
@@ -24,6 +25,7 @@
2425
import io.sentry.android.core.internal.util.SentryFrameMetricsCollector;
2526
import io.sentry.util.Objects;
2627
import java.util.ArrayList;
28+
import java.util.Date;
2729
import java.util.List;
2830
import org.jetbrains.annotations.NotNull;
2931
import org.jetbrains.annotations.Nullable;
@@ -44,6 +46,7 @@ final class AndroidTransactionProfiler implements ITransactionProfiler {
4446
private @Nullable AndroidProfiler profiler = null;
4547
private long profileStartNanos;
4648
private long profileStartCpuMillis;
49+
private @NotNull Date profileStartTimestamp;
4750

4851
/**
4952
* @deprecated please use a constructor that doesn't takes a {@link IHub} instead, as it would be
@@ -95,6 +98,7 @@ public AndroidTransactionProfiler(
9598
this.profilingTracesHz = profilingTracesHz;
9699
this.executorService =
97100
Objects.requireNonNull(executorService, "The ISentryExecutorService is required.");
101+
this.profileStartTimestamp = DateUtils.getCurrentDateTime();
98102
}
99103

100104
private void init() {
@@ -165,6 +169,7 @@ private boolean onFirstStart() {
165169
}
166170
profileStartNanos = startData.startNanos;
167171
profileStartCpuMillis = startData.startCpuMillis;
172+
profileStartTimestamp = startData.startTimestamp;
168173
return true;
169174
}
170175

@@ -275,6 +280,7 @@ public synchronized void bindTransaction(final @NotNull ITransaction transaction
275280
// done in the background when the trace file is read
276281
return new ProfilingTraceData(
277282
endData.traceFile,
283+
profileStartTimestamp,
278284
transactionList,
279285
transactionName,
280286
transactionId,

sentry-android-core/src/test/java/io/sentry/android/core/AndroidProfilerTest.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ class AndroidProfilerTest {
178178
val endData = profiler.endAndCollect(false, null)
179179
assertNotNull(startData?.startNanos)
180180
assertNotNull(startData?.startCpuMillis)
181+
assertNotNull(startData?.startTimestamp)
181182
assertNotNull(endData?.endNanos)
182183
assertNotNull(endData?.endCpuMillis)
183184
}

sentry/api/sentry.api

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1420,7 +1420,7 @@ public final class io/sentry/ProfilingTraceData : io/sentry/JsonSerializable, io
14201420
public static final field TRUNCATION_REASON_NORMAL Ljava/lang/String;
14211421
public static final field TRUNCATION_REASON_TIMEOUT Ljava/lang/String;
14221422
public fun <init> (Ljava/io/File;Lio/sentry/ITransaction;)V
1423-
public fun <init> (Ljava/io/File;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/util/concurrent/Callable;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;)V
1423+
public fun <init> (Ljava/io/File;Ljava/util/Date;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/util/concurrent/Callable;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;)V
14241424
public fun getAndroidApiLevel ()I
14251425
public fun getBuildId ()Ljava/lang/String;
14261426
public fun getCpuArchitecture ()Ljava/lang/String;
@@ -1439,6 +1439,7 @@ public final class io/sentry/ProfilingTraceData : io/sentry/JsonSerializable, io
14391439
public fun getProfileId ()Ljava/lang/String;
14401440
public fun getRelease ()Ljava/lang/String;
14411441
public fun getSampledProfile ()Ljava/lang/String;
1442+
public fun getTimestamp ()Ljava/util/Date;
14421443
public fun getTraceFile ()Ljava/io/File;
14431444
public fun getTraceId ()Ljava/lang/String;
14441445
public fun getTransactionId ()Ljava/lang/String;
@@ -1465,6 +1466,7 @@ public final class io/sentry/ProfilingTraceData : io/sentry/JsonSerializable, io
14651466
public fun setProfileId (Ljava/lang/String;)V
14661467
public fun setRelease (Ljava/lang/String;)V
14671468
public fun setSampledProfile (Ljava/lang/String;)V
1469+
public fun setTimestamp (Ljava/util/Date;)V
14681470
public fun setTraceId (Ljava/lang/String;)V
14691471
public fun setTransactionId (Ljava/lang/String;)V
14701472
public fun setTransactionName (Ljava/lang/String;)V
@@ -1499,6 +1501,7 @@ public final class io/sentry/ProfilingTraceData$JsonKeys {
14991501
public static final field PROFILE_ID Ljava/lang/String;
15001502
public static final field RELEASE Ljava/lang/String;
15011503
public static final field SAMPLED_PROFILE Ljava/lang/String;
1504+
public static final field TIMESTAMP Ljava/lang/String;
15021505
public static final field TRACE_ID Ljava/lang/String;
15031506
public static final field TRANSACTION_ID Ljava/lang/String;
15041507
public static final field TRANSACTION_LIST Ljava/lang/String;

sentry/src/main/java/io/sentry/ProfilingTraceData.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.io.File;
66
import java.io.IOException;
77
import java.util.ArrayList;
8+
import java.util.Date;
89
import java.util.HashMap;
910
import java.util.List;
1011
import java.util.Locale;
@@ -64,6 +65,7 @@ public final class ProfilingTraceData implements JsonUnknown, JsonSerializable {
6465
private @NotNull String profileId;
6566
private @NotNull String environment;
6667
private @NotNull String truncationReason;
68+
private @NotNull Date timestamp;
6769
private final @NotNull Map<String, ProfileMeasurement> measurementsMap;
6870

6971
// Stacktrace (file)
@@ -78,6 +80,7 @@ public ProfilingTraceData(
7880
final @NotNull File traceFile, final @NotNull ITransaction transaction) {
7981
this(
8082
traceFile,
83+
DateUtils.getCurrentDateTime(),
8184
new ArrayList<>(),
8285
transaction.getName(),
8386
transaction.getEventId().toString(),
@@ -101,6 +104,7 @@ public ProfilingTraceData(
101104

102105
public ProfilingTraceData(
103106
@NotNull File traceFile,
107+
@NotNull Date profileStartTimestamp,
104108
@NotNull List<ProfilingTransactionData> transactions,
105109
@NotNull String transactionName,
106110
@NotNull String transactionId,
@@ -120,6 +124,7 @@ public ProfilingTraceData(
120124
@NotNull String truncationReason,
121125
final @NotNull Map<String, ProfileMeasurement> measurementsMap) {
122126
this.traceFile = traceFile;
127+
this.timestamp = profileStartTimestamp;
123128
this.cpuArchitecture = cpuArchitecture;
124129
this.deviceCpuFrequenciesReader = deviceCpuFrequenciesReader;
125130

@@ -262,6 +267,10 @@ public boolean isDeviceIsEmulator() {
262267
return truncationReason;
263268
}
264269

270+
public @NotNull Date getTimestamp() {
271+
return timestamp;
272+
}
273+
265274
public @NotNull Map<String, ProfileMeasurement> getMeasurementsMap() {
266275
return measurementsMap;
267276
}
@@ -306,6 +315,10 @@ public void setDevicePhysicalMemoryBytes(final @NotNull String devicePhysicalMem
306315
this.devicePhysicalMemoryBytes = devicePhysicalMemoryBytes;
307316
}
308317

318+
public void setTimestamp(final @NotNull Date timestamp) {
319+
this.timestamp = timestamp;
320+
}
321+
309322
public void setTruncationReason(final @NotNull String truncationReason) {
310323
this.truncationReason = truncationReason;
311324
}
@@ -386,6 +399,7 @@ public static final class JsonKeys {
386399
public static final String SAMPLED_PROFILE = "sampled_profile";
387400
public static final String TRUNCATION_REASON = "truncation_reason";
388401
public static final String MEASUREMENTS = "measurements";
402+
public static final String TIMESTAMP = "timestamp";
389403
}
390404

391405
@Override
@@ -422,6 +436,7 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger
422436
writer.name(JsonKeys.SAMPLED_PROFILE).value(sampledProfile);
423437
}
424438
writer.name(JsonKeys.MEASUREMENTS).value(logger, measurementsMap);
439+
writer.name(JsonKeys.TIMESTAMP).value(logger, timestamp);
425440
if (unknown != null) {
426441
for (String key : unknown.keySet()) {
427442
Object value = unknown.get(key);
@@ -602,6 +617,12 @@ public static final class Deserializer implements JsonDeserializer<ProfilingTrac
602617
data.measurementsMap.putAll(measurements);
603618
}
604619
break;
620+
case JsonKeys.TIMESTAMP:
621+
Date timestamp = reader.nextDateOrNull(logger);
622+
if (timestamp != null) {
623+
data.timestamp = timestamp;
624+
}
625+
break;
605626
case JsonKeys.SAMPLED_PROFILE:
606627
String sampledProfile = reader.nextStringOrNull();
607628
if (sampledProfile != null) {

sentry/src/test/java/io/sentry/JsonSerializerTest.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,7 @@ class JsonSerializerTest {
492492
@Test
493493
fun `serializes profilingTraceData`() {
494494
val profilingTraceData = ProfilingTraceData(fixture.traceFile, NoOpTransaction.getInstance())
495+
val now = Date()
495496
profilingTraceData.androidApiLevel = 21
496497
profilingTraceData.deviceLocale = "deviceLocale"
497498
profilingTraceData.deviceManufacturer = "deviceManufacturer"
@@ -503,6 +504,7 @@ class JsonSerializerTest {
503504
profilingTraceData.deviceCpuFrequencies = listOf(1, 2, 3, 4)
504505
profilingTraceData.devicePhysicalMemoryBytes = "2000000"
505506
profilingTraceData.buildId = "buildId"
507+
profilingTraceData.timestamp = now
506508
profilingTraceData.transactions = listOf(
507509
ProfilingTransactionData(NoOpTransaction.getInstance(), 1, 2),
508510
ProfilingTransactionData(NoOpTransaction.getInstance(), 2, 3)
@@ -559,6 +561,7 @@ class JsonSerializerTest {
559561
assertEquals("2000000", element["device_physical_memory_bytes"] as String)
560562
assertEquals("android", element["platform"] as String)
561563
assertEquals("buildId", element["build_id"] as String)
564+
assertEquals(DateUtils.getTimestamp(now), element["timestamp"] as String)
562565
assertEquals(
563566
listOf(
564567
mapOf(
@@ -655,6 +658,7 @@ class JsonSerializerTest {
655658
"device_physical_memory_bytes":"2000000",
656659
"platform":"android",
657660
"build_id":"buildId",
661+
"timestamp":"2024-05-24T12:52:03.561Z",
658662
"transactions":[
659663
{
660664
"id":"id",
@@ -729,6 +733,7 @@ class JsonSerializerTest {
729733
assertEquals("2000000", profilingTraceData.devicePhysicalMemoryBytes)
730734
assertEquals("android", profilingTraceData.platform)
731735
assertEquals("buildId", profilingTraceData.buildId)
736+
assertEquals(DateUtils.getDateTime("2024-05-24T12:52:03.561Z"), profilingTraceData.timestamp)
732737
val expectedTransactions = listOf(
733738
ProfilingTransactionData().apply {
734739
id = "id"

0 commit comments

Comments
 (0)