Skip to content

Commit c234bb3

Browse files
committed
Add automatic check for AOT training mode, based on CDS.isDumpingArchive()
1 parent 07cf114 commit c234bb3

File tree

3 files changed

+66
-3
lines changed

3 files changed

+66
-3
lines changed

dd-java-agent/build.gradle

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,24 +23,35 @@ 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
54+
main_java11CompileOnly libs.forbiddenapis
4455
main_java6CompileOnly libs.forbiddenapis
4556
testImplementation sourceSets.main_java6.output
4657
}
@@ -249,6 +260,8 @@ includeShadowJar(traceShadowJar, 'trace', includedJarFileTree)
249260
tasks.named("shadowJar", ShadowJar) {
250261
// Include AgentPreCheck compiled with Java 6.
251262
from sourceSets.main_java6.output
263+
// Include additional checks compiled with Java 11.
264+
from sourceSets.main_java11.output
252265

253266
generalShadowJarConfig(it)
254267

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: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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 de.thetaphi.forbiddenapis.SuppressForbidden;
9+
import java.lang.instrument.Instrumentation;
10+
import java.lang.reflect.Method;
11+
12+
/** Additional agent checks that require Java 11+. */
13+
public final class AdvancedAgentChecks {
14+
15+
/** Returns {@code true} if the JVM is writing to a CDS/AOT archive, i.e. is in training mode. */
16+
@SuppressForbidden
17+
public static boolean isAotTraining(Instrumentation inst) {
18+
try {
19+
Class<?> cds = Class.forName("jdk.internal.misc.CDS");
20+
21+
// ensure the module containing CDS exports it to our unnamed module
22+
Module cdsModule = cds.getModule();
23+
Module unnamedModule = AdvancedAgentChecks.class.getClassLoader().getUnnamedModule();
24+
inst.redefineModule(
25+
cdsModule,
26+
emptySet(),
27+
singletonMap("jdk.internal.misc", singleton(unnamedModule)),
28+
emptyMap(),
29+
emptySet(),
30+
emptyMap());
31+
32+
// if the JVM is writing to a CDS/AOT archive then it's in training mode
33+
Method isDumpingArchive = cds.getMethod("isDumpingArchive");
34+
return (boolean) isDumpingArchive.invoke(null);
35+
} catch (Throwable ignore) {
36+
return false; // if we don't have access then assume we're not training
37+
}
38+
}
39+
}

0 commit comments

Comments
 (0)