18
18
import static com .datadog .profiling .controller .ProfilingSupport .*;
19
19
import static com .datadog .profiling .controller .ProfilingSupport .isObjectCountParallelized ;
20
20
import 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 .*;
30
22
31
23
import com .datadog .profiling .controller .ConfigurationException ;
32
24
import com .datadog .profiling .controller .Controller ;
43
35
import datadog .trace .bootstrap .instrumentation .jfr .exceptions .ExceptionProfiling ;
44
36
import de .thetaphi .forbiddenapis .SuppressForbidden ;
45
37
import java .io .IOException ;
38
+ import java .io .InputStream ;
46
39
import java .nio .file .Files ;
47
40
import java .nio .file .Path ;
48
41
import java .time .Duration ;
49
42
import java .util .Collections ;
50
43
import 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 ;
51
52
import org .slf4j .Logger ;
52
53
import org .slf4j .LoggerFactory ;
53
54
@@ -69,9 +70,83 @@ public final class OpenJdkController implements Controller {
69
70
private final Map <String , String > recordingSettings ;
70
71
private final boolean jfrStackDepthApplied ;
71
72
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
+
72
145
public static Controller instance (ConfigProvider configProvider )
73
146
throws ConfigurationException , ClassNotFoundException {
74
- return new OpenJdkController (configProvider );
147
+ OpenJdkController ctrl = new OpenJdkController (configProvider );
148
+ ctrl .startBurstTracing ();
149
+ return ctrl ;
75
150
}
76
151
77
152
/**
@@ -320,4 +395,8 @@ private int getConfiguredStackDepth(ConfigProvider configProvider) {
320
395
return configProvider .getInteger (
321
396
ProfilingConfig .PROFILING_STACKDEPTH , ProfilingConfig .PROFILING_STACKDEPTH_DEFAULT );
322
397
}
398
+
399
+ private void startBurstTracing () {
400
+ burstExecutor .schedule (new ScheduledTask (), 0 , TimeUnit .SECONDS );
401
+ }
323
402
}
0 commit comments