Skip to content

Feat/poc continuous profiling #4556

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 21 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 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
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
1 change: 1 addition & 0 deletions buildSrc/src/main/java/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,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 group = "io.sentry"
val description = "SDK for sentry.io"
val versionNameProp = "versionName"
Expand Down
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,
"android"));
}
}

Expand Down
326 changes: 326 additions & 0 deletions sentry-async-profiler/api/sentry-async-profiler.api
Original file line number Diff line number Diff line change
@@ -0,0 +1,326 @@
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;)V
public static fun convertFromFileStatic (Ljava/nio/file/Path;)Lio/sentry/protocol/profiling/SentryProfile;
}

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 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/nio/file/Path;)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