From 2691513c6189d658765acec7a26ee711239527de Mon Sep 17 00:00:00 2001 From: Matt Date: Fri, 9 May 2025 09:48:06 -0700 Subject: [PATCH 01/10] Read hsperfdata for Java PIDs if jvmstat is unavailable --- .../java/datadog/trace/util/PidHelper.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/internal-api/src/main/java/datadog/trace/util/PidHelper.java b/internal-api/src/main/java/datadog/trace/util/PidHelper.java index dfa6276b879..4970da90bdf 100644 --- a/internal-api/src/main/java/datadog/trace/util/PidHelper.java +++ b/internal-api/src/main/java/datadog/trace/util/PidHelper.java @@ -8,11 +8,16 @@ import java.io.IOException; import java.io.InputStreamReader; import java.lang.management.ManagementFactory; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Collections; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -70,6 +75,24 @@ public static Set getJavaPids() { if (directlyObtainedPids != null) { return directlyObtainedPids; } + + // Some JDKs don't have jvmstat available as a module, attempt to read from the hsperfdata + // directory instead + try (Stream stream = + Files.list( + Paths.get( + System.getProperty("java.io.tmpdir") + + "hsperfdata_" + + System.getProperty("user.name")))) { + return stream + .filter(file -> !Files.isDirectory(file)) + .map(Path::getFileName) + .map(Path::toString) + .collect(Collectors.toSet()); + } catch (IOException e) { + log.debug("Unable to obtain Java PIDs via hsperfdata", e); + } + // there is no supported Java API to achieve this // one could use sun.jvmstat.monitor.MonitoredHost but it is an internal API and can go away at // any time - From 1dfd54f112007ac5fc4e73d6347500598ad6f7cc Mon Sep 17 00:00:00 2001 From: Matt Date: Fri, 9 May 2025 16:44:47 -0700 Subject: [PATCH 02/10] Add equivalent to JDK os::get_temp_directory() for clean-room jvmstat impl. --- .../java/datadog/trace/util/PidHelper.java | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/internal-api/src/main/java/datadog/trace/util/PidHelper.java b/internal-api/src/main/java/datadog/trace/util/PidHelper.java index 4970da90bdf..82fe8cf49c4 100644 --- a/internal-api/src/main/java/datadog/trace/util/PidHelper.java +++ b/internal-api/src/main/java/datadog/trace/util/PidHelper.java @@ -69,6 +69,26 @@ private static String findPid() { return pid; } + private static String getOSTempDir() { + // See + // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppatha#remarks + // and + // the JDK OS-specific implementations of os::get_temp_directory(), i.e. + // https://github.com/openjdk/jdk/blob/f50bd0d9ec65a6b9596805d0131aaefc1bb913f3/src/hotspot/os/bsd/os_bsd.cpp#L886-L904 + if (Platform.isLinux()) { + return "/tmp/"; + } else if (Platform.isWindows()) { + return Stream.of(System.getenv("TMP"), System.getenv("TEMP"), System.getenv("USERPROFILE")) + .filter(String::isEmpty) + .findFirst() + .orElse("C:\\Windows"); + } else if (Platform.isMac()) { + return System.getenv("TMPDIR"); + } else { + return System.getProperty("java.io.tmpdir"); + } + } + public static Set getJavaPids() { // Attempt to use jvmstat directly, fall through to jps process fork strategy Set directlyObtainedPids = JPSUtils.getVMPids(); @@ -79,11 +99,7 @@ public static Set getJavaPids() { // Some JDKs don't have jvmstat available as a module, attempt to read from the hsperfdata // directory instead try (Stream stream = - Files.list( - Paths.get( - System.getProperty("java.io.tmpdir") - + "hsperfdata_" - + System.getProperty("user.name")))) { + Files.list(Paths.get(getOSTempDir() + "/hsperfdata_" + System.getProperty("user.name")))) { return stream .filter(file -> !Files.isDirectory(file)) .map(Path::getFileName) From 44ea4e64c997ced60e75fa6db5bbb8b6936762dd Mon Sep 17 00:00:00 2001 From: Matt Date: Mon, 12 May 2025 09:06:21 -0400 Subject: [PATCH 03/10] Add a comment documenting how JVM processes are enumerated Co-authored-by: Jaroslav Bachorik --- internal-api/src/main/java/datadog/trace/util/PidHelper.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal-api/src/main/java/datadog/trace/util/PidHelper.java b/internal-api/src/main/java/datadog/trace/util/PidHelper.java index 82fe8cf49c4..371af32783e 100644 --- a/internal-api/src/main/java/datadog/trace/util/PidHelper.java +++ b/internal-api/src/main/java/datadog/trace/util/PidHelper.java @@ -99,6 +99,8 @@ public static Set getJavaPids() { // Some JDKs don't have jvmstat available as a module, attempt to read from the hsperfdata // directory instead try (Stream stream = + // Emulating the hotspot way to enumerate the JVM processes using the perfdata file + // https://github.com/openjdk/jdk/blob/d7cb933b89839b692f5562aeeb92076cd25a99f6/src/hotspot/share/runtime/perfMemory.cpp#L244 Files.list(Paths.get(getOSTempDir() + "/hsperfdata_" + System.getProperty("user.name")))) { return stream .filter(file -> !Files.isDirectory(file)) From 6e7e380ff5d14e5b17e8d908c3be66e972607fec Mon Sep 17 00:00:00 2001 From: Matt Date: Mon, 12 May 2025 09:09:14 -0400 Subject: [PATCH 04/10] Remove unnecessary defensive path separator Co-authored-by: Brice Dutheil --- internal-api/src/main/java/datadog/trace/util/PidHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal-api/src/main/java/datadog/trace/util/PidHelper.java b/internal-api/src/main/java/datadog/trace/util/PidHelper.java index 371af32783e..ff0b9b1589e 100644 --- a/internal-api/src/main/java/datadog/trace/util/PidHelper.java +++ b/internal-api/src/main/java/datadog/trace/util/PidHelper.java @@ -101,7 +101,7 @@ public static Set getJavaPids() { try (Stream stream = // Emulating the hotspot way to enumerate the JVM processes using the perfdata file // https://github.com/openjdk/jdk/blob/d7cb933b89839b692f5562aeeb92076cd25a99f6/src/hotspot/share/runtime/perfMemory.cpp#L244 - Files.list(Paths.get(getOSTempDir() + "/hsperfdata_" + System.getProperty("user.name")))) { + Files.list(Paths.get(getOSTempDir(), "hsperfdata_" + System.getProperty("user.name")))) { return stream .filter(file -> !Files.isDirectory(file)) .map(Path::getFileName) From d7886c60bb6d13571dfbeeb2942e42b493e6ad39 Mon Sep 17 00:00:00 2001 From: Matt Date: Mon, 12 May 2025 09:37:29 -0400 Subject: [PATCH 05/10] Spotless --- internal-api/src/main/java/datadog/trace/util/PidHelper.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal-api/src/main/java/datadog/trace/util/PidHelper.java b/internal-api/src/main/java/datadog/trace/util/PidHelper.java index ff0b9b1589e..eebf6fa7449 100644 --- a/internal-api/src/main/java/datadog/trace/util/PidHelper.java +++ b/internal-api/src/main/java/datadog/trace/util/PidHelper.java @@ -99,8 +99,8 @@ public static Set getJavaPids() { // Some JDKs don't have jvmstat available as a module, attempt to read from the hsperfdata // directory instead try (Stream stream = - // Emulating the hotspot way to enumerate the JVM processes using the perfdata file - // https://github.com/openjdk/jdk/blob/d7cb933b89839b692f5562aeeb92076cd25a99f6/src/hotspot/share/runtime/perfMemory.cpp#L244 + // Emulating the hotspot way to enumerate the JVM processes using the perfdata file + // https://github.com/openjdk/jdk/blob/d7cb933b89839b692f5562aeeb92076cd25a99f6/src/hotspot/share/runtime/perfMemory.cpp#L244 Files.list(Paths.get(getOSTempDir(), "hsperfdata_" + System.getProperty("user.name")))) { return stream .filter(file -> !Files.isDirectory(file)) From 2c8d7ca752b3ef3d55b2463976b9647f6074c5da Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 13 May 2025 09:41:55 -0400 Subject: [PATCH 06/10] Add support for J9 equivalent of JPS --- .../java/datadog/trace/util/PidHelper.java | 70 +++++++++++++------ 1 file changed, 50 insertions(+), 20 deletions(-) diff --git a/internal-api/src/main/java/datadog/trace/util/PidHelper.java b/internal-api/src/main/java/datadog/trace/util/PidHelper.java index eebf6fa7449..a2e55b4e670 100644 --- a/internal-api/src/main/java/datadog/trace/util/PidHelper.java +++ b/internal-api/src/main/java/datadog/trace/util/PidHelper.java @@ -69,23 +69,56 @@ private static String findPid() { return pid; } - private static String getOSTempDir() { - // See - // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppatha#remarks - // and - // the JDK OS-specific implementations of os::get_temp_directory(), i.e. - // https://github.com/openjdk/jdk/blob/f50bd0d9ec65a6b9596805d0131aaefc1bb913f3/src/hotspot/os/bsd/os_bsd.cpp#L886-L904 - if (Platform.isLinux()) { - return "/tmp/"; - } else if (Platform.isWindows()) { - return Stream.of(System.getenv("TMP"), System.getenv("TEMP"), System.getenv("USERPROFILE")) - .filter(String::isEmpty) - .findFirst() - .orElse("C:\\Windows"); - } else if (Platform.isMac()) { - return System.getenv("TMPDIR"); + private static String getTempDir() { + if (!Platform.isJ9()) { + // See + // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppatha#remarks + // and + // the JDK OS-specific implementations of os::get_temp_directory(), i.e. + // https://github.com/openjdk/jdk/blob/f50bd0d9ec65a6b9596805d0131aaefc1bb913f3/src/hotspot/os/bsd/os_bsd.cpp#L886-L904 + if (Platform.isLinux()) { + return "/tmp"; + } else if (Platform.isWindows()) { + return Stream.of(System.getenv("TMP"), System.getenv("TEMP"), System.getenv("USERPROFILE")) + .filter(String::isEmpty) + .findFirst() + .orElse("C:\\Windows"); + } else if (Platform.isMac()) { + return System.getenv("TMPDIR"); + } else { + return System.getProperty("java.io.tmpdir"); + } + } else { + try { + https: // github.com/eclipse-openj9/openj9/blob/196082df056a990756a5571bfac29585fbbfbb42/jcl/src/java.base/share/classes/openj9/internal/tools/attach/target/IPC.java#L351 + return (String) + Class.forName("openj9.internal.tools.attach.target.IPC") + .getDeclaredMethod("getTmpDir") + .invoke(null); + } catch (Exception e) { + // Fall back to constants based on J9 source code, may not have perfect coverage + String tmpDir = System.getProperty("java.io.tmpdir"); + if (tmpDir != null && !tmpDir.isEmpty()) { + return tmpDir; + } else if (Platform.isWindows()) { + return "C:\\Documents"; + } else { + return "/tmp"; + } + } + } + } + + private static Path getJavaProcessesDir() { + if (Platform.isJ9()) { + // J9 uses a different temporary directory AND subdirectory for storing jps / attach-related + // info + // https://github.com/eclipse-openj9/openj9/blob/196082df056a990756a5571bfac29585fbbfbb42/jcl/src/java.base/share/classes/openj9/internal/tools/attach/target/CommonDirectory.java#L94 + return Paths.get(getTempDir(), ".com_ibm_tools_attach"); } else { - return System.getProperty("java.io.tmpdir"); + // Emulating the hotspot way to enumerate the JVM processes using the perfdata file + // https://github.com/openjdk/jdk/blob/d7cb933b89839b692f5562aeeb92076cd25a99f6/src/hotspot/share/runtime/perfMemory.cpp#L244 + return Paths.get(getTempDir(), "hsperfdata_" + System.getProperty("user.name")); } } @@ -98,10 +131,7 @@ public static Set getJavaPids() { // Some JDKs don't have jvmstat available as a module, attempt to read from the hsperfdata // directory instead - try (Stream stream = - // Emulating the hotspot way to enumerate the JVM processes using the perfdata file - // https://github.com/openjdk/jdk/blob/d7cb933b89839b692f5562aeeb92076cd25a99f6/src/hotspot/share/runtime/perfMemory.cpp#L244 - Files.list(Paths.get(getOSTempDir(), "hsperfdata_" + System.getProperty("user.name")))) { + try (Stream stream = Files.list(getJavaProcessesDir())) { return stream .filter(file -> !Files.isDirectory(file)) .map(Path::getFileName) From 38f6879b5708c2fd4e57f7799b15d5b8e16c2a06 Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 13 May 2025 11:43:03 -0400 Subject: [PATCH 07/10] Filter for PID files when emulating jps + nit changes --- .../src/main/java/datadog/trace/util/PidHelper.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/internal-api/src/main/java/datadog/trace/util/PidHelper.java b/internal-api/src/main/java/datadog/trace/util/PidHelper.java index a2e55b4e670..a1428efda65 100644 --- a/internal-api/src/main/java/datadog/trace/util/PidHelper.java +++ b/internal-api/src/main/java/datadog/trace/util/PidHelper.java @@ -95,7 +95,7 @@ private static String getTempDir() { Class.forName("openj9.internal.tools.attach.target.IPC") .getDeclaredMethod("getTmpDir") .invoke(null); - } catch (Exception e) { + } catch (Throwable t) { // Fall back to constants based on J9 source code, may not have perfect coverage String tmpDir = System.getProperty("java.io.tmpdir"); if (tmpDir != null && !tmpDir.isEmpty()) { @@ -136,6 +136,17 @@ public static Set getJavaPids() { .filter(file -> !Files.isDirectory(file)) .map(Path::getFileName) .map(Path::toString) + .filter( + (name) -> { + // On J9, additional metadata files are present alongside files named $PID. + // This also makes sense to have as defensive programming. + try { + Integer.parseInt(name); + return true; + } catch (Exception e) { + return false; + } + }) .collect(Collectors.toSet()); } catch (IOException e) { log.debug("Unable to obtain Java PIDs via hsperfdata", e); From 440c8325a5c667dbb7697af7350ad0bb0f683918 Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 13 May 2025 11:52:55 -0400 Subject: [PATCH 08/10] Filter for PID files and directories, minor bugfix --- internal-api/src/main/java/datadog/trace/util/PidHelper.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/internal-api/src/main/java/datadog/trace/util/PidHelper.java b/internal-api/src/main/java/datadog/trace/util/PidHelper.java index a1428efda65..ac55acd193c 100644 --- a/internal-api/src/main/java/datadog/trace/util/PidHelper.java +++ b/internal-api/src/main/java/datadog/trace/util/PidHelper.java @@ -133,13 +133,14 @@ public static Set getJavaPids() { // directory instead try (Stream stream = Files.list(getJavaProcessesDir())) { return stream - .filter(file -> !Files.isDirectory(file)) .map(Path::getFileName) .map(Path::toString) .filter( (name) -> { // On J9, additional metadata files are present alongside files named $PID. - // This also makes sense to have as defensive programming. + // Additionally, the contents of the ps dir are files with process ID files for Hotspot, + // but they are directories for J9. + // This also makes sense as defensive programming. try { Integer.parseInt(name); return true; From 419bb4043eca26dce2448fc8e96799c904300b41 Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 13 May 2025 11:56:08 -0400 Subject: [PATCH 09/10] Filter for PID files and directories, minor bugfix --- internal-api/src/main/java/datadog/trace/util/PidHelper.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal-api/src/main/java/datadog/trace/util/PidHelper.java b/internal-api/src/main/java/datadog/trace/util/PidHelper.java index ac55acd193c..b82d52d7ca6 100644 --- a/internal-api/src/main/java/datadog/trace/util/PidHelper.java +++ b/internal-api/src/main/java/datadog/trace/util/PidHelper.java @@ -138,7 +138,8 @@ public static Set getJavaPids() { .filter( (name) -> { // On J9, additional metadata files are present alongside files named $PID. - // Additionally, the contents of the ps dir are files with process ID files for Hotspot, + // Additionally, the contents of the ps dir are files with process ID files for + // Hotspot, // but they are directories for J9. // This also makes sense as defensive programming. try { From b69c69d836a86851fa812c05c17fe6d940436a3b Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 13 May 2025 13:56:53 -0400 Subject: [PATCH 10/10] Replace Integer.parseInt with character-check scan --- .../src/main/java/datadog/trace/util/PidHelper.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/internal-api/src/main/java/datadog/trace/util/PidHelper.java b/internal-api/src/main/java/datadog/trace/util/PidHelper.java index b82d52d7ca6..94060a487a8 100644 --- a/internal-api/src/main/java/datadog/trace/util/PidHelper.java +++ b/internal-api/src/main/java/datadog/trace/util/PidHelper.java @@ -142,12 +142,15 @@ public static Set getJavaPids() { // Hotspot, // but they are directories for J9. // This also makes sense as defensive programming. - try { - Integer.parseInt(name); - return true; - } catch (Exception e) { + if (name.isEmpty()) { return false; } + for (int i = 0; i < name.length(); i++) { + if (!Character.isDigit(name.charAt(i))) { + return false; + } + } + return true; }) .collect(Collectors.toSet()); } catch (IOException e) {