Skip to content

Commit 05ccd9a

Browse files
mtoffl01datadog-datadog-prod-us1[bot]mcculls
authored
Provide access to VM arguments via shared CLIHelper (#8538)
* Abstract away getVMArgumentsThroughReflection into a VMArgsCache class * Change underlying data structure of VMArgsCache to HashSet * Update internal-api/src/main/java/datadog/trace/bootstrap/config/provider/VMArgsCache.java Co-authored-by: datadog-datadog-prod-us1[bot] <88084959+datadog-datadog-prod-us1[bot]@users.noreply.github.com> * Update internal-api/src/main/java/datadog/trace/bootstrap/config/provider/VMArgsCache.java Co-authored-by: datadog-datadog-prod-us1[bot] <88084959+datadog-datadog-prod-us1[bot]@users.noreply.github.com> * Update internal-api/src/main/java/datadog/trace/bootstrap/config/provider/VMArgsCache.java Co-authored-by: datadog-datadog-prod-us1[bot] <88084959+datadog-datadog-prod-us1[bot]@users.noreply.github.com> * Clean up for initial review * move VMArgsCache to components/cli * fix datadog.cli import * nits: remove outdated comments * annotate initJvmArgs with @SuppressForbidden to allow Class.forName() usage * Fix package directory * Cleanup code: * store args in static field, make available via static getter * use list of strings as storage type to begin with * prefer NIO methods to read content (defaults to UTF8) * use try-with-resources to ensure reader is closed * check file exists as a quick short-circuit * Remove warning when reflection fails * Expose components:cli from internal-api --------- Co-authored-by: datadog-datadog-prod-us1[bot] <88084959+datadog-datadog-prod-us1[bot]@users.noreply.github.com> Co-authored-by: Stuart McCulloch <[email protected]>
1 parent fe8895f commit 05ccd9a

File tree

9 files changed

+105
-55
lines changed

9 files changed

+105
-55
lines changed

Diff for: components/cli/build.gradle.kts

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
plugins {
2+
id("me.champeau.jmh")
3+
}
4+
5+
apply(from = "$rootDir/gradle/java.gradle")
6+
7+
jmh {
8+
version = "1.28"
9+
}
+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package datadog.cli;
2+
3+
import de.thetaphi.forbiddenapis.SuppressForbidden;
4+
import java.io.BufferedReader;
5+
import java.lang.management.ManagementFactory;
6+
import java.lang.reflect.Field;
7+
import java.nio.file.Files;
8+
import java.nio.file.Path;
9+
import java.nio.file.Paths;
10+
import java.util.Arrays;
11+
import java.util.Collections;
12+
import java.util.List;
13+
import java.util.Locale;
14+
15+
public final class CLIHelper {
16+
private static final List<String> VM_ARGS = findVmArgs();
17+
18+
public static List<String> getVmArgs() {
19+
return VM_ARGS;
20+
}
21+
22+
@SuppressForbidden
23+
private static List<String> findVmArgs() {
24+
// Try ProcFS on Linux
25+
try {
26+
if (isLinux()) {
27+
Path cmdlinePath = Paths.get("/proc/self/cmdline");
28+
if (Files.exists(cmdlinePath)) {
29+
try (BufferedReader in = Files.newBufferedReader(cmdlinePath)) {
30+
return Arrays.asList(in.readLine().split("\0"));
31+
}
32+
}
33+
}
34+
} catch (Throwable ignored) {
35+
// Ignored exception
36+
}
37+
38+
// Try Oracle-based
39+
// IBM Semeru Runtime 1.8.0_345-b01 will throw UnsatisfiedLinkError here.
40+
try {
41+
final Class<?> managementFactoryHelperClass =
42+
Class.forName("sun.management.ManagementFactoryHelper");
43+
44+
final Class<?> vmManagementClass = Class.forName("sun.management.VMManagement");
45+
46+
Object vmManagement;
47+
48+
try {
49+
vmManagement =
50+
managementFactoryHelperClass.getDeclaredMethod("getVMManagement").invoke(null);
51+
} catch (final NoSuchMethodException e) {
52+
// Older vm before getVMManagement() existed
53+
final Field field = managementFactoryHelperClass.getDeclaredField("jvm");
54+
field.setAccessible(true);
55+
vmManagement = field.get(null);
56+
field.setAccessible(false);
57+
}
58+
59+
//noinspection unchecked
60+
return (List<String>) vmManagementClass.getMethod("getVmArguments").invoke(vmManagement);
61+
} catch (final ReflectiveOperationException | UnsatisfiedLinkError ignored) {
62+
// Ignored exception
63+
}
64+
65+
// Try IBM-based.
66+
try {
67+
final Class<?> VMClass = Class.forName("com.ibm.oti.vm.VM");
68+
final String[] argArray = (String[]) VMClass.getMethod("getVMArgs").invoke(null);
69+
return Arrays.asList(argArray);
70+
} catch (final ReflectiveOperationException ignored) {
71+
// Ignored exception
72+
}
73+
74+
// Fallback to default
75+
try {
76+
return ManagementFactory.getRuntimeMXBean().getInputArguments();
77+
} catch (final Throwable t) {
78+
// Throws InvocationTargetException on modularized applications
79+
// with non-opened java.management module
80+
System.err.println("WARNING: Unable to get VM args using managed beans");
81+
}
82+
return Collections.emptyList();
83+
}
84+
85+
private static boolean isLinux() {
86+
return System.getProperty("os.name").toLowerCase(Locale.ROOT).contains("linux");
87+
}
88+
}

Diff for: dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Constants.java

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public final class Constants {
1717
"datadog.slf4j",
1818
"datadog.json",
1919
"datadog.context",
20+
"datadog.cli",
2021
"datadog.appsec.api",
2122
"datadog.trace.api",
2223
"datadog.trace.bootstrap",

Diff for: dd-java-agent/build.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ tasks.withType(GenerateMavenPom).configureEach { task ->
203203

204204
dependencies {
205205
implementation project(path: ':components:json')
206+
implementation project(path: ':components:cli')
206207
modules {
207208
module("com.squareup.okio:okio") {
208209
replacedBy("com.datadoghq.okio:okio") // embed our patched fork

Diff for: dd-java-agent/src/main/java/datadog/trace/bootstrap/AgentBootstrap.java

+2-55
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,20 @@
22

33
import static java.nio.charset.StandardCharsets.UTF_8;
44

5+
import datadog.cli.CLIHelper;
56
import de.thetaphi.forbiddenapis.SuppressForbidden;
67
import java.io.BufferedReader;
78
import java.io.File;
89
import java.io.IOException;
910
import java.io.InputStreamReader;
1011
import java.io.PrintStream;
1112
import java.lang.instrument.Instrumentation;
12-
import java.lang.management.ManagementFactory;
13-
import java.lang.reflect.Field;
1413
import java.lang.reflect.Method;
1514
import java.net.URI;
1615
import java.net.URISyntaxException;
1716
import java.net.URL;
1817
import java.security.CodeSource;
1918
import java.util.ArrayList;
20-
import java.util.Arrays;
2119
import java.util.Collections;
2220
import java.util.IdentityHashMap;
2321
import java.util.List;
@@ -383,7 +381,7 @@ private static List<File> getAgentFilesFromVMArguments() {
383381
// - On IBM-based JDKs since at least 1.7
384382
// This prevents custom log managers from working correctly
385383
// Use reflection to bypass the loading of the class~
386-
for (final String argument : getVMArgumentsThroughReflection()) {
384+
for (final String argument : CLIHelper.getVmArgs()) {
387385
if (argument.startsWith(JAVA_AGENT_ARGUMENT)) {
388386
int index = argument.indexOf('=', JAVA_AGENT_ARGUMENT.length());
389387
String agentPathname =
@@ -424,57 +422,6 @@ private static File getAgentFileUsingClassLoaderLookup() throws URISyntaxExcepti
424422
return javaagentFile;
425423
}
426424

427-
@SuppressForbidden
428-
private static List<String> getVMArgumentsThroughReflection() {
429-
// Try Oracle-based
430-
// IBM Semeru Runtime 1.8.0_345-b01 will throw UnsatisfiedLinkError here.
431-
try {
432-
final Class<?> managementFactoryHelperClass =
433-
Class.forName("sun.management.ManagementFactoryHelper");
434-
435-
final Class<?> vmManagementClass = Class.forName("sun.management.VMManagement");
436-
437-
Object vmManagement;
438-
439-
try {
440-
vmManagement =
441-
managementFactoryHelperClass.getDeclaredMethod("getVMManagement").invoke(null);
442-
} catch (final NoSuchMethodException e) {
443-
// Older vm before getVMManagement() existed
444-
final Field field = managementFactoryHelperClass.getDeclaredField("jvm");
445-
field.setAccessible(true);
446-
vmManagement = field.get(null);
447-
field.setAccessible(false);
448-
}
449-
450-
//noinspection unchecked
451-
return (List<String>) vmManagementClass.getMethod("getVmArguments").invoke(vmManagement);
452-
} catch (final ReflectiveOperationException | UnsatisfiedLinkError ignored) {
453-
// Ignored exception
454-
}
455-
456-
// Try IBM-based.
457-
try {
458-
final Class<?> VMClass = Class.forName("com.ibm.oti.vm.VM");
459-
final String[] argArray = (String[]) VMClass.getMethod("getVMArgs").invoke(null);
460-
return Arrays.asList(argArray);
461-
} catch (final ReflectiveOperationException ignored) {
462-
// Ignored exception
463-
}
464-
465-
// Fallback to default
466-
try {
467-
System.err.println(
468-
"WARNING: Unable to get VM args through reflection. A custom java.util.logging.LogManager may not work correctly");
469-
return ManagementFactory.getRuntimeMXBean().getInputArguments();
470-
} catch (final Throwable t) {
471-
// Throws InvocationTargetException on modularized applications
472-
// with non-opened java.management module
473-
System.err.println("WARNING: Unable to get VM args using managed beans");
474-
}
475-
return Collections.emptyList();
476-
}
477-
478425
private static void checkJarManifestMainClassIsThis(final URL jarUrl) throws IOException {
479426
final URL manifestUrl = new URL("jar:" + jarUrl + "!/META-INF/MANIFEST.MF");
480427
final String mainClassLine = "Main-Class: " + thisClass.getCanonicalName();

Diff for: dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/SpockRunner.java

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public class SpockRunner extends JUnitPlatform {
4040
"datadog.slf4j",
4141
"datadog.json",
4242
"datadog.context",
43+
"datadog.cli",
4344
"datadog.appsec.api",
4445
"datadog.trace.api",
4546
"datadog.trace.bootstrap",

Diff for: gradle/dependencies.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ final class CachedData {
1818
exclude(project(':communication'))
1919
exclude(project(':components:context'))
2020
exclude(project(':components:json'))
21+
exclude(project(':components:cli'))
2122
exclude(project(':remote-config:remote-config-api'))
2223
exclude(project(':remote-config:remote-config-core'))
2324
exclude(project(':telemetry'))

Diff for: internal-api/build.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ dependencies {
234234
api project(':dd-trace-api')
235235
api libs.slf4j
236236
api project(':components:context')
237+
api project(':components:cli')
237238
api project(":utils:time-utils")
238239

239240
// has to be loaded by system classloader:

Diff for: settings.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ include ':dd-java-agent:agent-otel:otel-shim'
6666
include ':dd-java-agent:agent-otel:otel-tooling'
6767

6868
include ':communication'
69+
include ':components:cli'
6970
include ':components:context'
7071
include ':components:json'
7172
include ':telemetry'

0 commit comments

Comments
 (0)