diff --git a/components/cli/build.gradle.kts b/components/cli/build.gradle.kts new file mode 100644 index 00000000000..4dca7fc3036 --- /dev/null +++ b/components/cli/build.gradle.kts @@ -0,0 +1,9 @@ +plugins { + id("me.champeau.jmh") +} + +apply(from = "$rootDir/gradle/java.gradle") + +jmh { + version = "1.28" +} diff --git a/components/cli/src/main/java/datadog/cli/CLIHelper.java b/components/cli/src/main/java/datadog/cli/CLIHelper.java new file mode 100644 index 00000000000..0f37953a92f --- /dev/null +++ b/components/cli/src/main/java/datadog/cli/CLIHelper.java @@ -0,0 +1,88 @@ +package datadog.cli; + +import de.thetaphi.forbiddenapis.SuppressForbidden; +import java.io.BufferedReader; +import java.lang.management.ManagementFactory; +import java.lang.reflect.Field; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Locale; + +public final class CLIHelper { + private static final List VM_ARGS = findVmArgs(); + + public static List getVmArgs() { + return VM_ARGS; + } + + @SuppressForbidden + private static List findVmArgs() { + // Try ProcFS on Linux + try { + if (isLinux()) { + Path cmdlinePath = Paths.get("/proc/self/cmdline"); + if (Files.exists(cmdlinePath)) { + try (BufferedReader in = Files.newBufferedReader(cmdlinePath)) { + return Arrays.asList(in.readLine().split("\0")); + } + } + } + } catch (Throwable ignored) { + // Ignored exception + } + + // Try Oracle-based + // IBM Semeru Runtime 1.8.0_345-b01 will throw UnsatisfiedLinkError here. + try { + final Class managementFactoryHelperClass = + Class.forName("sun.management.ManagementFactoryHelper"); + + final Class vmManagementClass = Class.forName("sun.management.VMManagement"); + + Object vmManagement; + + try { + vmManagement = + managementFactoryHelperClass.getDeclaredMethod("getVMManagement").invoke(null); + } catch (final NoSuchMethodException e) { + // Older vm before getVMManagement() existed + final Field field = managementFactoryHelperClass.getDeclaredField("jvm"); + field.setAccessible(true); + vmManagement = field.get(null); + field.setAccessible(false); + } + + //noinspection unchecked + return (List) vmManagementClass.getMethod("getVmArguments").invoke(vmManagement); + } catch (final ReflectiveOperationException | UnsatisfiedLinkError ignored) { + // Ignored exception + } + + // Try IBM-based. + try { + final Class VMClass = Class.forName("com.ibm.oti.vm.VM"); + final String[] argArray = (String[]) VMClass.getMethod("getVMArgs").invoke(null); + return Arrays.asList(argArray); + } catch (final ReflectiveOperationException ignored) { + // Ignored exception + } + + // Fallback to default + try { + return ManagementFactory.getRuntimeMXBean().getInputArguments(); + } catch (final Throwable t) { + // Throws InvocationTargetException on modularized applications + // with non-opened java.management module + System.err.println("WARNING: Unable to get VM args using managed beans"); + } + return Collections.emptyList(); + } + + private static boolean isLinux() { + return System.getProperty("os.name").toLowerCase(Locale.ROOT).contains("linux"); + } +} diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Constants.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Constants.java index c017f331181..0ed02193072 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Constants.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Constants.java @@ -17,6 +17,7 @@ public final class Constants { "datadog.slf4j", "datadog.json", "datadog.context", + "datadog.cli", "datadog.appsec.api", "datadog.trace.api", "datadog.trace.bootstrap", diff --git a/dd-java-agent/build.gradle b/dd-java-agent/build.gradle index 6528a2e3c2e..95737b79eeb 100644 --- a/dd-java-agent/build.gradle +++ b/dd-java-agent/build.gradle @@ -203,6 +203,7 @@ tasks.withType(GenerateMavenPom).configureEach { task -> dependencies { implementation project(path: ':components:json') + implementation project(path: ':components:cli') modules { module("com.squareup.okio:okio") { replacedBy("com.datadoghq.okio:okio") // embed our patched fork diff --git a/dd-java-agent/src/main/java/datadog/trace/bootstrap/AgentBootstrap.java b/dd-java-agent/src/main/java/datadog/trace/bootstrap/AgentBootstrap.java index 095db7acc8c..f70038f26ba 100644 --- a/dd-java-agent/src/main/java/datadog/trace/bootstrap/AgentBootstrap.java +++ b/dd-java-agent/src/main/java/datadog/trace/bootstrap/AgentBootstrap.java @@ -2,6 +2,7 @@ import static java.nio.charset.StandardCharsets.UTF_8; +import datadog.cli.CLIHelper; import de.thetaphi.forbiddenapis.SuppressForbidden; import java.io.BufferedReader; import java.io.File; @@ -9,15 +10,12 @@ import java.io.InputStreamReader; import java.io.PrintStream; import java.lang.instrument.Instrumentation; -import java.lang.management.ManagementFactory; -import java.lang.reflect.Field; import java.lang.reflect.Method; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.security.CodeSource; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.IdentityHashMap; import java.util.List; @@ -383,7 +381,7 @@ private static List getAgentFilesFromVMArguments() { // - On IBM-based JDKs since at least 1.7 // This prevents custom log managers from working correctly // Use reflection to bypass the loading of the class~ - for (final String argument : getVMArgumentsThroughReflection()) { + for (final String argument : CLIHelper.getVmArgs()) { if (argument.startsWith(JAVA_AGENT_ARGUMENT)) { int index = argument.indexOf('=', JAVA_AGENT_ARGUMENT.length()); String agentPathname = @@ -424,57 +422,6 @@ private static File getAgentFileUsingClassLoaderLookup() throws URISyntaxExcepti return javaagentFile; } - @SuppressForbidden - private static List getVMArgumentsThroughReflection() { - // Try Oracle-based - // IBM Semeru Runtime 1.8.0_345-b01 will throw UnsatisfiedLinkError here. - try { - final Class managementFactoryHelperClass = - Class.forName("sun.management.ManagementFactoryHelper"); - - final Class vmManagementClass = Class.forName("sun.management.VMManagement"); - - Object vmManagement; - - try { - vmManagement = - managementFactoryHelperClass.getDeclaredMethod("getVMManagement").invoke(null); - } catch (final NoSuchMethodException e) { - // Older vm before getVMManagement() existed - final Field field = managementFactoryHelperClass.getDeclaredField("jvm"); - field.setAccessible(true); - vmManagement = field.get(null); - field.setAccessible(false); - } - - //noinspection unchecked - return (List) vmManagementClass.getMethod("getVmArguments").invoke(vmManagement); - } catch (final ReflectiveOperationException | UnsatisfiedLinkError ignored) { - // Ignored exception - } - - // Try IBM-based. - try { - final Class VMClass = Class.forName("com.ibm.oti.vm.VM"); - final String[] argArray = (String[]) VMClass.getMethod("getVMArgs").invoke(null); - return Arrays.asList(argArray); - } catch (final ReflectiveOperationException ignored) { - // Ignored exception - } - - // Fallback to default - try { - System.err.println( - "WARNING: Unable to get VM args through reflection. A custom java.util.logging.LogManager may not work correctly"); - return ManagementFactory.getRuntimeMXBean().getInputArguments(); - } catch (final Throwable t) { - // Throws InvocationTargetException on modularized applications - // with non-opened java.management module - System.err.println("WARNING: Unable to get VM args using managed beans"); - } - return Collections.emptyList(); - } - private static void checkJarManifestMainClassIsThis(final URL jarUrl) throws IOException { final URL manifestUrl = new URL("jar:" + jarUrl + "!/META-INF/MANIFEST.MF"); final String mainClassLine = "Main-Class: " + thisClass.getCanonicalName(); diff --git a/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/SpockRunner.java b/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/SpockRunner.java index 684b5c4b11a..eda9e4d86d4 100644 --- a/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/SpockRunner.java +++ b/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/SpockRunner.java @@ -40,6 +40,7 @@ public class SpockRunner extends JUnitPlatform { "datadog.slf4j", "datadog.json", "datadog.context", + "datadog.cli", "datadog.appsec.api", "datadog.trace.api", "datadog.trace.bootstrap", diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 2fd95f2c4af..76e8061f5d8 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -18,6 +18,7 @@ final class CachedData { exclude(project(':communication')) exclude(project(':components:context')) exclude(project(':components:json')) + exclude(project(':components:cli')) exclude(project(':remote-config:remote-config-api')) exclude(project(':remote-config:remote-config-core')) exclude(project(':telemetry')) diff --git a/internal-api/build.gradle b/internal-api/build.gradle index 947fe5d0ea8..9d0107ae991 100644 --- a/internal-api/build.gradle +++ b/internal-api/build.gradle @@ -234,6 +234,7 @@ dependencies { api project(':dd-trace-api') api libs.slf4j api project(':components:context') + api project(':components:cli') api project(":utils:time-utils") // has to be loaded by system classloader: diff --git a/settings.gradle b/settings.gradle index b78e2287dda..0b6043e9346 100644 --- a/settings.gradle +++ b/settings.gradle @@ -66,6 +66,7 @@ include ':dd-java-agent:agent-otel:otel-shim' include ':dd-java-agent:agent-otel:otel-tooling' include ':communication' +include ':components:cli' include ':components:context' include ':components:json' include ':telemetry'