diff --git a/buildSrc/src/main/kotlin/dd-trace-java.configure-tests.gradle.kts b/buildSrc/src/main/kotlin/dd-trace-java.configure-tests.gradle.kts index d0060bbf278..23289cb08e6 100644 --- a/buildSrc/src/main/kotlin/dd-trace-java.configure-tests.gradle.kts +++ b/buildSrc/src/main/kotlin/dd-trace-java.configure-tests.gradle.kts @@ -42,6 +42,10 @@ tasks.withType().configureEach { !rootProject.providers.gradleProperty("rerun.tests.${project.name}").isPresent } + // Trick to avoid on CI: "Couldn't flush user prefs: java.util.prefs.BackingStoreException: Couldn't get file lock." + // Use a task-specific user prefs directory + systemProperty("java.util.prefs.userRoot", "$buildDir/tmp/userPrefs/${name}") + // Split up tests that want to run forked in their own separate JVM for generated tasks if (name.startsWith("forkedTest") || name.endsWith("ForkedTest")) { setExcludes(emptyList()) diff --git a/dd-java-agent/agent-ci-visibility/civisibility-test-fixtures/src/main/groovy/datadog/trace/civisibility/CiVisibilitySmokeTest.groovy b/dd-java-agent/agent-ci-visibility/civisibility-test-fixtures/src/main/groovy/datadog/trace/civisibility/CiVisibilitySmokeTest.groovy index 31e4d6a67af..14a01165915 100644 --- a/dd-java-agent/agent-ci-visibility/civisibility-test-fixtures/src/main/groovy/datadog/trace/civisibility/CiVisibilitySmokeTest.groovy +++ b/dd-java-agent/agent-ci-visibility/civisibility-test-fixtures/src/main/groovy/datadog/trace/civisibility/CiVisibilitySmokeTest.groovy @@ -6,9 +6,13 @@ import datadog.trace.api.config.CiVisibilityConfig import datadog.trace.api.config.GeneralConfig import datadog.trace.api.config.TraceInstrumentationConfig import datadog.trace.api.config.TracerConfig +import java.nio.file.Paths import spock.lang.Specification +import spock.lang.TempDir import spock.util.environment.Jvm +import java.nio.file.Path + import static datadog.trace.util.ConfigStrings.propertyNameToSystemPropertyName abstract class CiVisibilitySmokeTest extends Specification { @@ -21,6 +25,9 @@ abstract class CiVisibilitySmokeTest extends Specification { private static final Map DEFAULT_TRACER_CONFIG = defaultJvmArguments() + @TempDir + protected Path prefsDir + protected static String buildJavaHome() { if (Jvm.current.isJava8()) { return System.getenv("JAVA_8_HOME") @@ -69,6 +76,9 @@ abstract class CiVisibilitySmokeTest extends Specification { protected List buildJvmArguments(String mockBackendIntakeUrl, String serviceName, Map additionalArgs) { List arguments = [] + + arguments += preventJulPrefsFileLock() + Map argMap = buildJvmArgMap(mockBackendIntakeUrl, serviceName, additionalArgs) // for convenience when debugging locally @@ -85,6 +95,28 @@ abstract class CiVisibilitySmokeTest extends Specification { return arguments } + /** + * Trick to prevent jul Prefs file lock issue on forked processes, in particular in CI which + * runs on Linux and have competing processes trying to write to it, including the Gradle daemon. + * + *

+   * Couldn't flush user prefs: java.util.prefs.BackingStoreException: Couldn't get file lock.
+   * 
+ * + * Note, some tests can setup arguments on spec level, so `prefsDir` will be `null` during + * `setupSpec()`. + */ + protected String preventJulPrefsFileLock() { + String prefsPath = (prefsDir ?: tempUserPrefsPath()).toAbsolutePath() + return "-Djava.util.prefs.userRoot=$prefsPath".toString() + } + + private static Path tempUserPrefsPath() { + String uniqueId = "${System.currentTimeMillis()}_${System.nanoTime()}_${Thread.currentThread().id}" + Path prefsPath = Paths.get(System.getProperty("java.io.tmpdir"), "gradle-test-userPrefs", uniqueId) + return prefsPath + } + protected verifyEventsAndCoverages(String projectName, String toolchain, String toolchainVersion, List> events, List> coverages, List additionalDynamicTags = []) { def additionalReplacements = ["content.meta.['test.toolchain']": "$toolchain:$toolchainVersion"] diff --git a/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/ProcessBuilderHelper.java b/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/ProcessBuilderHelper.java index 1b107f7c31d..5faf29bbaa5 100644 --- a/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/ProcessBuilderHelper.java +++ b/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/ProcessBuilderHelper.java @@ -28,6 +28,20 @@ public static ProcessBuilder createProcessBuilder( String mainClassName, String... params) { + // Trick to prevent jul preferences file lock issue on forked processes, in particular in CI + // which runs on Linux and have competing processes trying to write to it, including the + // Gradle daemon. + // + // Couldn't flush user prefs: java.util.prefs.BackingStoreException: Couldn't get file lock. + String prefsDir = + System.getProperty("java.io.tmpdir") + + File.separator + + "userPrefs" + + File.separator + + mainClassName + + "_" + + System.nanoTime(); + List baseCommand = Arrays.asList( javaPath(), @@ -37,7 +51,8 @@ public static ProcessBuilder createProcessBuilder( "-javaagent:" + agentShadowJar(), "-XX:ErrorFile=/tmp/hs_err_pid%p.log", "-Ddd.env=smoketest", - "-Ddd.version=99"); + "-Ddd.version=99", + "-Djava.util.prefs.userRoot=" + prefsDir); List command = new ArrayList<>(); command.addAll(baseCommand); diff --git a/dd-smoke-tests/gradle/src/test/groovy/datadog/smoketest/GradleDaemonSmokeTest.groovy b/dd-smoke-tests/gradle/src/test/groovy/datadog/smoketest/GradleDaemonSmokeTest.groovy index 45a7c5deb7c..ec808b56c0e 100644 --- a/dd-smoke-tests/gradle/src/test/groovy/datadog/smoketest/GradleDaemonSmokeTest.groovy +++ b/dd-smoke-tests/gradle/src/test/groovy/datadog/smoketest/GradleDaemonSmokeTest.groovy @@ -34,10 +34,6 @@ class GradleDaemonSmokeTest extends AbstractGradleTest { @TempDir Path testKitFolder - def setupSpec() { - givenGradleProperties() - } - @IgnoreIf(reason = "Jacoco plugin does not work with OpenJ9 in older Gradle versions", value = { JavaVirtualMachine.isJ9() }) @@ -79,6 +75,7 @@ class GradleDaemonSmokeTest extends AbstractGradleTest { def "test junit4 class ordering v#gradleVersion"() { givenGradleVersionIsCompatibleWithCurrentJvm(gradleVersion) givenGradleProjectFiles(projectName) + givenGradleProjectProperties() ensureDependenciesDownloaded(gradleVersion) mockBackend.givenKnownTests(true) @@ -124,6 +121,7 @@ class GradleDaemonSmokeTest extends AbstractGradleTest { givenGradleVersionIsCompatibleWithCurrentJvm(gradleVersion) givenConfigurationCacheIsCompatibleWithCurrentPlatform(configurationCache) givenGradleProjectFiles(projectName) + givenGradleProjectProperties() ensureDependenciesDownloaded(gradleVersion) mockBackend.givenFlakyRetries(flakyRetries) @@ -150,7 +148,7 @@ class GradleDaemonSmokeTest extends AbstractGradleTest { } } - private void givenGradleProperties() { + private void givenGradleProjectProperties() { assert new File(AGENT_JAR).isFile() def ddApiKeyPath = testKitFolder.resolve(".dd.api.key") @@ -173,7 +171,9 @@ class GradleDaemonSmokeTest extends AbstractGradleTest { def arguments = buildJvmArguments(mockBackend.intakeUrl, TEST_SERVICE_NAME, additionalArgs) def gradleProperties = "org.gradle.jvmargs=${arguments.join(" ")}".toString() - Files.write(testKitFolder.resolve("gradle.properties"), gradleProperties.getBytes()) + // Write to projectFolder (per-test) instead of testKitFolder (shared), so each + // Gradle daemon gets its own unique preference directory + Files.write(projectFolder.resolve("gradle.properties"), gradleProperties.getBytes()) } private BuildResult runGradleTests(String gradleVersion, boolean successExpected = true, boolean configurationCache = false) { diff --git a/dd-smoke-tests/src/main/groovy/datadog/smoketest/AbstractSmokeTest.groovy b/dd-smoke-tests/src/main/groovy/datadog/smoketest/AbstractSmokeTest.groovy index 8bc5e2f5a33..5f882fc359a 100644 --- a/dd-smoke-tests/src/main/groovy/datadog/smoketest/AbstractSmokeTest.groovy +++ b/dd-smoke-tests/src/main/groovy/datadog/smoketest/AbstractSmokeTest.groovy @@ -188,6 +188,12 @@ abstract class AbstractSmokeTest extends ProcessManager { def javaProperties() { def tmpDir = "/tmp" + // Trick to prevent jul preferences file lock issue on forked processes, in particular in CI which + // runs on Linux and have competing processes trying to write to it, including the Gradle daemon. + // + // Couldn't flush user prefs: java.util.prefs.BackingStoreException: Couldn't get file lock. + def prefsDir = "${tmpDir}/userPrefs/${this.getClass().simpleName}_${System.nanoTime()}" + def ret = [ "${getMaxMemoryArgumentForFork()}", "${getMinMemoryArgumentForFork()}", @@ -204,7 +210,8 @@ abstract class AbstractSmokeTest extends ProcessManager { "-Ddd.profiling.ddprof.alloc.enabled=${isDdprofSafe()}", "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=${logLevel()}", "-Dorg.slf4j.simpleLogger.defaultLogLevel=${logLevel()}", - "-Ddd.site=" + "-Ddd.site=", + "-Djava.util.prefs.userRoot=${prefsDir}" ] if (inferServiceName()) { ret += "-Ddd.service.name=${SERVICE_NAME}"