1818import static com .datadog .profiling .controller .ProfilingSupport .*;
1919import static com .datadog .profiling .controller .ProfilingSupport .isObjectCountParallelized ;
2020import static datadog .trace .api .Platform .isJavaVersionAtLeast ;
21- import static datadog .trace .api .config .ProfilingConfig .PROFILING_HEAP_HISTOGRAM_ENABLED ;
22- import static datadog .trace .api .config .ProfilingConfig .PROFILING_HEAP_HISTOGRAM_ENABLED_DEFAULT ;
23- import static datadog .trace .api .config .ProfilingConfig .PROFILING_HEAP_HISTOGRAM_MODE ;
24- import static datadog .trace .api .config .ProfilingConfig .PROFILING_HEAP_HISTOGRAM_MODE_DEFAULT ;
25- import static datadog .trace .api .config .ProfilingConfig .PROFILING_QUEUEING_TIME_ENABLED ;
26- import static datadog .trace .api .config .ProfilingConfig .PROFILING_QUEUEING_TIME_ENABLED_DEFAULT ;
27- import static datadog .trace .api .config .ProfilingConfig .PROFILING_QUEUEING_TIME_THRESHOLD_MILLIS ;
28- import static datadog .trace .api .config .ProfilingConfig .PROFILING_QUEUEING_TIME_THRESHOLD_MILLIS_DEFAULT ;
29- import static datadog .trace .api .config .ProfilingConfig .PROFILING_ULTRA_MINIMAL ;
21+ import static datadog .trace .api .config .ProfilingConfig .*;
3022
3123import com .datadog .profiling .controller .ConfigurationException ;
3224import com .datadog .profiling .controller .Controller ;
4335import datadog .trace .bootstrap .instrumentation .jfr .exceptions .ExceptionProfiling ;
4436import de .thetaphi .forbiddenapis .SuppressForbidden ;
4537import java .io .IOException ;
38+ import java .io .InputStream ;
4639import java .nio .file .Files ;
4740import java .nio .file .Path ;
4841import java .time .Duration ;
4942import java .util .Collections ;
5043import java .util .Map ;
44+ import java .util .Properties ;
45+ import java .util .Random ;
46+ import java .util .UUID ;
47+ import java .util .concurrent .Executors ;
48+ import java .util .concurrent .ScheduledExecutorService ;
49+ import java .util .concurrent .TimeUnit ;
50+ import java .util .stream .Collectors ;
51+ import jdk .jfr .Recording ;
5152import org .slf4j .Logger ;
5253import org .slf4j .LoggerFactory ;
5354
@@ -69,9 +70,83 @@ public final class OpenJdkController implements Controller {
6970 private final Map <String , String > recordingSettings ;
7071 private final boolean jfrStackDepthApplied ;
7172
73+ private final ScheduledExecutorService burstExecutor =
74+ Executors .newSingleThreadScheduledExecutor (
75+ r -> {
76+ Thread t = new Thread (r , "Burst Trace Scheduler" );
77+ t .setDaemon (true );
78+ return t ;
79+ });
80+
81+ private class ScheduledTask implements Runnable {
82+ private final Random rnd = new Random (UUID .randomUUID ().getLeastSignificantBits ());
83+ private boolean armed = false ;
84+ private final Map <String , String > settings ;
85+ private final Duration interval ;
86+ private final Duration duration ;
87+
88+ private ScheduledTask () {
89+ try {
90+ Properties props = new Properties ();
91+ try (InputStream is = OpenJdkController .class .getResourceAsStream ("/jfr/bursty.jfp" )) {
92+ props .load (is );
93+ }
94+ settings =
95+ props .entrySet ().stream ()
96+ .collect (Collectors .toMap (e -> (String ) e .getKey (), e -> (String ) e .getValue ()));
97+ this .interval =
98+ Duration .ofMillis (
99+ configProvider .getLong (
100+ PROFILING_BURST_INTERVAl_MS , PROFILING_BURST_INTERVAl_MS_DEFAULT ));
101+ this .duration =
102+ Duration .ofMillis (
103+ configProvider .getLong (
104+ PROFILING_BURST_DURATION_MS , PROFILING_BURST_DURATION_MS_DEFAULT ));
105+ } catch (IOException e ) {
106+ throw new RuntimeException (e );
107+ }
108+ }
109+
110+ @ Override
111+ public void run () {
112+ if (interval .toMillis () < 0 ) {
113+ // bursts are disabled
114+ return ;
115+ }
116+ double u = rnd .nextDouble ();
117+ long nextMs = Math .round (-(Math .log (u ) * interval .toMillis ())); // 5 mins average interval
118+ if (!armed ) {
119+ armed = true ;
120+ log .info ("Scheduling bursty tracing in {} ms" , nextMs );
121+ burstExecutor .schedule (this , nextMs , TimeUnit .MILLISECONDS );
122+ return ;
123+ } else {
124+ nextMs += 15_000 ; // offset the next start by the max recording duration
125+ }
126+ // Let's start a new recording with extremely detailed latency events.
127+ // This recording is not persisted and is automatically discarded.
128+ // However, due to have settings-merging works in JFR the thresholds
129+ // for latency events will be lowered also for the main recording.
130+ // Also, the thresholds will be restored when this recording ends,
131+ // automatically.
132+ log .info ("Burst Trace Scheduler is executing" );
133+ Recording recording = new Recording ();
134+ recording .setName ("Burst Trace" );
135+ recording .setSettings (settings );
136+ recording .setDuration (duration );
137+ recording .setToDisk (false );
138+ recording .setMaxSize (64 * 1024 );
139+ recording .start ();
140+ log .info ("Scheduling next bursty tracing in {} ms" , nextMs );
141+ burstExecutor .schedule (this , nextMs , TimeUnit .MILLISECONDS );
142+ }
143+ }
144+
72145 public static Controller instance (ConfigProvider configProvider )
73146 throws ConfigurationException , ClassNotFoundException {
74- return new OpenJdkController (configProvider );
147+ OpenJdkController ctrl = new OpenJdkController (configProvider );
148+ ctrl .startBurstTracing ();
149+ return ctrl ;
75150 }
76151
77152 /**
@@ -320,4 +395,8 @@ private int getConfiguredStackDepth(ConfigProvider configProvider) {
320395 return configProvider .getInteger (
321396 ProfilingConfig .PROFILING_STACKDEPTH , ProfilingConfig .PROFILING_STACKDEPTH_DEFAULT );
322397 }
398+
399+ private void startBurstTracing () {
400+ burstExecutor .schedule (new ScheduledTask (), 0 , TimeUnit .SECONDS );
401+ }
323402}
0 commit comments