3
3
import static datadog .environment .JavaVirtualMachine .isJavaVersionAtLeast ;
4
4
import static datadog .environment .JavaVirtualMachine .isOracleJDK8 ;
5
5
import static datadog .trace .api .ConfigDefaults .DEFAULT_STARTUP_LOGS_ENABLED ;
6
+ import static datadog .trace .api .telemetry .LogCollector .SEND_TELEMETRY ;
6
7
import static datadog .trace .bootstrap .Library .WILDFLY ;
7
8
import static datadog .trace .bootstrap .Library .detectLibraries ;
8
9
import static datadog .trace .util .AgentThreadFactory .AgentThread .JMX_STARTUP ;
21
22
import datadog .trace .api .appsec .AppSecEventTracker ;
22
23
import datadog .trace .api .config .AppSecConfig ;
23
24
import datadog .trace .api .config .CiVisibilityConfig ;
25
+ import datadog .trace .api .config .CrashTrackingConfig ;
24
26
import datadog .trace .api .config .CwsConfig ;
25
27
import datadog .trace .api .config .DebuggerConfig ;
26
28
import datadog .trace .api .config .GeneralConfig ;
56
58
import java .net .URISyntaxException ;
57
59
import java .net .URL ;
58
60
import java .security .CodeSource ;
61
+ import java .util .Arrays ;
59
62
import java .util .EnumSet ;
60
63
import java .util .concurrent .TimeUnit ;
61
64
import java .util .concurrent .atomic .AtomicBoolean ;
@@ -97,6 +100,9 @@ private enum AgentFeature {
97
100
TRACING (TraceInstrumentationConfig .TRACE_ENABLED , true ),
98
101
JMXFETCH (JmxFetchConfig .JMX_FETCH_ENABLED , true ),
99
102
STARTUP_LOGS (GeneralConfig .STARTUP_LOGS_ENABLED , DEFAULT_STARTUP_LOGS_ENABLED ),
103
+ CRASH_TRACKING (
104
+ CrashTrackingConfig .CRASH_TRACKING_ENABLED ,
105
+ CrashTrackingConfig .CRASH_TRACKING_ENABLED_DEFAULT ),
100
106
PROFILING (ProfilingConfig .PROFILING_ENABLED , false ),
101
107
APPSEC (AppSecConfig .APPSEC_ENABLED , false ),
102
108
IAST (IastConfig .IAST_ENABLED , false ),
@@ -150,9 +156,11 @@ public boolean isEnabledByDefault() {
150
156
private static ClassLoader AGENT_CLASSLOADER = null ;
151
157
152
158
private static volatile Runnable PROFILER_INIT_AFTER_JMX = null ;
159
+ private static volatile Runnable CRASHTRACKER_INIT_AFTER_JMX = null ;
153
160
154
161
private static boolean jmxFetchEnabled = true ;
155
162
private static boolean profilingEnabled = false ;
163
+ private static boolean crashTrackingEnabled = false ;
156
164
private static boolean appSecEnabled ;
157
165
private static boolean appSecFullyDisabled ;
158
166
private static boolean remoteConfigEnabled = true ;
@@ -282,6 +290,7 @@ public static void start(
282
290
283
291
jmxFetchEnabled = isFeatureEnabled (AgentFeature .JMXFETCH );
284
292
profilingEnabled = isFeatureEnabled (AgentFeature .PROFILING );
293
+ crashTrackingEnabled = isFeatureEnabled (AgentFeature .CRASH_TRACKING );
285
294
usmEnabled = isFeatureEnabled (AgentFeature .USM );
286
295
appSecEnabled = isFeatureEnabled (AgentFeature .APPSEC );
287
296
appSecFullyDisabled = isFullyDisabled (AgentFeature .APPSEC );
@@ -328,13 +337,7 @@ public static void start(
328
337
// Profiling can not run early on Oracle JDK 8 because it will cause JFR initialization
329
338
// deadlock.
330
339
// Oracle JDK 8 JFR controller requires JMX so register an 'after-jmx-initialized' callback.
331
- PROFILER_INIT_AFTER_JMX =
332
- new Runnable () {
333
- @ Override
334
- public void run () {
335
- startProfilingAgent (false , inst );
336
- }
337
- };
340
+ PROFILER_INIT_AFTER_JMX = () -> startProfilingAgent (false , inst );
338
341
}
339
342
}
340
343
@@ -347,6 +350,14 @@ public void run() {
347
350
* when it will happen after the class transformers were added.
348
351
*/
349
352
AgentTaskScheduler .initialize ();
353
+
354
+ // We need to run the crashtracking initialization after all the config has been resolved and
355
+ // task scheduler initialized
356
+ if (crashTrackingEnabled ) {
357
+ StaticEventLogger .begin ("crashtracking" );
358
+ startCrashTracking ();
359
+ StaticEventLogger .end ("crashtracking" );
360
+ }
350
361
startDatadogAgent (initTelemetry , inst );
351
362
352
363
final EnumSet <Library > libraries = detectLibraries (log );
@@ -741,6 +752,29 @@ private static synchronized void installDatadogTracer(
741
752
StaticEventLogger .end ("GlobalTracer" );
742
753
}
743
754
755
+ private static void startCrashTracking () {
756
+ if (isJavaVersionAtLeast (9 )) {
757
+ // it is safe to initialize crashtracking early
758
+ // since it can take 100ms+ to initialize the native library we will defer the initialization
759
+ // ... unless we request early start with the debug config flag
760
+ boolean forceEarlyStart = CrashTrackingConfig .CRASH_TRACKING_START_EARLY_DEFAULT ;
761
+ String forceEarlyStartStr =
762
+ ddGetProperty ("dd." + CrashTrackingConfig .CRASH_TRACKING_START_EARLY );
763
+ if (forceEarlyStartStr != null ) {
764
+ forceEarlyStart = Boolean .parseBoolean (forceEarlyStartStr );
765
+ }
766
+ if (forceEarlyStart ) {
767
+ initializeCrashTrackingDefault ();
768
+ } else {
769
+ AgentTaskScheduler .INSTANCE .execute (Agent ::initializeCrashTrackingDefault );
770
+ }
771
+ } else {
772
+ // for Java 8 we are relying on JMX to give us the process PID
773
+ // we need to delay the crash tracking initialization until JMX is available
774
+ CRASHTRACKER_INIT_AFTER_JMX = Agent ::initializeDelayedCrashTracking ;
775
+ }
776
+ }
777
+
744
778
private static void scheduleJmxStart (final int jmxStartDelay ) {
745
779
if (jmxStartDelay > 0 ) {
746
780
AgentTaskScheduler .INSTANCE .scheduleWithJitter (
@@ -764,25 +798,41 @@ private static synchronized void startJmx() {
764
798
if (jmxStarting .getAndSet (true )) {
765
799
return ; // another thread is already in startJmx
766
800
}
767
- // error tracking initialization relies on JMX being available
768
- initializeErrorTracking ();
769
801
if (jmxFetchEnabled ) {
770
802
startJmxFetch ();
771
803
}
772
804
initializeJmxSystemAccessProvider (AGENT_CLASSLOADER );
805
+ if (crashTrackingEnabled && CRASHTRACKER_INIT_AFTER_JMX != null ) {
806
+ try {
807
+ CRASHTRACKER_INIT_AFTER_JMX .run ();
808
+ } finally {
809
+ CRASHTRACKER_INIT_AFTER_JMX = null ;
810
+ }
811
+ }
773
812
if (profilingEnabled ) {
774
813
registerDeadlockDetectionEvent ();
775
814
registerSmapEntryEvent ();
776
815
if (PROFILER_INIT_AFTER_JMX != null ) {
777
- if (getJmxStartDelay () == 0 ) {
778
- log .debug ("Waiting for profiler initialization" );
779
- AgentTaskScheduler .INSTANCE .scheduleWithJitter (
780
- PROFILER_INIT_AFTER_JMX , 500 , TimeUnit .MILLISECONDS );
781
- } else {
782
- log .debug ("Initializing profiler" );
783
- PROFILER_INIT_AFTER_JMX .run ();
816
+ try {
817
+ /*
818
+ When getJmxStartDelay() is set to 0 we will attempt to initialize the JMX subsystem as soon as available.
819
+ But, this can cause issues with JFR as it needs some 'grace period' after JMX is ready. That's why we are
820
+ re-scheduling the profiler initialization code just a tad later.
821
+
822
+ If the jmx start delay is set, we are already delayed relative to the jmx init so we can just plainly
823
+ run the initialization code.
824
+ */
825
+ if (getJmxStartDelay () == 0 ) {
826
+ log .debug ("Waiting for profiler initialization" );
827
+ AgentTaskScheduler .INSTANCE .scheduleWithJitter (
828
+ PROFILER_INIT_AFTER_JMX , 500 , TimeUnit .MILLISECONDS );
829
+ } else {
830
+ log .debug ("Initializing profiler" );
831
+ PROFILER_INIT_AFTER_JMX .run ();
832
+ }
833
+ } finally {
834
+ PROFILER_INIT_AFTER_JMX = null ;
784
835
}
785
- PROFILER_INIT_AFTER_JMX = null ;
786
836
}
787
837
}
788
838
}
@@ -1042,16 +1092,63 @@ private static void stopTelemetry() {
1042
1092
}
1043
1093
}
1044
1094
1045
- private static void initializeErrorTracking () {
1095
+ private static void initializeDelayedCrashTracking () {
1096
+ initializeCrashTracking (true , isCrashTrackingAutoconfigEnabled ());
1097
+ }
1098
+
1099
+ private static void initializeDelayedCrashTrackingOnlyJmx () {
1100
+ initializeCrashTracking (true , false );
1101
+ }
1102
+
1103
+ private static void initializeCrashTrackingDefault () {
1104
+ initializeCrashTracking (false , isCrashTrackingAutoconfigEnabled ());
1105
+ }
1106
+
1107
+ private static boolean isCrashTrackingAutoconfigEnabled () {
1108
+ String enabledVal = ddGetProperty ("dd." + CrashTrackingConfig .CRASH_TRACKING_ENABLE_AUTOCONFIG );
1109
+ boolean enabled = CrashTrackingConfig .CRASH_TRACKING_ENABLE_AUTOCONFIG_DEFAULT ;
1110
+ if (enabledVal != null ) {
1111
+ enabled = Boolean .parseBoolean (enabledVal );
1112
+ } else {
1113
+ // If the property is not set, then we check if profiling is enabled
1114
+ enabled = profilingEnabled ;
1115
+ }
1116
+ return enabled ;
1117
+ }
1118
+
1119
+ private static void initializeCrashTracking (boolean delayed , boolean checkNative ) {
1046
1120
if (JavaVirtualMachine .isJ9 ()) {
1047
1121
// TODO currently crash tracking is supported only for HotSpot based JVMs
1048
1122
return ;
1049
1123
}
1124
+ log .debug ("Initializing crashtracking" );
1050
1125
try {
1051
- Class <?> clz = AGENT_CLASSLOADER .loadClass ("com.datadog.crashtracking.ScriptInitializer" );
1052
- clz .getMethod ("initialize" ).invoke (null );
1126
+ Class <?> clz = AGENT_CLASSLOADER .loadClass ("datadog.crashtracking.Initializer" );
1127
+ // first try to use the JVMAccess using the native library; unless `checkNative` is false
1128
+ Boolean rslt =
1129
+ checkNative && (Boolean ) clz .getMethod ("initialize" , boolean .class ).invoke (null , false );
1130
+ if (!rslt ) {
1131
+ if (delayed ) {
1132
+ // already delayed initialization, so no need to reschedule it again
1133
+ // just call initialize and force JMX
1134
+ rslt = (Boolean ) clz .getMethod ("initialize" , boolean .class ).invoke (null , true );
1135
+ } else {
1136
+ // delayed initialization, so we need to reschedule it and mark as delayed but do not
1137
+ // re-check the native library
1138
+ CRASHTRACKER_INIT_AFTER_JMX = Agent ::initializeDelayedCrashTrackingOnlyJmx ;
1139
+ rslt = null ; // we will initialize it later
1140
+ }
1141
+ }
1142
+ if (rslt == null ) {
1143
+ log .debug ("Crashtracking initialization delayed until JMX is available" );
1144
+ } else if (rslt ) {
1145
+ log .debug ("Crashtracking initialized" );
1146
+ } else {
1147
+ log .debug (
1148
+ SEND_TELEMETRY , "Crashtracking failed to initialize. No additional details available." );
1149
+ }
1053
1150
} catch (Throwable t ) {
1054
- log .debug ("Unable to initialize crash uploader " , t );
1151
+ log .debug (SEND_TELEMETRY , "Unable to initialize crashtracking " , t );
1055
1152
}
1056
1153
}
1057
1154
@@ -1150,8 +1247,11 @@ public void withTracer(TracerAPI tracer) {
1150
1247
}
1151
1248
});
1152
1249
}
1153
- } catch (final Throwable ex ) {
1154
- log .error ("Throwable thrown while starting profiling agent" , ex );
1250
+ } catch (final Throwable t ) {
1251
+ log .error (
1252
+ SEND_TELEMETRY ,
1253
+ "Throwable thrown while starting profiling agent "
1254
+ + Arrays .toString (t .getCause ().getStackTrace ()));
1155
1255
} finally {
1156
1256
Thread .currentThread ().setContextClassLoader (contextLoader );
1157
1257
}
0 commit comments