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:
parent
a738a4c053
commit
0b97a1bcbe
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user