Skip to content

Commit 0bff977

Browse files
Instrument Gradle Launcher to avoid overwriting org.gradle.jvmargs property
1 parent adf9784 commit 0bff977

File tree

3 files changed

+82
-2
lines changed

3 files changed

+82
-2
lines changed

dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/ProcessHierarchy.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,21 @@ public boolean isChild() {
5050
* not one of the supported build system processes.
5151
*/
5252
public boolean isHeadless() {
53-
return !isChild() && !isParent();
53+
return !isChild() && !isParent() && !isWrapper();
5454
}
5555

5656
private boolean isParent() {
5757
return isMavenParent() || isGradleDaemon();
5858
}
5959

60+
/**
61+
* Determines if current process is a wrapper that starts the build system. In other words a
62+
* process that is not a build system, and not a JVM that runs tests.
63+
*/
64+
private boolean isWrapper() {
65+
return isGradleLauncher();
66+
}
67+
6068
private boolean isMavenParent() {
6169
return System.getProperty("maven.home") != null
6270
&& System.getProperty("classworlds.conf") != null;
@@ -70,6 +78,12 @@ private boolean isGradleDaemon() {
7078
&& System.getProperties().getProperty("org.gradle.internal.worker.tmpdir") == null;
7179
}
7280

81+
private boolean isGradleLauncher() {
82+
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
83+
return contextClassLoader.getResource("org/gradle/launcher/Main.class") != null
84+
|| contextClassLoader.getResource("org/gradle/launcher/GradleMain.class") != null;
85+
}
86+
7387
@Nullable
7488
public InetSocketAddress getSignalServerAddress() {
7589
// System.getProperty is used rather than Config,

dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/test/ExecutionStrategy.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,11 @@ public TestRetryPolicy retryPolicy(TestIdentifier test) {
9999
public boolean isEarlyFlakeDetectionLimitReached() {
100100
int detectionsUsed = earlyFlakeDetectionsUsed.get();
101101
Collection<TestIdentifier> knownTests = executionSettings.getKnownTests();
102-
int totalTests = knownTests.size() + detectionsUsed;
102+
if (knownTests == null) {
103+
return false;
104+
}
103105

106+
int totalTests = knownTests.size() + detectionsUsed;
104107
EarlyFlakeDetectionSettings earlyFlakeDetectionSettings =
105108
executionSettings.getEarlyFlakeDetectionSettings();
106109
int threshold =
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package datadog.trace.instrumentation.gradle;
2+
3+
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
4+
5+
import com.google.auto.service.AutoService;
6+
import datadog.trace.agent.tooling.Instrumenter;
7+
import datadog.trace.agent.tooling.InstrumenterModule;
8+
import datadog.trace.api.Config;
9+
import java.io.File;
10+
import java.nio.file.Path;
11+
import java.util.Map;
12+
import java.util.Properties;
13+
import net.bytebuddy.asm.Advice;
14+
import org.gradle.process.internal.JvmOptions;
15+
16+
/**
17+
* This instrumentation targets Gradle Launcher, which is the process that is started with
18+
* `gradle`/`gradlew` commands. The launcher starts Gradle Daemon (if not started yet), which is a
19+
* long-lived process that actually runs builds. The instrumentation injects the tracer and its
20+
* config properties into Gradle Daemon JVM settings when the daemon is started.
21+
*/
22+
@AutoService(InstrumenterModule.class)
23+
public class GradleDaemonJvmOptionsInstrumentation extends InstrumenterModule.CiVisibility
24+
implements Instrumenter.ForSingleType {
25+
26+
public GradleDaemonJvmOptionsInstrumentation() {
27+
super("gradle", "gradle-daemon-jvm-options");
28+
}
29+
30+
@Override
31+
public String instrumentedType() {
32+
return "org.gradle.launcher.daemon.configuration.DaemonJvmOptions";
33+
}
34+
35+
@Override
36+
public void methodAdvice(MethodTransformer transformer) {
37+
transformer.applyAdvice(
38+
isConstructor(),
39+
GradleDaemonJvmOptionsInstrumentation.class.getName() + "$InjectJavaAgent");
40+
}
41+
42+
public static class InjectJavaAgent {
43+
@Advice.OnMethodExit(suppress = Throwable.class)
44+
public static void injectJavaAgent(@Advice.This final JvmOptions daemonJvmOptions) {
45+
File agentJar = Config.get().getCiVisibilityAgentJarFile();
46+
Path agentJarPath = agentJar.toPath();
47+
48+
StringBuilder agentArg = new StringBuilder("-javaagent:").append(agentJarPath).append('=');
49+
50+
Properties systemProperties = System.getProperties();
51+
for (Map.Entry<Object, Object> e : systemProperties.entrySet()) {
52+
String propertyName = (String) e.getKey();
53+
Object propertyValue = e.getValue();
54+
if (propertyName.startsWith(Config.PREFIX)) {
55+
daemonJvmOptions.systemProperty(propertyName, propertyValue);
56+
agentArg.append(propertyName).append('=').append(propertyValue).append(',');
57+
}
58+
}
59+
60+
daemonJvmOptions.jvmArgs(agentArg);
61+
}
62+
}
63+
}

0 commit comments

Comments
 (0)