Change agent jar inclusion in tests that launch a process

The tests were the main problem. By using a different approach to pass in the agent jar, the TracingAgent code can be much simpler
This commit is contained in:
Laplie Anderson 2019-08-05 12:06:43 -04:00
parent a9d0d2cbbe
commit ba6ff678db
6 changed files with 34 additions and 79 deletions

View File

@ -121,15 +121,11 @@ dependencies {
tasks.withType(Test).configureEach { tasks.withType(Test).configureEach {
jvmArgs "-Ddd.service.name=java-agent-tests" jvmArgs "-Ddd.service.name=java-agent-tests"
jvmArgs "-Ddd.writer.type=LoggingWriter" jvmArgs "-Ddd.writer.type=LoggingWriter"
jvmArgs "-Dagent.jar.to.forward=${shadowJar.archivePath}"
// Multi-threaded logging seems to be causing deadlocks with Gradle's log capture. // Multi-threaded logging seems to be causing deadlocks with Gradle's log capture.
// jvmArgs "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=debug" // jvmArgs "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=debug"
// jvmArgs "-Dorg.slf4j.simpleLogger.defaultLogLevel=debug" // jvmArgs "-Dorg.slf4j.simpleLogger.defaultLogLevel=debug"
doFirst {
// Defining here to allow jacoco to be first on the command line.
jvmArgs "-javaagent:${shadowJar.archivePath}"
}
testLogging { testLogging {
events "started" events "started"
} }

View File

@ -152,32 +152,16 @@ public class TracingAgent {
private static synchronized URL installBootstrapJar( private static synchronized URL installBootstrapJar(
final Instrumentation inst, final boolean usingCustomLogManager) final Instrumentation inst, final boolean usingCustomLogManager)
throws IOException, URISyntaxException, ClassNotFoundException { throws IOException, URISyntaxException, ClassNotFoundException {
URL bootstrapURL = null;
File bootstrapFile = null;
final CodeSource codeSource = TracingAgent.class.getProtectionDomain().getCodeSource(); final CodeSource codeSource = TracingAgent.class.getProtectionDomain().getCodeSource();
if (codeSource != null) { if (codeSource != null) {
bootstrapURL = codeSource.getLocation(); final URL bootstrapURL = codeSource.getLocation();
bootstrapFile = new File(bootstrapURL.toURI()); inst.appendToBootstrapClassLoaderSearch(new JarFile(new File(bootstrapURL.toURI())));
return bootstrapURL;
} else {
throw new RuntimeException("Unable to install bootstrap jar. Code source not found");
} }
if (bootstrapFile == null || bootstrapFile.isDirectory()) {
// At most one of ( DatadogClassLoader.class , TracingAgent.class ) has a CodeSource that
// doesn't lead to the agent jar
bootstrapURL =
ClassLoader.getSystemClassLoader()
.loadClass("datadog.trace.bootstrap.DatadogClassLoader")
.getProtectionDomain()
.getCodeSource()
.getLocation();
bootstrapFile = new File(bootstrapURL.toURI());
}
inst.appendToBootstrapClassLoaderSearch(new JarFile(bootstrapFile));
return bootstrapURL;
} }
/** /**
@ -186,11 +170,11 @@ public class TracingAgent {
* *
* @param innerJarFilename Filename of internal jar to use for the classpath of the datadog * @param innerJarFilename Filename of internal jar to use for the classpath of the datadog
* classloader * classloader
* @param bootStrapURL * @param bootstrapURL
* @return Datadog Classloader * @return Datadog Classloader
*/ */
private static ClassLoader createDatadogClassLoader( private static ClassLoader createDatadogClassLoader(
final String innerJarFilename, final URL bootStrapURL) throws Exception { final String innerJarFilename, final URL bootstrapURL) throws Exception {
final ClassLoader agentParent; final ClassLoader agentParent;
final String javaVersion = System.getProperty("java.version"); final String javaVersion = System.getProperty("java.version");
if (javaVersion.startsWith("1.7") || javaVersion.startsWith("1.8")) { if (javaVersion.startsWith("1.7") || javaVersion.startsWith("1.8")) {
@ -204,7 +188,7 @@ public class TracingAgent {
ClassLoader.getSystemClassLoader().loadClass("datadog.trace.bootstrap.DatadogClassLoader"); ClassLoader.getSystemClassLoader().loadClass("datadog.trace.bootstrap.DatadogClassLoader");
final Constructor constructor = final Constructor constructor =
loaderClass.getDeclaredConstructor(URL.class, String.class, ClassLoader.class); loaderClass.getDeclaredConstructor(URL.class, String.class, ClassLoader.class);
return (ClassLoader) constructor.newInstance(bootStrapURL, innerJarFilename, agentParent); return (ClassLoader) constructor.newInstance(bootstrapURL, innerJarFilename, agentParent);
} }
private static ClassLoader getPlatformClassLoader() private static ClassLoader getPlatformClassLoader()

View File

@ -2,31 +2,15 @@ package datadog.trace.agent
import datadog.trace.agent.test.IntegrationTestUtils import datadog.trace.agent.test.IntegrationTestUtils
import jvmbootstraptest.AgentLoadedChecker import jvmbootstraptest.AgentLoadedChecker
import spock.lang.Shared
import spock.lang.Specification import spock.lang.Specification
import java.lang.management.ManagementFactory
import java.lang.management.RuntimeMXBean
class AgentLoadedIntoBootstrapTest extends Specification { class AgentLoadedIntoBootstrapTest extends Specification {
@Shared
private agentArg
def setupSpec() {
final RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean()
for (String arg : runtimeMxBean.getInputArguments()) {
if (arg.startsWith("-javaagent")) {
agentArg = arg
break
}
}
assert agentArg != null
}
def "Agent loads in when separate jvm is launched"() { def "Agent loads in when separate jvm is launched"() {
expect: expect:
IntegrationTestUtils.runOnSeparateJvm(AgentLoadedChecker.getName() IntegrationTestUtils.runOnSeparateJvm(AgentLoadedChecker.getName()
, [agentArg] as String[] , "" as String[]
, "" as String[] , "" as String[]
, [:] , [:]
, true) == 0 , true) == 0

View File

@ -4,38 +4,19 @@ import datadog.trace.agent.test.IntegrationTestUtils
import jvmbootstraptest.LogManagerSetter import jvmbootstraptest.LogManagerSetter
import spock.lang.Requires import spock.lang.Requires
import spock.lang.Retry import spock.lang.Retry
import spock.lang.Shared
import spock.lang.Specification import spock.lang.Specification
import spock.lang.Timeout import spock.lang.Timeout
import java.lang.management.ManagementFactory
import java.lang.management.RuntimeMXBean
// Note: this test is fails on IBM JVM, we would need to investigate this at some point // Note: this test is fails on IBM JVM, we would need to investigate this at some point
@Requires({ !System.getProperty("java.vm.name").contains("IBM J9 VM") }) @Requires({ !System.getProperty("java.vm.name").contains("IBM J9 VM") })
@Retry @Retry
@Timeout(30) @Timeout(30)
class CustomLogManagerTest extends Specification { class CustomLogManagerTest extends Specification {
// Run all tests using forked jvm because groovy has already set the global log manager // Run all tests using forked jvm because groovy has already set the global log manager
@Shared
private agentArg
def setupSpec() {
final RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean()
for (String arg : runtimeMxBean.getInputArguments()) {
if (arg.startsWith("-javaagent")) {
agentArg = arg
break
}
}
assert agentArg != null
}
def "jmxfetch starts up in premain with no custom log manager set"() { def "jmxfetch starts up in premain with no custom log manager set"() {
expect: expect:
IntegrationTestUtils.runOnSeparateJvm(LogManagerSetter.getName() IntegrationTestUtils.runOnSeparateJvm(LogManagerSetter.getName()
, [agentArg, "-Ddd.jmxfetch.enabled=true", "-Ddd.jmxfetch.refresh-beans-period=1", "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=off"] as String[] , ["-Ddd.jmxfetch.enabled=true", "-Ddd.jmxfetch.refresh-beans-period=1", "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=off"] as String[]
, "" as String[] , "" as String[]
, [:] , [:]
, true) == 0 , true) == 0
@ -44,7 +25,7 @@ class CustomLogManagerTest extends Specification {
def "jmxfetch starts up in premain if configured log manager on system classpath"() { def "jmxfetch starts up in premain if configured log manager on system classpath"() {
expect: expect:
IntegrationTestUtils.runOnSeparateJvm(LogManagerSetter.getName() IntegrationTestUtils.runOnSeparateJvm(LogManagerSetter.getName()
, [agentArg, "-Ddd.jmxfetch.enabled=true", "-Ddd.jmxfetch.refresh-beans-period=1", "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=off", "-Djava.util.logging.manager=jvmbootstraptest.CustomLogManager"] as String[] , ["-Ddd.jmxfetch.enabled=true", "-Ddd.jmxfetch.refresh-beans-period=1", "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=off", "-Djava.util.logging.manager=jvmbootstraptest.CustomLogManager"] as String[]
, "" as String[] , "" as String[]
, [:] , [:]
, true) == 0 , true) == 0
@ -53,7 +34,7 @@ class CustomLogManagerTest extends Specification {
def "jmxfetch startup is delayed with java.util.logging.manager sysprop"() { def "jmxfetch startup is delayed with java.util.logging.manager sysprop"() {
expect: expect:
IntegrationTestUtils.runOnSeparateJvm(LogManagerSetter.getName() IntegrationTestUtils.runOnSeparateJvm(LogManagerSetter.getName()
, [agentArg, "-Ddd.jmxfetch.enabled=true", "-Ddd.jmxfetch.refresh-beans-period=1", "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=off", "-Djava.util.logging.manager=jvmbootstraptest.MissingLogManager"] as String[] , ["-Ddd.jmxfetch.enabled=true", "-Ddd.jmxfetch.refresh-beans-period=1", "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=off", "-Djava.util.logging.manager=jvmbootstraptest.MissingLogManager"] as String[]
, "" as String[] , "" as String[]
, [:] , [:]
, true) == 0 , true) == 0
@ -62,7 +43,7 @@ class CustomLogManagerTest extends Specification {
def "jmxfetch startup delayed with tracer custom log manager setting"() { def "jmxfetch startup delayed with tracer custom log manager setting"() {
expect: expect:
IntegrationTestUtils.runOnSeparateJvm(LogManagerSetter.getName() IntegrationTestUtils.runOnSeparateJvm(LogManagerSetter.getName()
, [agentArg, "-Ddd.jmxfetch.enabled=true", "-Ddd.jmxfetch.refresh-beans-period=1", "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=off", "-Ddd.app.customlogmanager=true"] as String[] , ["-Ddd.jmxfetch.enabled=true", "-Ddd.jmxfetch.refresh-beans-period=1", "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=off", "-Ddd.app.customlogmanager=true"] as String[]
, "" as String[] , "" as String[]
, [:] , [:]
, true) == 0 , true) == 0
@ -71,7 +52,7 @@ class CustomLogManagerTest extends Specification {
def "jmxfetch startup delayed with JBOSS_HOME environment variable"() { def "jmxfetch startup delayed with JBOSS_HOME environment variable"() {
expect: expect:
IntegrationTestUtils.runOnSeparateJvm(LogManagerSetter.getName() IntegrationTestUtils.runOnSeparateJvm(LogManagerSetter.getName()
, [agentArg, "-Ddd.jmxfetch.enabled=true", "-Ddd.jmxfetch.refresh-beans-period=1", "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=off", "-Ddd.app.customlogmanager=true"] as String[] , ["-Ddd.jmxfetch.enabled=true", "-Ddd.jmxfetch.refresh-beans-period=1", "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=off", "-Ddd.app.customlogmanager=true"] as String[]
, "" as String[] , "" as String[]
, ["JBOSS_HOME": "/"] , ["JBOSS_HOME": "/"]
, true) == 0 , true) == 0
@ -80,7 +61,7 @@ class CustomLogManagerTest extends Specification {
def "jmxfetch startup in premain forced by customlogmanager=false"() { def "jmxfetch startup in premain forced by customlogmanager=false"() {
expect: expect:
IntegrationTestUtils.runOnSeparateJvm(LogManagerSetter.getName() IntegrationTestUtils.runOnSeparateJvm(LogManagerSetter.getName()
, [agentArg, "-Ddd.jmxfetch.enabled=true", "-Ddd.jmxfetch.refresh-beans-period=1", "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=off", "-Ddd.app.customlogmanager=false", "-Djava.util.logging.manager=jvmbootstraptest.CustomLogManager"] as String[] , ["-Ddd.jmxfetch.enabled=true", "-Ddd.jmxfetch.refresh-beans-period=1", "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=off", "-Ddd.app.customlogmanager=false", "-Djava.util.logging.manager=jvmbootstraptest.CustomLogManager"] as String[]
, "" as String[] , "" as String[]
, ["JBOSS_HOME": "/"] , ["JBOSS_HOME": "/"]
, true) == 0 , true) == 0

View File

@ -190,16 +190,29 @@ public class IntegrationTestUtils {
final Map<String, String> envVars, final Map<String, String> envVars,
final boolean printOutputStreams) final boolean printOutputStreams)
throws Exception { throws Exception {
final String separator = System.getProperty("file.separator"); final String separator = System.getProperty("file.separator");
final String classpath = System.getProperty("java.class.path"); final String agentJar = System.getProperty("agent.jar.to.forward");
if (agentJar == null) {
throw new RuntimeException("agent.jar.to.forward property must be set");
}
final String classpath =
agentJar + System.getProperty("path.separator") + System.getProperty("java.class.path");
final String path = System.getProperty("java.home") + separator + "bin" + separator + "java"; final String path = System.getProperty("java.home") + separator + "bin" + separator + "java";
final List<String> vmArgsList = new ArrayList<>(Arrays.asList(jvmArgs));
vmArgsList.add("-javaagent:" + agentJar);
final List<String> commands = new ArrayList<>(); final List<String> commands = new ArrayList<>();
commands.add(path); commands.add(path);
commands.addAll(Arrays.asList(jvmArgs)); commands.addAll(vmArgsList);
commands.add("-cp"); commands.add("-cp");
commands.add(classpath); commands.add(classpath);
commands.add(mainClassName); commands.add(mainClassName);
commands.addAll(Arrays.asList(mainMethodArgs)); commands.addAll(Arrays.asList(mainMethodArgs));
final ProcessBuilder processBuilder = new ProcessBuilder(commands.toArray(new String[0])); final ProcessBuilder processBuilder = new ProcessBuilder(commands.toArray(new String[0]));
processBuilder.environment().putAll(envVars); processBuilder.environment().putAll(envVars);
final Process process = processBuilder.start(); final Process process = processBuilder.start();

View File

@ -2,12 +2,9 @@ package jvmbootstraptest;
import java.net.URL; import java.net.URL;
import java.net.URLClassLoader; import java.net.URLClassLoader;
import java.util.Arrays;
public class AgentLoadedChecker { public class AgentLoadedChecker {
public static void main(final String[] args) throws ClassNotFoundException { public static void main(final String[] args) throws ClassNotFoundException {
System.out.println(Arrays.toString(args));
// Empty classloader that delegates to bootstrap // Empty classloader that delegates to bootstrap
final URLClassLoader emptyClassLoader = new URLClassLoader(new URL[] {}, null); final URLClassLoader emptyClassLoader = new URLClassLoader(new URL[] {}, null);
final Class agentClass = emptyClassLoader.loadClass("datadog.trace.agent.TracingAgent"); final Class agentClass = emptyClassLoader.loadClass("datadog.trace.agent.TracingAgent");