986 lines
38 KiB
Diff
986 lines
38 KiB
Diff
Date: Wed, 31 May 2023 03:55:21 +0000
|
|
Subject: [PATCH 04/59] 8193710: jcmd -l and jps commands do not list Java processes running in Docker containers
|
|
|
|
Bug url: https://bugs.openjdk.org/browse/JDK-8193710
|
|
---
|
|
hotspot/src/os/linux/vm/perfMemory_linux.cpp | 102 ++++++--
|
|
.../classes/sun/jvmstat/PlatformSupport.java | 114 +++++++++
|
|
.../sun/jvmstat/PlatformSupportImpl.java | 227 ++++++++++++++++++
|
|
.../protocol/local/LocalVmManager.java | 93 +++----
|
|
.../monitor/protocol/local/PerfDataFile.java | 179 ++++++++------
|
|
5 files changed, 582 insertions(+), 133 deletions(-)
|
|
create mode 100644 jdk/src/share/classes/sun/jvmstat/PlatformSupport.java
|
|
create mode 100644 jdk/src/share/classes/sun/jvmstat/PlatformSupportImpl.java
|
|
|
|
diff --git a/hotspot/src/os/linux/vm/perfMemory_linux.cpp b/hotspot/src/os/linux/vm/perfMemory_linux.cpp
|
|
index 8293b7168..b45032edb 100644
|
|
--- a/hotspot/src/os/linux/vm/perfMemory_linux.cpp
|
|
+++ b/hotspot/src/os/linux/vm/perfMemory_linux.cpp
|
|
@@ -147,11 +147,23 @@ static void save_memory_to_file(char* addr, size_t size) {
|
|
|
|
// return the user specific temporary directory name.
|
|
//
|
|
+// If containerized process, get dirname of
|
|
+// /proc/{vmid}/root/tmp/{PERFDATA_NAME_user}
|
|
+// otherwise /tmp/{PERFDATA_NAME_user}
|
|
+//
|
|
// the caller is expected to free the allocated memory.
|
|
//
|
|
-static char* get_user_tmp_dir(const char* user) {
|
|
+#define TMP_BUFFER_LEN (4+22)
|
|
+static char* get_user_tmp_dir(const char* user, int vmid, int nspid) {
|
|
+ char buffer[TMP_BUFFER_LEN];
|
|
+ char* tmpdir = (char *)os::get_temp_directory();
|
|
+ assert(strlen(tmpdir) == 4, "No longer using /tmp - update buffer size");
|
|
+
|
|
+ if (nspid != -1) {
|
|
+ jio_snprintf(buffer, TMP_BUFFER_LEN, "/proc/%d/root%s", vmid, tmpdir);
|
|
+ tmpdir = buffer;
|
|
+ }
|
|
|
|
- const char* tmpdir = os::get_temp_directory();
|
|
const char* perfdir = PERFDATA_NAME;
|
|
size_t nbytes = strlen(tmpdir) + strlen(perfdir) + strlen(user) + 3;
|
|
char* dirname = NEW_C_HEAP_ARRAY(char, nbytes, mtInternal);
|
|
@@ -500,7 +512,10 @@ static char* get_user_name(uid_t uid) {
|
|
//
|
|
// the caller is expected to free the allocated memory.
|
|
//
|
|
-static char* get_user_name_slow(int vmid, TRAPS) {
|
|
+// If nspid != -1, look in /proc/{vmid}/root/tmp for directories
|
|
+// containing nspid, otherwise just look for vmid in /tmp
|
|
+//
|
|
+static char* get_user_name_slow(int vmid, int nspid, TRAPS) {
|
|
|
|
// short circuit the directory search if the process doesn't even exist.
|
|
if (kill(vmid, 0) == OS_ERR) {
|
|
@@ -516,8 +531,19 @@ static char* get_user_name_slow(int vmid, TRAPS) {
|
|
// directory search
|
|
char* oldest_user = NULL;
|
|
time_t oldest_ctime = 0;
|
|
+ char buffer[TMP_BUFFER_LEN];
|
|
+ int searchpid;
|
|
+ char* tmpdirname = (char *)os::get_temp_directory();
|
|
+ assert(strlen(tmpdirname) == 4, "No longer using /tmp - update buffer size");
|
|
|
|
- const char* tmpdirname = os::get_temp_directory();
|
|
+ if (nspid == -1) {
|
|
+ searchpid = vmid;
|
|
+ }
|
|
+ else {
|
|
+ jio_snprintf(buffer, MAXPATHLEN, "/proc/%d/root%s", vmid, tmpdirname);
|
|
+ tmpdirname = buffer;
|
|
+ searchpid = nspid;
|
|
+ }
|
|
|
|
// open the temp directory
|
|
DIR* tmpdirp = os::opendir(tmpdirname);
|
|
@@ -528,7 +554,7 @@ static char* get_user_name_slow(int vmid, TRAPS) {
|
|
}
|
|
|
|
// for each entry in the directory that matches the pattern hsperfdata_*,
|
|
- // open the directory and check if the file for the given vmid exists.
|
|
+ // open the directory and check if the file for the given vmid or nspid exists.
|
|
// The file with the expected name and the latest creation date is used
|
|
// to determine the user name for the process id.
|
|
//
|
|
@@ -571,7 +597,7 @@ static char* get_user_name_slow(int vmid, TRAPS) {
|
|
errno = 0;
|
|
while ((udentry = os::readdir(subdirp)) != NULL) {
|
|
|
|
- if (filename_to_pid(udentry->d_name) == vmid) {
|
|
+ if (filename_to_pid(udentry->d_name) == searchpid) {
|
|
struct stat statbuf;
|
|
int result;
|
|
|
|
@@ -620,10 +646,51 @@ static char* get_user_name_slow(int vmid, TRAPS) {
|
|
return(oldest_user);
|
|
}
|
|
|
|
+// Determine if the vmid is the parent pid
|
|
+// for a child in a PID namespace.
|
|
+// return the namespace pid if so, otherwise -1
|
|
+static int get_namespace_pid(int vmid) {
|
|
+ char fname[24];
|
|
+ int retpid = -1;
|
|
+
|
|
+ snprintf(fname, sizeof(fname), "/proc/%d/status", vmid);
|
|
+ FILE *fp = fopen(fname, "r");
|
|
+
|
|
+ if (fp) {
|
|
+ int pid, nspid;
|
|
+ int ret;
|
|
+ while (!feof(fp)) {
|
|
+ ret = fscanf(fp, "NSpid: %d %d", &pid, &nspid);
|
|
+ if (ret == 1) {
|
|
+ break;
|
|
+ }
|
|
+ if (ret == 2) {
|
|
+ retpid = nspid;
|
|
+ break;
|
|
+ }
|
|
+ for (;;) {
|
|
+ int ch = fgetc(fp);
|
|
+ if (ch == EOF || ch == (int)'\n') break;
|
|
+ }
|
|
+ }
|
|
+ fclose(fp);
|
|
+ }
|
|
+ return retpid;
|
|
+}
|
|
+
|
|
// return the name of the user that owns the JVM indicated by the given vmid.
|
|
//
|
|
-static char* get_user_name(int vmid, TRAPS) {
|
|
- return get_user_name_slow(vmid, THREAD);
|
|
+static char* get_user_name(int vmid, int *nspid, TRAPS) {
|
|
+ char *result = get_user_name_slow(vmid, *nspid, THREAD);
|
|
+
|
|
+ // If we are examining a container process without PID namespaces enabled
|
|
+ // we need to use /proc/{pid}/root/tmp to find hsperfdata files.
|
|
+ if (result == NULL) {
|
|
+ result = get_user_name_slow(vmid, vmid, THREAD);
|
|
+ // Enable nspid logic going forward
|
|
+ if (result != NULL) *nspid = vmid;
|
|
+ }
|
|
+ return result;
|
|
}
|
|
|
|
// return the file name of the backing store file for the named
|
|
@@ -631,13 +698,15 @@ static char* get_user_name(int vmid, TRAPS) {
|
|
//
|
|
// the caller is expected to free the allocated memory.
|
|
//
|
|
-static char* get_sharedmem_filename(const char* dirname, int vmid) {
|
|
+static char* get_sharedmem_filename(const char* dirname, int vmid, int nspid) {
|
|
+
|
|
+ int pid = (nspid == -1) ? vmid : nspid;
|
|
|
|
// add 2 for the file separator and a null terminator.
|
|
size_t nbytes = strlen(dirname) + UINT_CHARS + 2;
|
|
|
|
char* name = NEW_C_HEAP_ARRAY(char, nbytes, mtInternal);
|
|
- snprintf(name, nbytes, "%s/%d", dirname, vmid);
|
|
+ snprintf(name, nbytes, "%s/%d", dirname, pid);
|
|
|
|
return name;
|
|
}
|
|
@@ -929,8 +998,8 @@ static char* mmap_create_shared(size_t size) {
|
|
if (user_name == NULL)
|
|
return NULL;
|
|
|
|
- char* dirname = get_user_tmp_dir(user_name);
|
|
- char* filename = get_sharedmem_filename(dirname, vmid);
|
|
+ char* dirname = get_user_tmp_dir(user_name, vmid, -1);
|
|
+ char* filename = get_sharedmem_filename(dirname, vmid, -1);
|
|
// get the short filename
|
|
char* short_filename = strrchr(filename, '/');
|
|
if (short_filename == NULL) {
|
|
@@ -1076,8 +1145,11 @@ static void mmap_attach_shared(const char* user, int vmid, PerfMemory::PerfMemor
|
|
"Illegal access mode");
|
|
}
|
|
|
|
+ // determine if vmid is for a containerized process
|
|
+ int nspid = get_namespace_pid(vmid);
|
|
+
|
|
if (user == NULL || strlen(user) == 0) {
|
|
- luser = get_user_name(vmid, CHECK);
|
|
+ luser = get_user_name(vmid, &nspid, CHECK);
|
|
}
|
|
else {
|
|
luser = user;
|
|
@@ -1088,7 +1160,7 @@ static void mmap_attach_shared(const char* user, int vmid, PerfMemory::PerfMemor
|
|
"Could not map vmid to user Name");
|
|
}
|
|
|
|
- char* dirname = get_user_tmp_dir(luser);
|
|
+ char* dirname = get_user_tmp_dir(luser, vmid, nspid);
|
|
|
|
// since we don't follow symbolic links when creating the backing
|
|
// store file, we don't follow them when attaching either.
|
|
@@ -1099,7 +1171,7 @@ static void mmap_attach_shared(const char* user, int vmid, PerfMemory::PerfMemor
|
|
"Process not found");
|
|
}
|
|
|
|
- char* filename = get_sharedmem_filename(dirname, vmid);
|
|
+ char* filename = get_sharedmem_filename(dirname, vmid, nspid);
|
|
|
|
// copy heap memory to resource memory. the open_sharedmem_file
|
|
// method below need to use the filename, but could throw an
|
|
diff --git a/jdk/src/share/classes/sun/jvmstat/PlatformSupport.java b/jdk/src/share/classes/sun/jvmstat/PlatformSupport.java
|
|
new file mode 100644
|
|
index 000000000..84f3b2274
|
|
--- /dev/null
|
|
+++ b/jdk/src/share/classes/sun/jvmstat/PlatformSupport.java
|
|
@@ -0,0 +1,114 @@
|
|
+/*
|
|
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
|
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
+ *
|
|
+ * This code is free software; you can redistribute it and/or modify it
|
|
+ * under the terms of the GNU General Public License version 2 only, as
|
|
+ * published by the Free Software Foundation. Oracle designates this
|
|
+ * particular file as subject to the "Classpath" exception as provided
|
|
+ * by Oracle in the LICENSE file that accompanied this code.
|
|
+ *
|
|
+ * This code is distributed in the hope that it will be useful, but WITHOUT
|
|
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
+ * version 2 for more details (a copy is included in the LICENSE file that
|
|
+ * accompanied this code).
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License version
|
|
+ * 2 along with this work; if not, write to the Free Software Foundation,
|
|
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
+ *
|
|
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
+ * or visit www.oracle.com if you need additional information or have any
|
|
+ * questions.
|
|
+ */
|
|
+package sun.jvmstat;
|
|
+
|
|
+import java.io.File;
|
|
+import java.lang.reflect.Constructor;
|
|
+import java.util.Collections;
|
|
+import java.util.List;
|
|
+import sun.misc.VMSupport;
|
|
+
|
|
+/*
|
|
+ * Support routines handling temp directory locating
|
|
+ * and process ID extraction.
|
|
+ */
|
|
+public class PlatformSupport {
|
|
+ private static final String tmpDirName;
|
|
+ static {
|
|
+ /*
|
|
+ * For this to work, the target VM and this code need to use
|
|
+ * the same directory. Instead of guessing which directory the
|
|
+ * VM is using, we will ask.
|
|
+ */
|
|
+ String tmpdir = VMSupport.getVMTemporaryDirectory();
|
|
+
|
|
+ /*
|
|
+ * Assure that the string returned has a trailing File.separator
|
|
+ * character. This check was added because the Linux implementation
|
|
+ * changed such that the java.io.tmpdir string no longer terminates
|
|
+ * with a File.separator character.
|
|
+ */
|
|
+ if (tmpdir.lastIndexOf(File.separator) != (tmpdir.length()-1)) {
|
|
+ tmpdir = tmpdir + File.separator;
|
|
+ }
|
|
+ tmpDirName = tmpdir;
|
|
+ }
|
|
+
|
|
+ public static PlatformSupport getInstance() {
|
|
+ try {
|
|
+ Class<?> c = Class.forName("sun.jvmstat.PlatformSupportImpl");
|
|
+ @SuppressWarnings("unchecked")
|
|
+ Constructor<PlatformSupport> cntr = (Constructor<PlatformSupport>) c.getConstructor();
|
|
+ return cntr.newInstance();
|
|
+ } catch (ClassNotFoundException e) {
|
|
+ return new PlatformSupport();
|
|
+ } catch (ReflectiveOperationException e) {
|
|
+ throw new InternalError(e);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // package-private
|
|
+ PlatformSupport() {}
|
|
+
|
|
+ /*
|
|
+ * Return the OS specific temporary directory
|
|
+ */
|
|
+ public static String getTemporaryDirectory() {
|
|
+ return tmpDirName;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Return a list of the temporary directories that the VM uses
|
|
+ * for the attach and perf data files. This function returns
|
|
+ * the traditional temp directory in addition to any paths
|
|
+ * accessible by the host which map to temp directories used
|
|
+ * by containers. The container functionality is only currently
|
|
+ * supported on Linux platforms.
|
|
+ *
|
|
+ * It is important that this directory is well-known and the
|
|
+ * same for all VM instances. It cannot be affected by configuration
|
|
+ * variables such as java.io.tmpdir.
|
|
+ */
|
|
+ public List<String> getTemporaryDirectories(int vmid) {
|
|
+ // Add the default temporary directory only
|
|
+ return Collections.singletonList(tmpDirName);
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Extract the host PID from a file path.
|
|
+ */
|
|
+ public int getLocalVmId(File file) throws NumberFormatException {
|
|
+ return Integer.parseInt(file.getName());
|
|
+ }
|
|
+
|
|
+
|
|
+ /*
|
|
+ * Return the inner most namespaced PID if there is one,
|
|
+ * otherwise return the original PID.
|
|
+ */
|
|
+ public int getNamespaceVmId(int pid) {
|
|
+ return pid;
|
|
+ }
|
|
+}
|
|
diff --git a/jdk/src/share/classes/sun/jvmstat/PlatformSupportImpl.java b/jdk/src/share/classes/sun/jvmstat/PlatformSupportImpl.java
|
|
new file mode 100644
|
|
index 000000000..4d1d718ab
|
|
--- /dev/null
|
|
+++ b/jdk/src/share/classes/sun/jvmstat/PlatformSupportImpl.java
|
|
@@ -0,0 +1,227 @@
|
|
+/*
|
|
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
|
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
+ *
|
|
+ * This code is free software; you can redistribute it and/or modify it
|
|
+ * under the terms of the GNU General Public License version 2 only, as
|
|
+ * published by the Free Software Foundation. Oracle designates this
|
|
+ * particular file as subject to the "Classpath" exception as provided
|
|
+ * by Oracle in the LICENSE file that accompanied this code.
|
|
+ *
|
|
+ * This code is distributed in the hope that it will be useful, but WITHOUT
|
|
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
+ * version 2 for more details (a copy is included in the LICENSE file that
|
|
+ * accompanied this code).
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License version
|
|
+ * 2 along with this work; if not, write to the Free Software Foundation,
|
|
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
+ *
|
|
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
+ * or visit www.oracle.com if you need additional information or have any
|
|
+ * questions.
|
|
+ */
|
|
+
|
|
+package sun.jvmstat;
|
|
+
|
|
+import java.io.*;
|
|
+import java.util.*;
|
|
+import java.util.regex.*;
|
|
+import java.nio.file.Path;
|
|
+import java.nio.file.Paths;
|
|
+import java.nio.file.Files;
|
|
+import java.nio.charset.*;
|
|
+
|
|
+/*
|
|
+ * Linux specific implementation of the PlatformSupport routines
|
|
+ * providing process ID and temp directory support for host and
|
|
+ * cgroup container processes.
|
|
+ */
|
|
+public class PlatformSupportImpl extends PlatformSupport {
|
|
+ private static final String containerTmpPath = "/root" + getTemporaryDirectory();
|
|
+ private static final String pidPatternStr = "^[0-9]+$";
|
|
+
|
|
+ /*
|
|
+ * Return the temporary directories that the VM uses for the attach
|
|
+ * and perf data files. This function returns the traditional
|
|
+ * /tmp directory in addition to paths within the /proc file system
|
|
+ * allowing access to container tmp directories such as /proc/{pid}/root/tmp.
|
|
+ *
|
|
+ * It is important that this directory is well-known and the
|
|
+ * same for all VM instances. It cannot be affected by configuration
|
|
+ * variables such as java.io.tmpdir.
|
|
+ *
|
|
+ * Implementation Details:
|
|
+ *
|
|
+ * Java processes that run in docker containers are typically running
|
|
+ * under cgroups with separate pid namespaces which means that pids
|
|
+ * within the container are different that the pid which is visible
|
|
+ * from the host. The container pids typically start with 1 and
|
|
+ * increase. The java process running in the container will use these
|
|
+ * pids when creating the hsperfdata files. In order to locate java
|
|
+ * processes that are running in containers, we take advantage of
|
|
+ * the Linux proc file system which maps the containers tmp directory
|
|
+ * to the hosts under /proc/{hostpid}/root/tmp. We use the /proc status
|
|
+ * file /proc/{hostpid}/status to determine the containers pid and
|
|
+ * then access the hsperfdata file. The status file contains an
|
|
+ * entry "NSPid:" which shows the mapping from the hostpid to the
|
|
+ * containers pid.
|
|
+ *
|
|
+ * Example:
|
|
+ *
|
|
+ * NSPid: 24345 11
|
|
+ *
|
|
+ * In this example process 24345 is visible from the host,
|
|
+ * is running under the PID namespace and has a container specific
|
|
+ * pid of 11.
|
|
+ *
|
|
+ * The search for Java processes is done by first looking in the
|
|
+ * traditional /tmp for host process hsperfdata files and then
|
|
+ * the search will container in every /proc/{pid}/root/tmp directory.
|
|
+ * There are of course added complications to this search that
|
|
+ * need to be taken into account.
|
|
+ *
|
|
+ * 1. duplication of tmp directories
|
|
+ *
|
|
+ * /proc/{hostpid}/root/tmp directories exist for many processes
|
|
+ * that are running on a Linux kernel that has cgroups enabled even
|
|
+ * if they are not running in a container. To avoid this duplication,
|
|
+ * we compare the inode of the /proc tmp directories to /tmp and
|
|
+ * skip these duplicated directories.
|
|
+ *
|
|
+ * 2. Containerized processes without PID namespaces being enabled.
|
|
+ *
|
|
+ * If a container is running a Java process without namespaces being
|
|
+ * enabled, an hsperfdata file will only be located at
|
|
+ * /proc/{hostpid}/root/tmp/{hostpid}. This is handled by
|
|
+ * checking the last component in the path for both the hostpid
|
|
+ * and potential namespacepids (if one exists).
|
|
+ */
|
|
+ public List<String> getTemporaryDirectories(int pid) {
|
|
+ FilenameFilter pidFilter;
|
|
+ Matcher pidMatcher;
|
|
+ Pattern pidPattern = Pattern.compile(pidPatternStr);
|
|
+ long tmpInode = 0;
|
|
+
|
|
+ File procdir = new File("/proc");
|
|
+
|
|
+ if (pid != 0) {
|
|
+ pidPattern = Pattern.compile(Integer.toString(pid));
|
|
+ }
|
|
+ else {
|
|
+ pidPattern = Pattern.compile(pidPatternStr);
|
|
+ }
|
|
+ pidMatcher = pidPattern.matcher("");
|
|
+
|
|
+ // Add the default temporary directory first
|
|
+ List<String> v = new ArrayList<>();
|
|
+ v.add(getTemporaryDirectory());
|
|
+
|
|
+ try {
|
|
+ File f = new File(getTemporaryDirectory());
|
|
+ tmpInode = (Long)Files.getAttribute(f.toPath(), "unix:ino");
|
|
+ }
|
|
+ catch (IOException e) {}
|
|
+
|
|
+ pidFilter = new FilenameFilter() {
|
|
+ public boolean accept(File dir, String name) {
|
|
+ if (!dir.isDirectory())
|
|
+ return false;
|
|
+ pidMatcher.reset(name);
|
|
+ return pidMatcher.matches();
|
|
+ }
|
|
+ };
|
|
+
|
|
+ File[] dirs = procdir.listFiles(pidFilter);
|
|
+
|
|
+ // Add all unique /proc/{pid}/root/tmp dirs that are not mapped to /tmp
|
|
+ for (File dir : dirs) {
|
|
+ String containerTmpDir = dir.getAbsolutePath() + containerTmpPath;
|
|
+ File containerFile = new File(containerTmpDir);
|
|
+
|
|
+ try {
|
|
+ long procInode = (Long)Files.getAttribute(containerFile.toPath(), "unix:ino");
|
|
+ if (containerFile.exists() && containerFile.isDirectory() &&
|
|
+ containerFile.canRead() && procInode != tmpInode) {
|
|
+ v.add(containerTmpDir);
|
|
+ }
|
|
+ }
|
|
+ catch (IOException e) {}
|
|
+ }
|
|
+
|
|
+ return v;
|
|
+ }
|
|
+
|
|
+
|
|
+ /*
|
|
+ * Extract either the host PID or the NameSpace PID
|
|
+ * from a file path.
|
|
+ *
|
|
+ * File path should be in 1 of these 2 forms:
|
|
+ *
|
|
+ * /proc/{pid}/root/tmp/hsperfdata_{user}/{nspid}
|
|
+ * or
|
|
+ * /tmp/hsperfdata_{user}/{pid}
|
|
+ *
|
|
+ * In either case we want to return {pid} and NOT {nspid}
|
|
+ *
|
|
+ * This function filters out host pids which do not have
|
|
+ * associated hsperfdata files. This is due to the fact that
|
|
+ * getTemporaryDirectories will return /proc/{pid}/root/tmp
|
|
+ * paths for all container processes whether they are java
|
|
+ * processes or not causing duplicate matches.
|
|
+ */
|
|
+ public int getLocalVmId(File file) throws NumberFormatException {
|
|
+ String p = file.getAbsolutePath();
|
|
+ String s[] = p.split("\\/");
|
|
+
|
|
+ // Determine if this file is from a container
|
|
+ if (s.length == 7 && s[1].equals("proc")) {
|
|
+ int hostpid = Integer.parseInt(s[2]);
|
|
+ int nspid = Integer.parseInt(s[6]);
|
|
+ if (nspid == hostpid || nspid == getNamespaceVmId(hostpid)) {
|
|
+ return hostpid;
|
|
+ }
|
|
+ else {
|
|
+ return -1;
|
|
+ }
|
|
+ }
|
|
+ else {
|
|
+ return Integer.parseInt(file.getName());
|
|
+ }
|
|
+ }
|
|
+
|
|
+
|
|
+ /*
|
|
+ * Return the inner most namespaced PID if there is one,
|
|
+ * otherwise return the original PID.
|
|
+ */
|
|
+ public int getNamespaceVmId(int pid) {
|
|
+ // Assuming a real procfs sits beneath, reading this doesn't block
|
|
+ // nor will it consume a lot of memory.
|
|
+ Path statusPath = Paths.get("/proc", Integer.toString(pid), "status");
|
|
+ if (Files.notExists(statusPath)) {
|
|
+ return pid; // Likely a bad pid, but this is properly handled later.
|
|
+ }
|
|
+
|
|
+ try {
|
|
+ for (String line : Files.readAllLines(statusPath, StandardCharsets.UTF_8)) {
|
|
+ String[] parts = line.split(":");
|
|
+ if (parts.length == 2 && parts[0].trim().equals("NSpid")) {
|
|
+ parts = parts[1].trim().split("\\s+");
|
|
+ // The last entry represents the PID the JVM "thinks" it is.
|
|
+ // Even in non-namespaced pids these entries should be
|
|
+ // valid. You could refer to it as the inner most pid.
|
|
+ int ns_pid = Integer.parseInt(parts[parts.length - 1]);
|
|
+ return ns_pid;
|
|
+ }
|
|
+ }
|
|
+ // Old kernels may not have NSpid field (i.e. 3.10).
|
|
+ // Fallback to original pid in the event we cannot deduce.
|
|
+ return pid;
|
|
+ } catch (NumberFormatException | IOException x) {
|
|
+ return pid;
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/jdk/src/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/LocalVmManager.java b/jdk/src/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/LocalVmManager.java
|
|
index fde75fb2d..35d25700d 100644
|
|
--- a/jdk/src/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/LocalVmManager.java
|
|
+++ b/jdk/src/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/LocalVmManager.java
|
|
@@ -1,5 +1,5 @@
|
|
/*
|
|
- * Copyright (c) 2004, 2007, Oracle and/or its affiliates. All rights reserved.
|
|
+ * Copyright (c) 2004, 2018, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
@@ -45,7 +45,7 @@ import java.io.*;
|
|
*/
|
|
public class LocalVmManager {
|
|
private String userName; // user name for monitored jvm
|
|
- private File tmpdir;
|
|
+ private List<String> tmpdirs;
|
|
private Pattern userPattern;
|
|
private Matcher userMatcher;
|
|
private FilenameFilter userFilter;
|
|
@@ -77,8 +77,9 @@ public class LocalVmManager {
|
|
public LocalVmManager(String user) {
|
|
this.userName = user;
|
|
|
|
+
|
|
if (userName == null) {
|
|
- tmpdir = new File(PerfDataFile.getTempDirectory());
|
|
+ tmpdirs = PerfDataFile.getTempDirectories(null, 0);
|
|
userPattern = Pattern.compile(PerfDataFile.userDirNamePattern);
|
|
userMatcher = userPattern.matcher("");
|
|
|
|
@@ -89,7 +90,7 @@ public class LocalVmManager {
|
|
}
|
|
};
|
|
} else {
|
|
- tmpdir = new File(PerfDataFile.getTempDirectory(userName));
|
|
+ tmpdirs = PerfDataFile.getTempDirectories(userName, 0);
|
|
}
|
|
|
|
filePattern = Pattern.compile(PerfDataFile.fileNamePattern);
|
|
@@ -134,65 +135,73 @@ public class LocalVmManager {
|
|
*/
|
|
Set<Integer> jvmSet = new HashSet<Integer>();
|
|
|
|
- if (! tmpdir.isDirectory()) {
|
|
- return jvmSet;
|
|
- }
|
|
+ for (String dir : tmpdirs) {
|
|
+ File tmpdir = new File(dir);
|
|
+ if (! tmpdir.isDirectory()) {
|
|
+ continue;
|
|
+ }
|
|
|
|
- if (userName == null) {
|
|
- /*
|
|
- * get a list of all of the user temporary directories and
|
|
- * iterate over the list to find any files within those directories.
|
|
- */
|
|
- File[] dirs = tmpdir.listFiles(userFilter);
|
|
-
|
|
- for (int i = 0 ; i < dirs.length; i ++) {
|
|
- if (!dirs[i].isDirectory()) {
|
|
- continue;
|
|
+ if (userName == null) {
|
|
+ /*
|
|
+ * get a list of all of the user temporary directories and
|
|
+ * iterate over the list to find any files within those directories.
|
|
+ */
|
|
+ File[] dirs = tmpdir.listFiles(userFilter);
|
|
+ for (int i = 0 ; i < dirs.length; i ++) {
|
|
+ if (!dirs[i].isDirectory()) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ // get a list of files from the directory
|
|
+ File[] files = dirs[i].listFiles(fileFilter);
|
|
+ if (files != null) {
|
|
+ for (int j = 0; j < files.length; j++) {
|
|
+ if (files[j].isFile() && files[j].canRead()) {
|
|
+ int vmid = PerfDataFile.getLocalVmId(files[j]);
|
|
+ if (vmid != -1) {
|
|
+ jvmSet.add(vmid);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
}
|
|
+ } else {
|
|
+ /*
|
|
+ * Check if the user directory can be accessed. Any of these
|
|
+ * conditions may have asynchronously changed between subsequent
|
|
+ * calls to this method.
|
|
+ */
|
|
|
|
- // get a list of files from the directory
|
|
- File[] files = dirs[i].listFiles(fileFilter);
|
|
+ // get the list of files from the specified user directory
|
|
+ File[] files = tmpdir.listFiles(fileFilter);
|
|
|
|
if (files != null) {
|
|
for (int j = 0; j < files.length; j++) {
|
|
if (files[j].isFile() && files[j].canRead()) {
|
|
- jvmSet.add(new Integer(
|
|
- PerfDataFile.getLocalVmId(files[j])));
|
|
+ int vmid = PerfDataFile.getLocalVmId(files[j]);
|
|
+ if (vmid != -1) {
|
|
+ jvmSet.add(vmid);
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- } else {
|
|
- /*
|
|
- * Check if the user directory can be accessed. Any of these
|
|
- * conditions may have asynchronously changed between subsequent
|
|
- * calls to this method.
|
|
- */
|
|
|
|
- // get the list of files from the specified user directory
|
|
- File[] files = tmpdir.listFiles(fileFilter);
|
|
+ // look for any 1.4.1 files
|
|
+ File[] files = tmpdir.listFiles(tmpFileFilter);
|
|
|
|
if (files != null) {
|
|
for (int j = 0; j < files.length; j++) {
|
|
if (files[j].isFile() && files[j].canRead()) {
|
|
- jvmSet.add(new Integer(
|
|
- PerfDataFile.getLocalVmId(files[j])));
|
|
+ int vmid = PerfDataFile.getLocalVmId(files[j]);
|
|
+ if (vmid != -1) {
|
|
+ jvmSet.add(vmid);
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
- // look for any 1.4.1 files
|
|
- File[] files = tmpdir.listFiles(tmpFileFilter);
|
|
- if (files != null) {
|
|
- for (int j = 0; j < files.length; j++) {
|
|
- if (files[j].isFile() && files[j].canRead()) {
|
|
- jvmSet.add(new Integer(
|
|
- PerfDataFile.getLocalVmId(files[j])));
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
return jvmSet;
|
|
}
|
|
}
|
|
diff --git a/jdk/src/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/PerfDataFile.java b/jdk/src/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/PerfDataFile.java
|
|
index 62c64795b..26045ac5e 100644
|
|
--- a/jdk/src/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/PerfDataFile.java
|
|
+++ b/jdk/src/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/PerfDataFile.java
|
|
@@ -1,5 +1,5 @@
|
|
/*
|
|
- * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved.
|
|
+ * Copyright (c) 2004, 2018 Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
@@ -26,7 +26,11 @@
|
|
package sun.jvmstat.perfdata.monitor.protocol.local;
|
|
|
|
import java.io.File;
|
|
+import java.util.ArrayList;
|
|
+import java.util.List;
|
|
+import java.util.stream.Collectors;
|
|
import java.io.FilenameFilter;
|
|
+import sun.jvmstat.PlatformSupport;
|
|
|
|
/**
|
|
* Class to provide translations from the local Vm Identifier
|
|
@@ -44,11 +48,6 @@ import java.io.FilenameFilter;
|
|
public class PerfDataFile {
|
|
private PerfDataFile() { };
|
|
|
|
- /**
|
|
- * The name of the of the system dependent temporary directory
|
|
- */
|
|
- public static final String tmpDirName;
|
|
-
|
|
/**
|
|
* The file name prefix for PerfData shared memory files.
|
|
* <p>
|
|
@@ -79,6 +78,12 @@ public class PerfDataFile {
|
|
"^hsperfdata_[0-9]+(_[1-2]+)?$";
|
|
|
|
|
|
+ /**
|
|
+ * Platform Specific methods for looking up temporary directories
|
|
+ * and process IDs.
|
|
+ */
|
|
+ private static final PlatformSupport platSupport = PlatformSupport.getInstance();
|
|
+
|
|
/**
|
|
* Get a File object for the instrumentation backing store file
|
|
* for the JVM identified by the given local Vm Identifier.
|
|
@@ -93,7 +98,7 @@ public class PerfDataFile {
|
|
* @return File - a File object to the backing store file for the named
|
|
* shared memory region of the target JVM.
|
|
* @see java.io.File
|
|
- * @see #getTempDirectory()
|
|
+ * @see #getTempDirectories()
|
|
*/
|
|
public static File getFile(int lvmid) {
|
|
if (lvmid == 0) {
|
|
@@ -106,56 +111,65 @@ public class PerfDataFile {
|
|
return null;
|
|
}
|
|
|
|
- /*
|
|
- * iterate over all files in all directories in tmpDirName that
|
|
- * match the file name patterns.
|
|
- */
|
|
- File tmpDir = new File(tmpDirName);
|
|
- String[] files = tmpDir.list(new FilenameFilter() {
|
|
- public boolean accept(File dir, String name) {
|
|
- if (!name.startsWith(dirNamePrefix)) {
|
|
- return false;
|
|
- }
|
|
- File candidate = new File(dir, name);
|
|
- return ((candidate.isDirectory() || candidate.isFile())
|
|
- && candidate.canRead());
|
|
- }
|
|
- });
|
|
-
|
|
- long newestTime = 0;
|
|
+ List<String> tmpDirs = getTempDirectories(null, lvmid);
|
|
File newest = null;
|
|
|
|
- for (int i = 0; i < files.length; i++) {
|
|
- File f = new File(tmpDirName + files[i]);
|
|
- File candidate = null;
|
|
+ for (String dir : tmpDirs) {
|
|
+ /*
|
|
+ * iterate over all files in all directories in this tmpDir that
|
|
+ * match the file name patterns.
|
|
+ */
|
|
+ File tmpDir = new File(dir);
|
|
+ String[] files = tmpDir.list(new FilenameFilter() {
|
|
+ public boolean accept(File dir, String name) {
|
|
+ if (!name.startsWith(dirNamePrefix)) {
|
|
+ return false;
|
|
+ }
|
|
+ File candidate = new File(dir, name);
|
|
+ return ((candidate.isDirectory() || candidate.isFile())
|
|
+ && candidate.canRead());
|
|
+ }
|
|
+ });
|
|
|
|
- if (f.exists() && f.isDirectory()) {
|
|
- /*
|
|
- * found a directory matching the name patterns. This
|
|
- * is a 1.4.2 hsperfdata_<user> directory. Check for
|
|
- * file named <lvmid> in that directory
|
|
- */
|
|
- String name = Integer.toString(lvmid);
|
|
- candidate = new File(f.getName(), name);
|
|
+ long newestTime = 0;
|
|
|
|
- } else if (f.exists() && f.isFile()) {
|
|
- /*
|
|
- * found a file matching the name patterns. This
|
|
- * is a 1.4.1 hsperfdata_<lvmid> file.
|
|
- */
|
|
- candidate = f;
|
|
+ for (String file : files) {
|
|
+ File f = new File(dir + file);
|
|
+ File candidate = null;
|
|
|
|
- } else {
|
|
- // unexpected - let conditional below filter this one out
|
|
- candidate = f;
|
|
- }
|
|
+ if (f.exists() && f.isDirectory()) {
|
|
+ /*
|
|
+ * found a directory matching the name patterns. This
|
|
+ * is a 1.4.2 hsperfdata_<user> directory. Check for
|
|
+ * file named <lvmid> in that directory
|
|
+ */
|
|
+ String name = f.getAbsolutePath() + File.separator + lvmid;
|
|
+ candidate = new File(name);
|
|
+ // Try NameSpace Id if Host Id doesn't exist.
|
|
+ if (!candidate.exists()) {
|
|
+ name = f.getAbsolutePath() + File.separator +
|
|
+ platSupport.getNamespaceVmId(lvmid);
|
|
+ candidate = new File(name);
|
|
+ }
|
|
+ } else if (f.exists() && f.isFile()) {
|
|
+ /*
|
|
+ * found a file matching the name patterns. This
|
|
+ * is a 1.4.1 hsperfdata_<lvmid> file.
|
|
+ */
|
|
+ candidate = f;
|
|
|
|
- if (candidate.exists() && candidate.isFile()
|
|
- && candidate.canRead()) {
|
|
- long modTime = candidate.lastModified();
|
|
- if (modTime >= newestTime) {
|
|
- newestTime = modTime;
|
|
- newest = candidate;
|
|
+ } else {
|
|
+ // unexpected - let conditional below filter this one out
|
|
+ candidate = f;
|
|
+ }
|
|
+
|
|
+ if (candidate.exists() && candidate.isFile()
|
|
+ && candidate.canRead()) {
|
|
+ long modTime = candidate.lastModified();
|
|
+ if (modTime >= newestTime) {
|
|
+ newestTime = modTime;
|
|
+ newest = candidate;
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
@@ -176,7 +190,7 @@ public class PerfDataFile {
|
|
* @return File - a File object to the backing store file for the named
|
|
* shared memory region of the target JVM.
|
|
* @see java.io.File
|
|
- * @see #getTempDirectory()
|
|
+ * @see #getTempDirectories()
|
|
*/
|
|
public static File getFile(String user, int lvmid) {
|
|
if (lvmid == 0) {
|
|
@@ -190,11 +204,22 @@ public class PerfDataFile {
|
|
}
|
|
|
|
// first try for 1.4.2 and later JVMs
|
|
- String basename = getTempDirectory(user) + Integer.toString(lvmid);
|
|
- File f = new File(basename);
|
|
+ List<String> tmpDirs = getTempDirectories(user, lvmid);
|
|
+ String basename;
|
|
+ File f;
|
|
|
|
- if (f.exists() && f.isFile() && f.canRead()) {
|
|
- return f;
|
|
+ for (String dir : tmpDirs) {
|
|
+ basename = dir + lvmid;
|
|
+ f = new File(basename);
|
|
+ if (f.exists() && f.isFile() && f.canRead()) {
|
|
+ return f;
|
|
+ }
|
|
+ // Try NameSpace Id if Host Id doesn't exist.
|
|
+ basename = dir + platSupport.getNamespaceVmId(lvmid);
|
|
+ f = new File(basename);
|
|
+ if (f.exists() && f.isFile() && f.canRead()) {
|
|
+ return f;
|
|
+ }
|
|
}
|
|
|
|
// No hit on 1.4.2 JVMs, try 1.4.1 files
|
|
@@ -235,7 +260,7 @@ public class PerfDataFile {
|
|
public static int getLocalVmId(File file) {
|
|
try {
|
|
// try 1.4.2 and later format first
|
|
- return Integer.parseInt(file.getName());
|
|
+ return(platSupport.getLocalVmId(file));
|
|
} catch (NumberFormatException e) { }
|
|
|
|
// now try the 1.4.1 format
|
|
@@ -266,7 +291,7 @@ public class PerfDataFile {
|
|
* @return String - the name of the temporary directory.
|
|
*/
|
|
public static String getTempDirectory() {
|
|
- return tmpDirName;
|
|
+ return PlatformSupport.getTemporaryDirectory();
|
|
}
|
|
|
|
/**
|
|
@@ -282,26 +307,28 @@ public class PerfDataFile {
|
|
* @return String - the name of the temporary directory.
|
|
*/
|
|
public static String getTempDirectory(String user) {
|
|
- return tmpDirName + dirNamePrefix + user + File.separator;
|
|
+ return getTempDirectory() + dirNamePrefix + user + File.separator;
|
|
}
|
|
|
|
- static {
|
|
- /*
|
|
- * For this to work, the target VM and this code need to use
|
|
- * the same directory. Instead of guessing which directory the
|
|
- * VM is using, we will ask.
|
|
- */
|
|
- String tmpdir = sun.misc.VMSupport.getVMTemporaryDirectory();
|
|
-
|
|
- /*
|
|
- * Assure that the string returned has a trailing File.separator
|
|
- * character. This check was added because the Linux implementation
|
|
- * changed such that the java.io.tmpdir string no longer terminates
|
|
- * with a File.separator character.
|
|
- */
|
|
- if (tmpdir.lastIndexOf(File.separator) != (tmpdir.length()-1)) {
|
|
- tmpdir = tmpdir + File.separator;
|
|
+ /**
|
|
+ * Return the names of the temporary directories being searched for
|
|
+ * HotSpot PerfData backing store files.
|
|
+ * <p>
|
|
+ * This method returns the traditional host temp directory but also
|
|
+ * includes a list of temp directories used by containers.
|
|
+ *
|
|
+ * @return List<String> - A List of temporary directories to search.
|
|
+ */
|
|
+ public static List<String> getTempDirectories(String userName, int vmid) {
|
|
+ List<String> list = platSupport.getTemporaryDirectories(vmid);
|
|
+ if (userName == null) {
|
|
+ return list;
|
|
}
|
|
- tmpDirName = tmpdir;
|
|
+
|
|
+ List<String> nameList = list.stream()
|
|
+ .map(name -> name + dirNamePrefix + userName + File.separator)
|
|
+ .collect(Collectors.toList());
|
|
+
|
|
+ return nameList;
|
|
}
|
|
}
|
|
--
|
|
2.22.0
|
|
|