Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import org.gradle.api.provider.Property
import org.gradle.api.tasks.Exec
import org.gradle.api.tasks.SourceSetContainer
import org.gradle.api.tasks.testing.Test
import java.io.File
import java.time.Duration
import javax.inject.Inject

Expand Down Expand Up @@ -79,60 +78,6 @@ import javax.inject.Inject
*/
class ProfilerTestPlugin : Plugin<Project> {

/**
* Major version of the *test* JVM, read from its `release` file (`JAVA_VERSION="..."`) rather
* than by executing the launcher.
*
* Executing `$JAVA_TEST_HOME/bin/java -version` (PlatformUtils.testJvmMajorVersion()) is
* unreliable here: in the musl split-JDK matrix it has been observed to report the build JDK
* (21) even when the test JVM is JDK 8, which put a JDK-21-only `--add-exports` onto a JDK-8
* launcher and aborted it. Reading the `release` file is a pure file read of the same
* JAVA_TEST_HOME the executable is resolved from — deterministic, no subprocess, no exec-format
* or PATH hazards. Returns 0 when it cannot be determined (missing/old `release`), so callers
* fail safe: they omit the flag, the profiler degrades to thread-scoped storage, and the
* carrier-scoping tests skip — never an abort.
*/
private fun testJvmMajorVersionFromRelease(): Int = try {
val release = File(PlatformUtils.testJavaHome(), "release")
val version = release.takeIf { it.isFile }
?.readLines()
?.firstOrNull { it.startsWith("JAVA_VERSION=") }
?.substringAfter('=')?.trim()?.trim('"')
// "1.8.0_452" -> 8 ; "21.0.5" -> 21
val parts = version?.split('.').orEmpty()
val majorToken = when {
parts.isEmpty() -> ""
parts[0] == "1" && parts.size > 1 -> parts[1]
else -> parts[0]
}
majorToken.takeWhile { it.isDigit() }.toIntOrNull() ?: 0
} catch (e: Exception) {
0
}

/**
* JVM args required to enable carrier-scoped OTEL context storage
* (`OtelContextStorage.Mode.CARRIER`), or an empty list when the test JVM does not support it.
*
* Carrier scoping resolves `jdk.internal.misc.CarrierThreadLocal`, which lives in a
* non-exported package, so it needs `--add-exports java.base/jdk.internal.misc=ALL-UNNAMED`.
* That type only exists on JDK 21+, and the flag *aborts* a Java 8 JVM ("Unrecognized option"),
* so it is gated on the version of the actual test JVM.
*
* MUST be evaluated at task execution time (inside doFirst), not configuration time: the test
* JVM is selected via JAVA_TEST_HOME, which the CI only makes resolvable at execution time (see
* the `executable` assignments below).
*/
private fun carrierExportJvmArgs(project: Project): List<String> {
val major = testJvmMajorVersionFromRelease()
val enabled = major >= 21
project.logger.info(
"ddprof: carrier --add-exports gate — testJavaHome={}, detected major={}, flag {}",
PlatformUtils.testJavaHome(), major, if (enabled) "ADDED" else "omitted"
)
return if (enabled) listOf("--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED") else emptyList()
}

override fun apply(project: Project) {
val extension = project.extensions.create(
"profilerTest",
Expand Down Expand Up @@ -311,8 +256,6 @@ class ProfilerTestPlugin : Plugin<Project> {
testTask.doFirst {
val allArgs = mutableListOf<String>()
allArgs.addAll(testConfig.standardJvmArgs)
// Version-gated at execution time, when the real test JVM is resolvable.
allArgs.addAll(carrierExportJvmArgs(project))

if (extension.nativeLibDir.isPresent) {
allArgs.add("-Djava.library.path=${extension.nativeLibDir.get().asFile.absolutePath}")
Expand Down Expand Up @@ -386,8 +329,6 @@ class ProfilerTestPlugin : Plugin<Project> {

// JVM args
allArgs.addAll(testConfig.standardJvmArgs)
// Version-gated at execution time, when the real test JVM (JAVA_TEST_HOME) is resolvable.
allArgs.addAll(carrierExportJvmArgs(project))
if (extension.nativeLibDir.isPresent) {
allArgs.add("-Djava.library.path=${extension.nativeLibDir.get().asFile.absolutePath}")
}
Expand Down Expand Up @@ -748,12 +689,12 @@ abstract class ProfilerTestExtension @Inject constructor(

init {
// Standard JVM arguments for profiler testing.
// NOTE: JDK-version-gated flags (e.g. the carrier-scoping --add-exports) must NOT be
// added here. This convention is computed at configuration time, where JAVA_TEST_HOME
// is not yet resolvable and PlatformUtils.testJavaHome() falls back to the *build* JDK
// (JAVA_HOME) — which misdetects in the musl split-JDK CI (build JDK 21, test JDK 8) and
// would emit a JDK-21 flag onto a JDK-8 test JVM. Version-gated flags are added at
// execution time in the task doFirst blocks instead (see ProfilerTestPlugin).
// NOTE: JDK-version-gated flags must NOT be added here. This convention is computed at
// configuration time, where JAVA_TEST_HOME is not yet resolvable and
// PlatformUtils.testJavaHome() falls back to the *build* JDK (JAVA_HOME) — which
// misdetects in the musl split-JDK CI (build JDK 21, test JDK 8) and would emit a
// JDK-21 flag onto a JDK-8 test JVM. Version-gated flags belong in the task doFirst
// blocks instead (see ProfilerTestPlugin), where the real test JVM is resolvable.
standardJvmArgs.convention(listOf(
"-Djdk.attach.allowAttachSelf", // Allow profiler to attach to self
"-Djol.tryWithSudo=true", // JOL memory layout analysis
Expand Down
25 changes: 0 additions & 25 deletions ddprof-lib/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ plugins {
id("com.datadoghq.native-build")
id("com.datadoghq.gtest")
id("com.datadoghq.scanbuild")
id("com.datadoghq.versioned-sources")
}

val libraryName = "ddprof"
Expand Down Expand Up @@ -54,23 +53,11 @@ gtest {
failFast.set(true)
}

// Java configuration - using sourceCompatibility (not --release 8)
// because BufferWriter8 needs access to internal sun.nio.ch package
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

// Configure versioned sources for runtime version-specific implementations
versionedSources {
versions {
register("java9") {
release.set(9)
minToolchainVersion.set(11) // Compile Java 9 code with JDK 11+
}
}
}

// Test configuration
tasks.test {
onlyIf {
Expand All @@ -89,14 +76,6 @@ val copyExternalLibs by tasks.registering(Copy::class) {
}
}

// Gradle 9 requires explicit dependency: compileJava9Java uses mainSourceSet.output
// which includes the copyExternalLibs destination directory
afterEvaluate {
tasks.named("compileJava9Java") {
dependsOn(copyExternalLibs)
}
}

// Create JAR tasks for each build configuration using nativeBuild extension utilities
// Uses afterEvaluate to discover configurations dynamically from NativeBuildExtension
afterEvaluate {
Expand Down Expand Up @@ -133,7 +112,6 @@ afterEvaluate {
}

from(sourceSets.main.get().output.classesDirs)
versionedSources.configureJar(this)
from(nativeBuild.libraryTargetBase(name)) {
include("**/*")
// Exclude debug symbols from production JAR
Expand Down Expand Up @@ -169,7 +147,6 @@ tasks.jar {
// Source JAR
val sourcesJar by tasks.registering(Jar::class) {
from(sourceSets.main.get().allJava)
versionedSources.configureSourceJar(this)
archiveBaseName.set(libraryName)
archiveClassifier.set("sources")
archiveVersion.set(componentVersion)
Expand All @@ -178,8 +155,6 @@ val sourcesJar by tasks.registering(Jar::class) {
// Javadoc configuration
tasks.withType<Javadoc>().configureEach {
dependsOn(copyExternalLibs)
// Allow javadoc to access internal sun.nio.ch package used by BufferWriter8
(options as StandardJavadocDocletOptions).addStringOption("-add-exports", "java.base/sun.nio.ch=ALL-UNNAMED")
}

// Javadoc JAR
Expand Down
Loading
Loading