Skip to content

Commit d6f8356

Browse files
authored
Merge 8992018 into 8b7e489
2 parents 8b7e489 + 8992018 commit d6f8356

File tree

17 files changed

+521
-179
lines changed

17 files changed

+521
-179
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ private void stop(final boolean restartProfiler) {
301301
endData.measurementsMap,
302302
endData.traceFile,
303303
startProfileChunkTimestamp,
304-
ProfileChunk.Platform.ANDROID));
304+
ProfileChunk.PLATFORM_ANDROID));
305305
}
306306
}
307307

sentry-async-profiler/src/main/java/io/sentry/asyncprofiler/convert/JfrAsyncProfilerToSentryProfileConverter.java

Lines changed: 59 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,18 @@
1515
import java.io.IOException;
1616
import java.nio.file.Path;
1717
import java.util.ArrayList;
18+
import java.util.HashMap;
1819
import java.util.List;
20+
import java.util.Map;
1921
import org.jetbrains.annotations.NotNull;
2022
import org.jetbrains.annotations.Nullable;
2123

2224
public final class JfrAsyncProfilerToSentryProfileConverter extends JfrConverter {
23-
private static final long NANOS_PER_SECOND = 1_000_000_000L;
25+
private static final double NANOS_PER_SECOND = 1_000_000_000.0;
2426

2527
private final @NotNull SentryProfile sentryProfile = new SentryProfile();
2628
private final @NotNull SentryStackTraceFactory stackTraceFactory;
29+
private final @NotNull Map<SentryStackFrame, Integer> frameDeduplicationMap = new HashMap<>();
2730

2831
public JfrAsyncProfilerToSentryProfileConverter(
2932
JfrReader jfr, Arguments args, @NotNull SentryStackTraceFactory stackTraceFactory) {
@@ -61,6 +64,7 @@ private class ProfileEventVisitor extends AggregatedEventVisitor {
6164
private final @NotNull SentryStackTraceFactory stackTraceFactory;
6265
private final @NotNull JfrReader jfr;
6366
private final @NotNull Arguments args;
67+
private final double ticksPerNanosecond;
6468

6569
public ProfileEventVisitor(
6670
@NotNull SentryProfile sentryProfile,
@@ -71,6 +75,7 @@ public ProfileEventVisitor(
7175
this.stackTraceFactory = stackTraceFactory;
7276
this.jfr = jfr;
7377
this.args = args;
78+
ticksPerNanosecond = jfr.ticksPerSec / NANOS_PER_SECOND;
7479
}
7580

7681
@Override
@@ -83,12 +88,16 @@ public void visit(Event event, long value) {
8388
processThreadMetadata(event, threadId);
8489
}
8590

86-
createSample(event, threadId);
87-
88-
buildStackTraceAndFrames(stackTrace);
91+
processSampleWithStack(event, threadId, stackTrace);
8992
}
9093
}
9194

95+
private long resolveThreadId(int eventThreadId) {
96+
return jfr.threads.get(eventThreadId) != null
97+
? jfr.javaThreads.get(eventThreadId)
98+
: eventThreadId;
99+
}
100+
92101
private void processThreadMetadata(Event event, long threadId) {
93102
final String threadName = getPlainThreadName(event.tid);
94103
sentryProfile
@@ -103,28 +112,66 @@ private void processThreadMetadata(Event event, long threadId) {
103112
});
104113
}
105114

106-
private void buildStackTraceAndFrames(StackTrace stackTrace) {
107-
List<Integer> stack = new ArrayList<>();
108-
int currentFrame = sentryProfile.getFrames().size();
115+
private void processSampleWithStack(Event event, long threadId, StackTrace stackTrace) {
116+
int stackIndex = addStackTrace(stackTrace);
117+
118+
SentrySample sample = new SentrySample();
119+
sample.setTimestamp(calculateTimestamp(event));
120+
sample.setThreadId(String.valueOf(threadId));
121+
sample.setStackId(stackIndex);
122+
123+
sentryProfile.getSamples().add(sample);
124+
}
125+
126+
private double calculateTimestamp(Event event) {
127+
long nanosFromStart = (long) ((event.time - jfr.chunkStartTicks) / ticksPerNanosecond);
128+
129+
long timeNs = jfr.chunkStartNanos + nanosFromStart;
130+
131+
return DateUtils.nanosToSeconds(timeNs);
132+
}
133+
134+
private int addStackTrace(StackTrace stackTrace) {
135+
int stackIndex = sentryProfile.getStacks().size();
136+
List<Integer> callStack = createFramesAndCallStack(stackTrace);
137+
sentryProfile.getStacks().add(callStack);
138+
return stackIndex;
139+
}
140+
141+
private List<Integer> createFramesAndCallStack(StackTrace stackTrace) {
142+
List<Integer> callStack = new ArrayList<>();
109143

110144
long[] methods = stackTrace.methods;
111145
byte[] types = stackTrace.types;
112146
int[] locations = stackTrace.locations;
113147

114148
for (int i = 0; i < methods.length; i++) {
115149
StackTraceElement element = getStackTraceElement(methods[i], types[i], locations[i]);
116-
if (element.isNativeMethod()) {
150+
if (element.isNativeMethod() || isNativeFrame(types[i])) {
117151
continue;
118152
}
119153

120154
SentryStackFrame frame = createStackFrame(element);
121-
sentryProfile.getFrames().add(frame);
155+
frame.setNative(isNativeFrame(types[i]));
156+
int frameIndex = getOrAddFrame(frame);
157+
callStack.add(frameIndex);
158+
}
159+
160+
return callStack;
161+
}
162+
163+
// Get existing frame index or add new frame and return its index
164+
private int getOrAddFrame(SentryStackFrame frame) {
165+
Integer existingIndex = frameDeduplicationMap.get(frame);
122166

123-
stack.add(currentFrame);
124-
currentFrame++;
167+
if (existingIndex != null) {
168+
return existingIndex;
125169
}
126170

127-
sentryProfile.getStacks().add(stack);
171+
int newIndex = sentryProfile.getFrames().size();
172+
sentryProfile.getFrames().add(frame);
173+
frameDeduplicationMap.put(frame, newIndex);
174+
return newIndex;
128175
}
129176

130177
private SentryStackFrame createStackFrame(StackTraceElement element) {
@@ -176,36 +223,12 @@ private boolean isRegularClassWithoutPackage(String className) {
176223
return !className.startsWith("[");
177224
}
178225

179-
private void createSample(Event event, long threadId) {
180-
int stackId = sentryProfile.getStacks().size();
181-
SentrySample sample = new SentrySample();
182-
183-
// Calculate timestamp from JFR event time
184-
long nsFromStart =
185-
(event.time - jfr.chunkStartTicks)
186-
* JfrAsyncProfilerToSentryProfileConverter.NANOS_PER_SECOND
187-
/ jfr.ticksPerSec;
188-
long timeNs = jfr.chunkStartNanos + nsFromStart;
189-
sample.setTimestamp(DateUtils.nanosToSeconds(timeNs));
190-
191-
sample.setThreadId(String.valueOf(threadId));
192-
sample.setStackId(stackId);
193-
194-
sentryProfile.getSamples().add(sample);
195-
}
196-
197226
private boolean shouldMarkAsSystemFrame(StackTraceElement element, String className) {
198227
return element.isNativeMethod() || className.isEmpty();
199228
}
200229

201230
private @Nullable Integer extractLineNumber(StackTraceElement element) {
202231
return element.getLineNumber() != 0 ? element.getLineNumber() : null;
203232
}
204-
205-
private long resolveThreadId(int eventThreadId) {
206-
return jfr.threads.get(eventThreadId) != null
207-
? jfr.javaThreads.get(eventThreadId)
208-
: eventThreadId;
209-
}
210233
}
211234
}

0 commit comments

Comments
 (0)