Skip to content

Commit f669b4f

Browse files
committed
cleanup some minor things
1 parent 6a8fedd commit f669b4f

4 files changed

Lines changed: 90 additions & 63 deletions

File tree

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

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -79,20 +79,16 @@ public void visit(Event event, long value) {
7979
long threadId = resolveThreadId(event.tid);
8080

8181
if (stackTrace != null) {
82-
// Process thread metadata if enabled
8382
if (args.threads) {
8483
processThreadMetadata(event, threadId);
8584
}
86-
87-
// Create and add the sample
85+
8886
createSample(event, threadId);
8987

90-
// Build the stack trace from methods
9188
buildStackTraceAndFrames(stackTrace);
9289
}
9390
}
9491

95-
// Extract thread metadata and add to profile
9692
private void processThreadMetadata(Event event, long threadId) {
9793
final String threadName = getPlainThreadName(event.tid);
9894
sentryProfile
@@ -107,7 +103,6 @@ private void processThreadMetadata(Event event, long threadId) {
107103
});
108104
}
109105

110-
// Build stack trace from method array
111106
private void buildStackTraceAndFrames(StackTrace stackTrace) {
112107
List<Integer> stack = new ArrayList<>();
113108
int currentFrame = sentryProfile.getFrames().size();
@@ -132,19 +127,14 @@ private void buildStackTraceAndFrames(StackTrace stackTrace) {
132127
sentryProfile.getStacks().add(stack);
133128
}
134129

135-
// Create a single stack frame from a stack trace element
136130
private SentryStackFrame createStackFrame(StackTraceElement element) {
137131
SentryStackFrame frame = new SentryStackFrame();
138132
final String classNameWithLambdas = element.getClassName().replace("/", ".");
139133
frame.setFunction(element.getMethodName());
140134

141-
// Extract class name without lambda suffix
142135
String sanitizedClassName = extractSanitizedClassName(classNameWithLambdas);
143-
144-
// Set module based on package structure
145136
frame.setModule(extractModuleName(sanitizedClassName, classNameWithLambdas));
146137

147-
// Determine if frame should be marked as in_app
148138
if (shouldMarkAsSystemFrame(element, classNameWithLambdas)) {
149139
frame.setInApp(false);
150140
} else {
@@ -166,7 +156,7 @@ private String extractSanitizedClassName(String classNameWithLambdas) {
166156
return classNameWithLambdas;
167157
}
168158

169-
// Set module name based on package structure
159+
// TODO: test difference between null and empty string for module
170160
private @Nullable String extractModuleName(
171161
String sanitizedClassName, String classNameWithLambdas) {
172162
if (hasPackageStructure(sanitizedClassName)) {
@@ -178,17 +168,14 @@ private String extractSanitizedClassName(String classNameWithLambdas) {
178168
}
179169
}
180170

181-
// Check if the class name has a package structure (contains dots)
182171
private boolean hasPackageStructure(String className) {
183172
return className.lastIndexOf('.') > 0;
184173
}
185174

186-
// Check if it's a regular class without package (not an array type)
187175
private boolean isRegularClassWithoutPackage(String className) {
188176
return !className.startsWith("[");
189177
}
190178

191-
// Create sample with timestamp and thread info
192179
private void createSample(Event event, long threadId) {
193180
int stackId = sentryProfile.getStacks().size();
194181
SentrySample sample = new SentrySample();
@@ -201,24 +188,20 @@ private void createSample(Event event, long threadId) {
201188
long timeNs = jfr.chunkStartNanos + nsFromStart;
202189
sample.timestamp = DateUtils.nanosToSeconds(timeNs);
203190

204-
// Set thread ID
205191
sample.threadId = String.valueOf(threadId);
206192
sample.stackId = stackId;
207193

208194
sentryProfile.getSamples().add(sample);
209195
}
210196

211-
// Check if the stack frame should be marked as a system frame
212197
private boolean shouldMarkAsSystemFrame(StackTraceElement element, String className) {
213198
return element.isNativeMethod() || className.isEmpty();
214199
}
215200

216-
// Check if the stack trace element has a valid line number
217201
private @Nullable Integer extractLineNumber(StackTraceElement element) {
218202
return element.getLineNumber() != 0 ? element.getLineNumber() : null;
219203
}
220204

221-
// Resolve the actual thread ID from the JFR event
222205
private long resolveThreadId(int eventThreadId) {
223206
return jfr.threads.get(eventThreadId) != null
224207
? jfr.javaThreads.get(eventThreadId)
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package io.sentry.asyncprofiler.convert
2+
3+
import io.sentry.ILogger
4+
import io.sentry.IProfileConverter
5+
import io.sentry.IScope
6+
import io.sentry.IScopes
7+
import io.sentry.Sentry
8+
import io.sentry.SentryOptions
9+
import io.sentry.TracesSampler
10+
import io.sentry.asyncprofiler.provider.AsyncProfilerProfileConverterProvider
11+
import io.sentry.test.DeferredExecutorService
12+
import java.nio.file.Files
13+
import kotlin.io.path.absolutePathString
14+
import kotlin.test.AfterTest
15+
import kotlin.test.BeforeTest
16+
import one.profiler.AsyncProfiler
17+
import org.junit.Test
18+
import org.mockito.Mockito
19+
import org.mockito.kotlin.any
20+
import org.mockito.kotlin.mock
21+
import org.mockito.kotlin.spy
22+
import org.mockito.kotlin.whenever
23+
import kotlin.io.path.deleteIfExists
24+
25+
class JfrAsyncProfilerToSentryProfileConverterTest {
26+
27+
private val fixture = Fixture()
28+
29+
private class Fixture {
30+
private val mockDsn = "http://key@localhost/proj"
31+
val executor = DeferredExecutorService()
32+
val mockedSentry = Mockito.mockStatic(Sentry::class.java)
33+
val mockLogger = mock<ILogger>()
34+
val mockTracesSampler = mock<TracesSampler>()
35+
36+
val scopes: IScopes = mock()
37+
val scope: IScope = mock()
38+
39+
val options =
40+
spy(SentryOptions()).apply {
41+
dsn = mockDsn
42+
profilesSampleRate = 1.0
43+
isDebug = true
44+
setLogger(mockLogger)
45+
}
46+
47+
init {
48+
whenever(mockTracesSampler.sampleSessionProfile(any())).thenReturn(true)
49+
}
50+
51+
fun getSut(optionConfig: ((options: SentryOptions) -> Unit) = {}): IProfileConverter? {
52+
options.executorService = executor
53+
optionConfig(options)
54+
whenever(scopes.options).thenReturn(options)
55+
whenever(scope.options).thenReturn(options)
56+
return AsyncProfilerProfileConverterProvider().profileConverter
57+
}
58+
}
59+
60+
@BeforeTest
61+
fun `set up`() {
62+
Sentry.setCurrentScopes(fixture.scopes)
63+
64+
fixture.mockedSentry.`when`<IScopes> { Sentry.getCurrentScopes() }.thenReturn(fixture.scopes)
65+
fixture.mockedSentry.`when`<IScope> { Sentry.getGlobalScope() }.thenReturn(fixture.scope)
66+
}
67+
68+
@AfterTest
69+
fun clear() {
70+
Sentry.close()
71+
fixture.mockedSentry.close()
72+
}
73+
74+
@Test
75+
fun `convert async profiler to sentry`() {
76+
val profiler = AsyncProfiler.getInstance()
77+
val file = Files.createTempFile("sentry-async-profiler-test", ".jfr")
78+
val command = String.format("start,jfr,wall=%s,file=%s", "9900us", file.absolutePathString())
79+
profiler.execute(command)
80+
profiler.execute("stop,jfr")
81+
82+
fixture.getSut()!!.convertFromFile(file.toAbsolutePath())
83+
file.deleteIfExists()
84+
}
85+
}

sentry-async-profiler/src/test/java/io/sentry/asyncprofiler/JavaContinuousProfilerTest.kt renamed to sentry-async-profiler/src/test/java/io/sentry/asyncprofiler/profiling/JavaContinuousProfilerTest.kt

Lines changed: 2 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
package io.sentry.asyncprofiler
1+
package io.sentry.asyncprofiler.profiling
22

33
import io.sentry.DataCategory
4-
import io.sentry.IConnectionStatusProvider
54
import io.sentry.ILogger
65
import io.sentry.IScopes
76
import io.sentry.ProfileLifecycle
@@ -11,7 +10,6 @@ import io.sentry.SentryOptions
1110
import io.sentry.SentryTracer
1211
import io.sentry.TracesSampler
1312
import io.sentry.TransactionContext
14-
import io.sentry.asyncprofiler.profiling.JavaContinuousProfiler
1513
import io.sentry.protocol.SentryId
1614
import io.sentry.test.DeferredExecutorService
1715
import io.sentry.transport.RateLimiter
@@ -22,9 +20,7 @@ import kotlin.test.Test
2220
import kotlin.test.assertEquals
2321
import kotlin.test.assertFalse
2422
import kotlin.test.assertTrue
25-
import kotlin.use
2623
import org.mockito.Mockito
27-
import org.mockito.Mockito.mockStatic
2824
import org.mockito.kotlin.any
2925
import org.mockito.kotlin.eq
3026
import org.mockito.kotlin.mock
@@ -41,7 +37,7 @@ class JavaContinuousProfilerTest {
4137
private class Fixture {
4238
private val mockDsn = "http://key@localhost/proj"
4339
val executor = DeferredExecutorService()
44-
val mockedSentry = mockStatic(Sentry::class.java)
40+
val mockedSentry = Mockito.mockStatic(Sentry::class.java)
4541
val mockLogger = mock<ILogger>()
4642
val mockTracesSampler = mock<TracesSampler>()
4743

@@ -281,25 +277,6 @@ class JavaContinuousProfilerTest {
281277
verify(fixture.mockLogger).log(eq(SentryLevel.ERROR), eq("Failed to start profiling: "), any())
282278
}
283279

284-
// @Test
285-
// fun `profiler stops profiling and clear scheduled job on close`() {
286-
// val profiler = fixture.getSut()
287-
// profiler.startProfiler(ProfileLifecycle.MANUAL, fixture.mockTracesSampler)
288-
// assertTrue(profiler.isRunning)
289-
//
290-
// profiler.close(true)
291-
// assertFalse(profiler.isRunning)
292-
//
293-
// // The timeout scheduled job should be cleared
294-
// val androidProfiler = profiler.getProperty<JavaContinuousProfiler?>("profiler")
295-
// val scheduledJob = androidProfiler?.getProperty<Future<*>?>("scheduledFinish")
296-
// assertNull(scheduledJob)
297-
//
298-
// val stopFuture = profiler.getStopFuture()
299-
// assertNotNull(stopFuture)
300-
// assertTrue(stopFuture.isCancelled || stopFuture.isDone)
301-
// }
302-
303280
@Test
304281
fun `profiler stops and restart for each chunk`() {
305282
val profiler = fixture.getSut()
@@ -407,24 +384,6 @@ class JavaContinuousProfilerTest {
407384
.log(eq(SentryLevel.WARNING), eq("SDK is rate limited. Stopping profiler."))
408385
}
409386

410-
@Test
411-
fun `profiler does not start when offline`() {
412-
val profiler =
413-
fixture.getSut {
414-
it.connectionStatusProvider = mock { provider ->
415-
whenever(provider.connectionStatus)
416-
.thenReturn(IConnectionStatusProvider.ConnectionStatus.DISCONNECTED)
417-
}
418-
}
419-
420-
// If the device is offline, the profiler should never start
421-
profiler.startProfiler(ProfileLifecycle.MANUAL, fixture.mockTracesSampler)
422-
assertFalse(profiler.isRunning)
423-
assertEquals(SentryId.EMPTY_ID, profiler.profilerId)
424-
verify(fixture.mockLogger)
425-
.log(eq(SentryLevel.WARNING), eq("Device is offline. Stopping profiler."))
426-
}
427-
428387
fun withMockScopes(closure: () -> Unit) =
429388
Mockito.mockStatic(Sentry::class.java).use {
430389
it.`when`<Any> { Sentry.getCurrentScopes() }.thenReturn(fixture.scopes)

sentry-samples/sentry-samples-spring-boot-jakarta/src/main/resources/application.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# NOTE: Replace the test DSN below with YOUR OWN DSN to see the events from this app in your Sentry project/dashboard
2-
sentry.dsn=https://08c961cc816946f89b4dd69b92e75979@sentry.bloder.dev/3
2+
sentry.dsn=https://502f25099c204a2fbf4cb16edc5975d1@o447951.ingest.sentry.io/5428563
33
sentry.send-default-pii=true
44
sentry.max-request-body-size=medium
55
# Sentry Spring Boot integration allows more fine-grained SentryOptions configuration

0 commit comments

Comments
 (0)