Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
f32782c
wip
lbloder Apr 25, 2025
4fd36d2
Merge branch 'main' into feat/poc-continuous-profiling
lbloder May 19, 2025
0e2a1b7
re-add data category profile_chunk, fix json naming, new converter ba…
lbloder May 19, 2025
558ff79
read java thread ids from jfr and use those instead of os thread ids,…
lbloder Jun 16, 2025
04c82de
adhere to sentry conventions re format null safety etc, fix compilation
lbloder Jun 23, 2025
f44e7fd
add profile-session-sample-rate to external options
lbloder Jun 24, 2025
8b4c71a
add platform as constructor param to ProfileChunk, wip: set java cont…
lbloder Jun 24, 2025
a44287b
add doubleToBigDecimal in JfrSample and ProfileChunk, same as we do i…
lbloder Jul 1, 2025
0b39f7e
Merge branch 'main' into feat/poc-continuous-profiling
lbloder Jul 7, 2025
9cf7ef6
rename JfrProfile to SentryProfile
lbloder Jul 8, 2025
5534204
move java profiling into its own module, load using SPI
lbloder Jul 8, 2025
cabc384
[WIP] use getProfilingTracesDirPath
lbloder Jul 11, 2025
ec59901
add missing build.gradle.kts for profiler module
lbloder Jul 11, 2025
54c3a8f
Merge branch 'main' into feat/poc-continuous-profiling
lbloder Jul 11, 2025
543fc40
WIP continuous profiling in trace mode
lbloder Jul 11, 2025
fba1881
cleanup unused classes from vendor, cleanup packages
lbloder Jul 15, 2025
55df61c
allow setting profiling-traces-dir-path independently from cache dir …
lbloder Jul 15, 2025
9bcd4ea
use profileChunk.platform to decide how to deal with the chunk instea…
lbloder Jul 15, 2025
3f83146
port relevant AndroidContinuousProfilerTest tests to JavaContinuousPr…
lbloder Jul 15, 2025
4a7403a
add service loader tests for profiler and profile converter
lbloder Jul 15, 2025
03a20dd
remove old jfr test files
lbloder Jul 15, 2025
8a02f7b
Merge branch 'main' into feat/poc-continuous-profiling
lbloder Aug 5, 2025
642b213
Merge branch 'main' into feat/poc-continuous-profiling
lbloder Sep 12, 2025
6d11c36
Merge branch 'main' into feat/poc-continuous-profiling
lbloder Sep 22, 2025
9d71686
Merge branch 'main' into feat/poc-continuous-profiling
lbloder Sep 26, 2025
1f73970
Merge branch 'main' into feat/poc-continuous-profiling
lbloder Sep 26, 2025
3acbd63
Support ProfileLifecycle.TRACE (#4576)
lbloder Sep 26, 2025
1e998bd
change profiler api from Path to String to keep Android Api 21 compat…
lbloder Sep 29, 2025
7cc9386
Merge branch 'main' into feat/poc-continuous-profiling
lbloder Sep 29, 2025
0c93c2b
mark all profiling related classes as internal
lbloder Sep 29, 2025
b39bb60
Merge branch 'feat/poc-continuous-profiling' of github.com:getsentry/…
lbloder Sep 29, 2025
c404b77
Format code
getsentry-bot Sep 29, 2025
3d93ba0
mark Sentry profile classes internal
lbloder Sep 29, 2025
92b2812
Merge branch 'feat/poc-continuous-profiling' of github.com:getsentry/…
lbloder Sep 29, 2025
1321c03
Format code
getsentry-bot Sep 29, 2025
2298748
mark SentryThreadMetadata internal
lbloder Sep 30, 2025
8e027d8
Merge branch 'feat/poc-continuous-profiling' of github.com:getsentry/…
lbloder Sep 30, 2025
7d29d34
Merge branch 'main' into feat/poc-continuous-profiling
lbloder Sep 30, 2025
9a11e02
add changelog entry
lbloder Sep 30, 2025
e5ffefb
Format code
getsentry-bot Sep 30, 2025
b065ef1
re-add asyncprofiler classes, cr changes
lbloder Sep 30, 2025
13dbc8e
bump api
lbloder Sep 30, 2025
40c6756
Merge branch 'feat/poc-continuous-profiling' of github.com:getsentry/…
lbloder Sep 30, 2025
91e244d
detect dangerous/invalid chars in profiling directory name
lbloder Sep 30, 2025
81ad659
Merge branch 'main' into feat/poc-continuous-profiling
lbloder Sep 30, 2025
66413a0
ignore vendored profile conversion code in codecov
lbloder Sep 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

## Features
- Add support for continuous profiling of JVM applications on macOS and Linux ([#4556](https://github.com/getsentry/sentry-java/pull/4556))
- Sentry continuous profiling on the JVM is using async-profiler under the hood.

### Fixes

- Start performance collection on AppStart continuous profiling ([#4752](https://github.com/getsentry/sentry-java/pull/4752))
Expand Down
1 change: 1 addition & 0 deletions buildSrc/src/main/java/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ object Config {
val SENTRY_OKHTTP_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.okhttp"
val SENTRY_REACTOR_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.reactor"
val SENTRY_KOTLIN_EXTENSIONS_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.kotlin-extensions"
val SENTRY_ASYNC_PROFILER_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.async-profiler"
val SENTRY_KTOR_CLIENT_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.ktor-client"
val group = "io.sentry"
val description = "SDK for sentry.io"
Expand Down
1 change: 1 addition & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ ignore:
- "sentry-system-test-support/*"
- "sentry-test-support/*"
- "sentry-samples/*"
- "sentry-async-profiler/src/main/java/io/sentry/asyncprofiler/vendor/asyncprofiler/**"
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,8 @@ private void stop(final boolean restartProfiler) {
chunkId,
endData.measurementsMap,
endData.traceFile,
startProfileChunkTimestamp));
startProfileChunkTimestamp,
ProfileChunk.PLATFORM_ANDROID));
}
}

Expand Down
336 changes: 336 additions & 0 deletions sentry-async-profiler/api/sentry-async-profiler.api
Original file line number Diff line number Diff line change
@@ -0,0 +1,336 @@
public final class io/sentry/asyncprofiler/BuildConfig {
public static final field SENTRY_ASYNC_PROFILER_SDK_NAME Ljava/lang/String;
public static final field VERSION_NAME Ljava/lang/String;
}

public final class io/sentry/asyncprofiler/convert/JfrAsyncProfilerToSentryProfileConverter : io/sentry/asyncprofiler/vendor/asyncprofiler/convert/JfrConverter {
public fun <init> (Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/JfrReader;Lio/sentry/asyncprofiler/vendor/asyncprofiler/convert/Arguments;Lio/sentry/SentryStackTraceFactory;Lio/sentry/ILogger;)V
public static fun convertFromFileStatic (Ljava/lang/String;)Lio/sentry/protocol/profiling/SentryProfile;
}

public final class io/sentry/asyncprofiler/convert/NonAggregatingEventCollector : io/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/EventCollector {
public fun <init> ()V
public fun afterChunk ()V
public fun beforeChunk ()V
public fun collect (Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/Event;)V
public fun finish ()Z
public fun forEach (Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/EventCollector$Visitor;)V
}

public final class io/sentry/asyncprofiler/profiling/JavaContinuousProfiler : io/sentry/IContinuousProfiler, io/sentry/transport/RateLimiter$IRateLimitObserver {
public fun <init> (Lio/sentry/ILogger;Ljava/lang/String;ILio/sentry/ISentryExecutorService;)V
public fun close (Z)V
public fun getChunkId ()Lio/sentry/protocol/SentryId;
public fun getProfilerId ()Lio/sentry/protocol/SentryId;
public fun getRootSpanCounter ()I
public fun isRunning ()Z
public fun onRateLimitChanged (Lio/sentry/transport/RateLimiter;)V
public fun reevaluateSampling ()V
public fun startProfiler (Lio/sentry/ProfileLifecycle;Lio/sentry/TracesSampler;)V
public fun stopProfiler (Lio/sentry/ProfileLifecycle;)V
}

public final class io/sentry/asyncprofiler/provider/AsyncProfilerContinuousProfilerProvider : io/sentry/profiling/JavaContinuousProfilerProvider {
public fun <init> ()V
public fun getContinuousProfiler (Lio/sentry/ILogger;Ljava/lang/String;ILio/sentry/ISentryExecutorService;)Lio/sentry/IContinuousProfiler;
}

public final class io/sentry/asyncprofiler/provider/AsyncProfilerProfileConverterProvider : io/sentry/profiling/JavaProfileConverterProvider {
public fun <init> ()V
public fun getProfileConverter ()Lio/sentry/IProfileConverter;
}

public final class io/sentry/asyncprofiler/provider/AsyncProfilerProfileConverterProvider$AsyncProfilerProfileConverter : io/sentry/IProfileConverter {
public fun <init> ()V
public fun convertFromFile (Ljava/lang/String;)Lio/sentry/protocol/profiling/SentryProfile;
}

public final class io/sentry/asyncprofiler/vendor/asyncprofiler/convert/Arguments {
public field alloc Z
public field bci Z
public field classify Z
public field cpu Z
public field dot Z
public field exclude Ljava/util/regex/Pattern;
public final field files Ljava/util/List;
public field from J
public field grain D
public field help Z
public field highlight Ljava/lang/String;
public field include Ljava/util/regex/Pattern;
public field inverted Z
public field leak Z
public field lines Z
public field live Z
public field lock Z
public field minwidth D
public field nativemem Z
public field norm Z
public field output Ljava/lang/String;
public field reverse Z
public field simple Z
public field skip I
public field state Ljava/lang/String;
public field threads Z
public field title Ljava/lang/String;
public field to J
public field total Z
public field wall Z
public fun <init> ([Ljava/lang/String;)V
}

public final class io/sentry/asyncprofiler/vendor/asyncprofiler/convert/Frame : java/util/HashMap {
public static final field TYPE_C1_COMPILED B
public static final field TYPE_CPP B
public static final field TYPE_INLINED B
public static final field TYPE_INTERPRETED B
public static final field TYPE_JIT_COMPILED B
public static final field TYPE_KERNEL B
public static final field TYPE_NATIVE B
}

public abstract class io/sentry/asyncprofiler/vendor/asyncprofiler/convert/JfrConverter {
protected final field args Lio/sentry/asyncprofiler/vendor/asyncprofiler/convert/Arguments;
protected final field collector Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/EventCollector;
protected final field jfr Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/JfrReader;
protected field methodNames Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/Dictionary;
public fun <init> (Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/JfrReader;Lio/sentry/asyncprofiler/vendor/asyncprofiler/convert/Arguments;)V
protected fun collectEvents ()V
public fun convert ()V
protected fun convertChunk ()V
protected fun createCollector (Lio/sentry/asyncprofiler/vendor/asyncprofiler/convert/Arguments;)Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/EventCollector;
public synthetic fun getCategory (Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/StackTrace;)Lio/sentry/asyncprofiler/vendor/asyncprofiler/convert/Classifier$Category;
public fun getClassName (J)Ljava/lang/String;
public fun getMethodName (JB)Ljava/lang/String;
public fun getPlainThreadName (I)Ljava/lang/String;
public fun getStackTraceElement (JBI)Ljava/lang/StackTraceElement;
public fun getThreadName (I)Ljava/lang/String;
protected fun getThreadStates (Z)Ljava/util/BitSet;
protected fun isNativeFrame (B)Z
protected fun toThreadState (Ljava/lang/String;)I
protected fun toTicks (J)J
}

protected abstract class io/sentry/asyncprofiler/vendor/asyncprofiler/convert/JfrConverter$AggregatedEventVisitor : io/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/EventCollector$Visitor {
protected fun <init> (Lio/sentry/asyncprofiler/vendor/asyncprofiler/convert/JfrConverter;)V
protected abstract fun visit (Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/Event;J)V
public final fun visit (Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/Event;JJ)V
}

public final class io/sentry/asyncprofiler/vendor/asyncprofiler/jfr/ClassRef {
public final field name J
public fun <init> (J)V
}

public final class io/sentry/asyncprofiler/vendor/asyncprofiler/jfr/Dictionary {
public fun <init> ()V
public fun <init> (I)V
public fun clear ()V
public fun forEach (Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/Dictionary$Visitor;)V
public fun get (J)Ljava/lang/Object;
public fun preallocate (I)I
public fun put (JLjava/lang/Object;)V
public fun size ()I
}

public abstract interface class io/sentry/asyncprofiler/vendor/asyncprofiler/jfr/Dictionary$Visitor {
public abstract fun visit (JLjava/lang/Object;)V
}

public final class io/sentry/asyncprofiler/vendor/asyncprofiler/jfr/DictionaryInt {
public fun <init> ()V
public fun <init> (I)V
public fun clear ()V
public fun forEach (Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/DictionaryInt$Visitor;)V
public fun get (J)I
public fun get (JI)I
public fun preallocate (I)I
public fun put (JI)V
}

public abstract interface class io/sentry/asyncprofiler/vendor/asyncprofiler/jfr/DictionaryInt$Visitor {
public abstract fun visit (JI)V
}

public final class io/sentry/asyncprofiler/vendor/asyncprofiler/jfr/JfrClass {
public fun field (Ljava/lang/String;)Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/JfrField;
}

public final class io/sentry/asyncprofiler/vendor/asyncprofiler/jfr/JfrField {
}

public final class io/sentry/asyncprofiler/vendor/asyncprofiler/jfr/JfrReader : java/io/Closeable {
public field chunkEndNanos J
public field chunkStartNanos J
public field chunkStartTicks J
public final field classes Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/Dictionary;
public field endNanos J
public final field enums Ljava/util/Map;
public final field javaThreads Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/Dictionary;
public final field methods Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/Dictionary;
public final field settings Ljava/util/Map;
public final field stackTraces Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/Dictionary;
public field startNanos J
public field startTicks J
public field stopAtNewChunk Z
public final field strings Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/Dictionary;
public final field symbols Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/Dictionary;
public final field threads Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/Dictionary;
public field ticksPerSec J
public final field types Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/Dictionary;
public final field typesByName Ljava/util/Map;
public fun <init> (Ljava/lang/String;)V
public fun <init> (Ljava/nio/ByteBuffer;)V
public fun close ()V
public fun durationNanos ()J
public fun eof ()Z
public fun getBytes ()[B
public fun getDouble ()D
public fun getEnumKey (Ljava/lang/String;Ljava/lang/String;)I
public fun getEnumValue (Ljava/lang/String;I)Ljava/lang/String;
public fun getFloat ()F
public fun getString ()Ljava/lang/String;
public fun getVarint ()I
public fun getVarlong ()J
public fun hasMoreChunks ()Z
public fun incomplete ()Z
public fun readAllEvents ()Ljava/util/List;
public fun readAllEvents (Ljava/lang/Class;)Ljava/util/List;
public fun readEvent ()Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/Event;
public fun readEvent (Ljava/lang/Class;)Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/Event;
public fun registerEvent (Ljava/lang/String;Ljava/lang/Class;)V
}

public final class io/sentry/asyncprofiler/vendor/asyncprofiler/jfr/MethodRef {
public final field cls J
public final field name J
public final field sig J
public fun <init> (JJJ)V
}

public final class io/sentry/asyncprofiler/vendor/asyncprofiler/jfr/StackTrace {
public final field locations [I
public final field methods [J
public final field types [B
public fun <init> ([J[B[I)V
}

public final class io/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/AllocationSample : io/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/Event {
public final field allocationSize J
public final field classId I
public final field tlabSize J
public fun <init> (JIIIJJ)V
public fun classId ()J
public fun hashCode ()I
public fun sameGroup (Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/Event;)Z
public fun value ()J
}

public final class io/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/CPULoad : io/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/Event {
public final field jvmSystem F
public final field jvmUser F
public final field machineTotal F
public fun <init> (Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/JfrReader;)V
}

public final class io/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/ContendedLock : io/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/Event {
public final field classId I
public final field duration J
public fun <init> (JIIJI)V
public fun classId ()J
public fun hashCode ()I
public fun sameGroup (Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/Event;)Z
public fun value ()J
}

public abstract class io/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/Event : java/lang/Comparable {
public final field stackTraceId I
public final field tid I
public final field time J
protected fun <init> (JII)V
public fun classId ()J
public fun compareTo (Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/Event;)I
public synthetic fun compareTo (Ljava/lang/Object;)I
public fun hashCode ()I
public fun sameGroup (Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/Event;)Z
public fun samples ()J
public fun toString ()Ljava/lang/String;
public fun value ()J
}

public final class io/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/EventAggregator : io/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/EventCollector {
public fun <init> (ZD)V
public fun afterChunk ()V
public fun beforeChunk ()V
public fun coarsen (D)V
public fun collect (Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/Event;)V
public fun collect (Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/Event;JJ)V
public fun finish ()Z
public fun forEach (Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/EventCollector$Visitor;)V
public fun size ()I
}

public abstract interface class io/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/EventCollector {
public abstract fun afterChunk ()V
public abstract fun beforeChunk ()V
public abstract fun collect (Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/Event;)V
public abstract fun finish ()Z
public abstract fun forEach (Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/EventCollector$Visitor;)V
}

public abstract interface class io/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/EventCollector$Visitor {
public abstract fun visit (Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/Event;JJ)V
}

public final class io/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/ExecutionSample : io/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/Event {
public final field samples I
public final field threadState I
public fun <init> (JIIII)V
public fun samples ()J
public fun value ()J
}

public final class io/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/GCHeapSummary : io/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/Event {
public final field afterGC Z
public final field committed J
public final field gcId I
public final field reserved J
public final field used J
public fun <init> (Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/JfrReader;)V
}

public final class io/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/LiveObject : io/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/Event {
public final field allocationSize J
public final field allocationTime J
public final field classId I
public fun <init> (JIIIJJ)V
public fun classId ()J
public fun hashCode ()I
public fun sameGroup (Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/Event;)Z
public fun value ()J
}

public final class io/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/MallocEvent : io/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/Event {
public final field address J
public final field size J
public fun <init> (JIIJJ)V
public fun value ()J
}

public final class io/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/MallocLeakAggregator : io/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/EventCollector {
public fun <init> (Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/EventCollector;)V
public fun afterChunk ()V
public fun beforeChunk ()V
public fun collect (Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/Event;)V
public fun finish ()Z
public fun forEach (Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/EventCollector$Visitor;)V
}

public final class io/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/ObjectCount : io/sentry/asyncprofiler/vendor/asyncprofiler/jfr/event/Event {
public final field classId I
public final field count J
public final field gcId I
public final field totalSize J
public fun <init> (Lio/sentry/asyncprofiler/vendor/asyncprofiler/jfr/JfrReader;)V
}

Loading
Loading