8193710: jcmd -l and jps commands do not list Java processes running in Docker containers

Reviewed-by: mchung, dholmes, cjplummer
This commit is contained in:
Bob Vandette 2018-01-22 15:05:03 -05:00
parent a738a4c053
commit 0b97a1bcbe
5 changed files with 582 additions and 135 deletions

View File

@ -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 @@ static void save_memory_to_file(char* addr, size_t size) {
// 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");
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);
@ -502,7 +515,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) {
@ -518,8 +534,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);
@ -530,7 +557,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.
//
@ -575,7 +602,7 @@ static char* get_user_name_slow(int vmid, TRAPS) {
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 @@ 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
@ -637,13 +705,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;
}
@ -940,8 +1010,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, '/');
@ -1088,8 +1158,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;
@ -1100,7 +1173,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.
@ -1114,7 +1187,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

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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,72 @@ 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);
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;
}
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(
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;
}
}

View File

@ -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
@ -45,11 +48,6 @@ import jdk.internal.vm.VMSupport;
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>
@ -80,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.
@ -94,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) {
@ -107,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;
}
}
}
}
@ -177,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) {
@ -191,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
@ -236,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
@ -267,7 +291,7 @@ public class PerfDataFile {
* @return String - the name of the temporary directory.
*/
public static String getTempDirectory() {
return tmpDirName;
return PlatformSupport.getTemporaryDirectory();
}
/**
@ -283,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 = 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;
}
}