Skip to content

Commit 20a44ad

Browse files
committed
Add automatic check for AOT training mode, based on CDS.isDumpingArchive()
1 parent 9925a51 commit 20a44ad

File tree

3 files changed

+63
-3
lines changed

3 files changed

+63
-3
lines changed

dd-java-agent/build.gradle

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,24 +23,34 @@ tasks.named("processResources") {
2323
dependsOn(includedJarFileTree)
2424
}
2525

26-
// The special pre-check should be compiled with Java 6 to detect unsupported Java versions
27-
// and prevent issues for users that still using them.
2826
sourceSets {
27+
// The special pre-check must be compiled with Java 6 to detect unsupported
28+
// Java versions and prevent issues for users that still using them.
2929
"main_java6" {
3030
java.srcDirs "${project.projectDir}/src/main/java6"
3131
}
32+
// Additional checks that use the Java 11 API.
33+
"main_java11" {
34+
java.srcDirs "${project.projectDir}/src/main/java11"
35+
}
3236
main.resources.srcDir(includedAgentDir)
3337
}
3438

3539
def java6CompileTask = tasks.named("compileMain_java6Java") {
3640
configureCompiler(it, 8, JavaVersion.VERSION_1_6)
3741
}
3842

43+
def java11CompileTask = tasks.named("compileMain_java11Java") {
44+
configureCompiler(it, 11)
45+
}
46+
3947
tasks.named("compileJava") {
4048
dependsOn(java6CompileTask)
49+
dependsOn(java11CompileTask)
4150
}
4251

4352
dependencies {
53+
implementation sourceSets.main_java11.output
4454
main_java6CompileOnly libs.forbiddenapis
4555
testImplementation sourceSets.main_java6.output
4656
}
@@ -249,6 +259,8 @@ includeShadowJar(traceShadowJar, 'trace', includedJarFileTree)
249259
tasks.named("shadowJar", ShadowJar) {
250260
// Include AgentPreCheck compiled with Java 6.
251261
from sourceSets.main_java6.output
262+
// Include additional checks compiled with Java 11.
263+
from sourceSets.main_java11.output
252264

253265
generalShadowJarConfig(it)
254266

dd-java-agent/src/main/java/datadog/trace/bootstrap/AgentBootstrap.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ private static void agentmainImpl(
143143
}
144144

145145
String agentClassName;
146-
if ("aot_training".equalsIgnoreCase(agentArgs)) {
146+
if (isAotTraining(agentArgs, inst)) {
147147
agentClassName = "datadog.trace.bootstrap.aot.TrainingAgent";
148148
} else {
149149
agentClassName = "datadog.trace.bootstrap.Agent";
@@ -429,4 +429,15 @@ private static void checkJarManifestMainClassIsThis(final URL jarUrl) throws IOE
429429
+ jarUrl
430430
+ "'. Make sure you don't have this .class-file anywhere, besides dd-java-agent.jar");
431431
}
432+
433+
/** Returns {@code true} if the JVM is training, i.e. writing to a CDS/AOT archive. */
434+
private static boolean isAotTraining(String agentArgs, Instrumentation inst) {
435+
if (!JavaVirtualMachine.isJavaVersionAtLeast(25)) {
436+
return false; // agent doesn't support training mode before Java 25
437+
} else if ("aot_training".equalsIgnoreCase(agentArgs)) {
438+
return true; // training mode explicitly enabled via -javaagent
439+
} else {
440+
return AdvancedAgentChecks.isAotTraining(inst); // check JVM status
441+
}
442+
}
432443
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package datadog.trace.bootstrap;
2+
3+
import static java.util.Collections.emptyMap;
4+
import static java.util.Collections.emptySet;
5+
import static java.util.Collections.singleton;
6+
import static java.util.Collections.singletonMap;
7+
8+
import java.lang.instrument.Instrumentation;
9+
import java.lang.reflect.Method;
10+
11+
/** Additional agent checks that require Java 11+. */
12+
public final class AdvancedAgentChecks {
13+
14+
/** Returns {@code true} if the JVM is writing to a CDS/AOT archive, i.e. is in training mode. */
15+
public static boolean isAotTraining(Instrumentation inst) {
16+
try {
17+
Class<?> cds = Class.forName("jdk.internal.misc.CDS");
18+
19+
// ensure the module containing CDS exports it to our unnamed module
20+
Module cdsModule = cds.getModule();
21+
Module unnamedModule = AdvancedAgentChecks.class.getClassLoader().getUnnamedModule();
22+
inst.redefineModule(
23+
cdsModule,
24+
emptySet(),
25+
singletonMap("jdk.internal.misc", singleton(unnamedModule)),
26+
emptyMap(),
27+
emptySet(),
28+
emptyMap());
29+
30+
// if the JVM is writing to a CDS/AOT archive then it's in training mode
31+
Method isDumpingArchive = cds.getMethod("isDumpingArchive");
32+
return (boolean) isDumpingArchive.invoke(null);
33+
} catch (Throwable ignore) {
34+
return false; // if we don't have access then assume we're not training
35+
}
36+
}
37+
}

0 commit comments

Comments
 (0)