8193710: jcmd -l and jps commands do not list Java processes running in Docker containers
authorbobv
Mon, 22 Jan 2018 15:05:03 -0500
changeset 48801 099f18e10557
parent 48798 178aec08f009
child 48802 cc7ae802c537
8193710: jcmd -l and jps commands do not list Java processes running in Docker containers Reviewed-by: mchung, dholmes, cjplummer
src/hotspot/os/linux/perfMemory_linux.cpp
src/jdk.internal.jvmstat/linux/classes/sun/jvmstat/PlatformSupportImpl.java
src/jdk.internal.jvmstat/share/classes/sun/jvmstat/PlatformSupport.java
src/jdk.internal.jvmstat/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/LocalVmManager.java
src/jdk.internal.jvmstat/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/PerfDataFile.java
--- a/src/hotspot/os/linux/perfMemory_linux.cpp	Fri Jan 19 16:57:56 2018 +0000
+++ b/src/hotspot/os/linux/perfMemory_linux.cpp	Mon Jan 22 15:05:03 2018 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 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
@@ -147,13 +147,26 @@
 // which is always a local file system and is sometimes a RAM based file
 // system.
 
+
 // 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");
 
-  const char* tmpdir = os::get_temp_directory();
+  if (nspid != -1) {
+    jio_snprintf(buffer, TMP_BUFFER_LEN, "/proc/%d/root%s", vmid, tmpdir);
+    tmpdir = buffer;
+  }
+
   const char* perfdir = PERFDATA_NAME;
   size_t nbytes = strlen(tmpdir) + strlen(perfdir) + strlen(user) + 3;
   char* dirname = NEW_C_HEAP_ARRAY(char, nbytes, mtInternal);
@@ -502,7 +515,10 @@
 //
 // 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) {
@@ -518,8 +534,19 @@
   // 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);
@@ -530,7 +557,7 @@
   }
 
   // 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.
   //
@@ -575,7 +602,7 @@
     errno = 0;
     while ((udentry = os::readdir(subdirp, (struct dirent *)udbuf)) != NULL) {
 
-      if (filename_to_pid(udentry->d_name) == vmid) {
+      if (filename_to_pid(udentry->d_name) == searchpid) {
         struct stat statbuf;
         int result;
 
@@ -626,10 +653,51 @@
   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
@@ -637,13 +705,15 @@
 //
 // 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;
 }
@@ -940,8 +1010,8 @@
   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, '/');
@@ -1088,8 +1158,11 @@
               "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;
@@ -1100,7 +1173,7 @@
               "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.
@@ -1114,7 +1187,7 @@
               "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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.internal.jvmstat/linux/classes/sun/jvmstat/PlatformSupportImpl.java	Mon Jan 22 15:05:03 2018 -0500
@@ -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;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.internal.jvmstat/share/classes/sun/jvmstat/PlatformSupport.java	Mon Jan 22 15:05:03 2018 -0500
@@ -0,0 +1,113 @@
+/*
+ * 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.List;
+import jdk.internal.vm.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 List.of(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;
+    }
+}
--- a/src/jdk.internal.jvmstat/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/LocalVmManager.java	Fri Jan 19 16:57:56 2018 +0000
+++ b/src/jdk.internal.jvmstat/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/LocalVmManager.java	Mon Jan 22 15:05:03 2018 -0500
@@ -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 @@
  */
 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 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 @@
                 }
             };
         } else {
-            tmpdir = new File(PerfDataFile.getTempDirectory(userName));
+            tmpdirs = PerfDataFile.getTempDirectories(userName, 0);
         }
 
         filePattern = Pattern.compile(PerfDataFile.fileNamePattern);
@@ -134,65 +135,72 @@
          */
         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);
+                    // 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.
+                 */
 
-            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);
+                // 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(
-                                    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(
-                                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(
-                            PerfDataFile.getLocalVmId(files[j]));
-                }
-            }
-        }
-
         return jvmSet;
     }
 }
--- a/src/jdk.internal.jvmstat/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/PerfDataFile.java	Fri Jan 19 16:57:56 2018 +0000
+++ b/src/jdk.internal.jvmstat/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/PerfDataFile.java	Mon Jan 22 15:05:03 2018 -0500
@@ -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,8 +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 jdk.internal.vm.VMSupport;
+import sun.jvmstat.PlatformSupport;
 
 /**
  * Class to provide translations from the local Vm Identifier
@@ -46,11 +49,6 @@
     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>
      * This prefix must be kept in sync with the prefix used by the JVM.
@@ -81,6 +79,12 @@
 
 
     /**
+     * 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.
      * <p>
@@ -94,7 +98,7 @@
      * @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) {
@@ -107,56 +111,65 @@
             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;
+
+            for (String file : files) {
+                File f = new File(dir + file);
+                File candidate = null;
 
-            } 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 (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;
 
-            } else {
-                // unexpected - let conditional below filter this one out
-                candidate = f;
-            }
+                } 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;
+                if (candidate.exists() && candidate.isFile()
+                        && candidate.canRead()) {
+                    long modTime = candidate.lastModified();
+                    if (modTime >= newestTime) {
+                        newestTime = modTime;
+                        newest = candidate;
+                    }
                 }
             }
         }
@@ -177,7 +190,7 @@
      * @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) {
@@ -191,11 +204,22 @@
         }
 
         // 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
@@ -236,7 +260,7 @@
     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
@@ -267,7 +291,7 @@
      * @return String - the name of the temporary directory.
      */
     public static String getTempDirectory() {
-        return tmpDirName;
+        return PlatformSupport.getTemporaryDirectory();
     }
 
     /**
@@ -283,26 +307,28 @@
      * @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 = VMSupport.getVMTemporaryDirectory();
+    /**
+     * 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;
+        }
 
-        /*
-         * 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;
+        List<String> nameList = list.stream()
+            .map(name -> name + dirNamePrefix + userName + File.separator)
+            .collect(Collectors.toList());
+
+        return nameList;
     }
 }