# HG changeset patch # User simonis # Date 1438193916 -7200 # Wed Jul 29 20:18:36 2015 +0200 # Node ID 9db66f9cf04f5c9ae212706846350ffff132c8e4 # Parent c1dc1321df35474225bca56b660de907245cc4ee 8131168: Refactor ProcessHandleImpl_*.c and add implememtation for AIX diff --git a/src/java.base/aix/native/libjava/ProcessHandleImpl_aix.c b/src/java.base/aix/native/libjava/ProcessHandleImpl_aix.c new file mode 100644 --- /dev/null +++ b/src/java.base/aix/native/libjava/ProcessHandleImpl_aix.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2015, 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. + */ + +#include "jni.h" +#include "jni_util.h" +#include "java_lang_ProcessHandleImpl.h" +#include "java_lang_ProcessHandleImpl_Info.h" + +#include "ProcessHandleImpl_unix.h" + +#include + +/* + * Implementation of native ProcessHandleImpl functions for AIX. + * See ProcessHandleImpl_unix.c for more details. + */ + +void os_initNative(JNIEnv *env, jclass clazz) {} + +jint os_getChildren(JNIEnv *env, jlong jpid, jlongArray jarray, + jlongArray jparentArray, jlongArray jstimesArray) { + return unix_getChildren(env, jpid, jarray, jparentArray, jstimesArray); +} + +pid_t os_getParentPidAndTimings(JNIEnv *env, pid_t pid, jlong *total, jlong *start) { + return unix_getParentPidAndTimings(env, pid, total, start); +} + +void os_getCmdlineAndUserInfo(JNIEnv *env, jobject jinfo, pid_t pid) { + unix_getCmdlineAndUserInfo(env, jinfo, pid); +} diff --git a/src/java.base/linux/native/libjava/ProcessHandleImpl_linux.c b/src/java.base/linux/native/libjava/ProcessHandleImpl_linux.c new file mode 100644 --- /dev/null +++ b/src/java.base/linux/native/libjava/ProcessHandleImpl_linux.c @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2015, 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. + */ + +#include "jni.h" +#include "jni_util.h" +#include "java_lang_ProcessHandleImpl.h" +#include "java_lang_ProcessHandleImpl_Info.h" + +#include "ProcessHandleImpl_unix.h" + + +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * Implementation of native ProcessHandleImpl functions for Linux. + * See ProcessHandleImpl_unix.c for more details. + */ + +/* Signatures for internal OS specific functions. */ +static long long getBoottime(JNIEnv *env); + +/* A static offset in milliseconds since boot. */ +static long long bootTime_ms; + +void os_initNative(JNIEnv *env, jclass clazz) { + bootTime_ms = getBoottime(env); +} + +jint os_getChildren(JNIEnv *env, jlong jpid, jlongArray jarray, + jlongArray jparentArray, jlongArray jstimesArray) { + return unix_getChildren(env, jpid, jarray, jparentArray, jstimesArray); +} + +/** + * Read /proc//stat and return the ppid, total cputime and start time. + * -1 is fail; zero is unknown; > 0 is parent pid + * 'total' will contain the running time of 'pid' in nanoseconds. + * 'start' will contain the start time of 'pid' in milliseconds since epoch. + */ +pid_t os_getParentPidAndTimings(JNIEnv *env, pid_t pid, + jlong *totalTime, jlong* startTime) { + FILE* fp; + char buffer[2048]; + int statlen; + char fn[32]; + char* s; + int parentPid; + long unsigned int utime = 0; // clock tics + long unsigned int stime = 0; // clock tics + long long unsigned int start = 0; // microseconds + + /* + * Try to stat and then open /proc/%d/stat + */ + snprintf(fn, sizeof fn, "/proc/%d/stat", pid); + + fp = fopen(fn, "r"); + if (fp == NULL) { + return -1; // fail, no such /proc/pid/stat + } + + /* + * The format is: pid (command) state ppid ... + * As the command could be anything we must find the right most + * ")" and then skip the white spaces that follow it. + */ + statlen = fread(buffer, 1, (sizeof buffer - 1), fp); + fclose(fp); + if (statlen < 0) { + return 0; // parent pid is not available + } + + buffer[statlen] = '\0'; + s = strchr(buffer, '('); + if (s == NULL) { + return 0; // parent pid is not available + } + // Found start of command, skip to end + s++; + s = strrchr(s, ')'); + if (s == NULL) { + return 0; // parent pid is not available + } + s++; + + // Scan the needed fields from status, retaining only ppid(4), + // utime (14), stime(15), starttime(22) + if (4 != sscanf(s, " %*c %d %*d %*d %*d %*d %*d %*u %*u %*u %*u %lu %lu %*d %*d %*d %*d %*d %*d %llu", + &parentPid, &utime, &stime, &start)) { + return 0; // not all values parsed; return error + } + + *totalTime = (utime + stime) * (jlong)(1000000000 / clock_ticks_per_second); + + *startTime = bootTime_ms + ((start * 1000) / clock_ticks_per_second); + + return parentPid; +} + +void os_getCmdlineAndUserInfo(JNIEnv *env, jobject jinfo, pid_t pid) { + int fd; + int cmdlen = 0; + char *cmdline = NULL, *cmdEnd; // used for command line args and exe + jstring cmdexe = NULL; + char fn[32]; + struct stat stat_buf; + + /* + * Try to open /proc/%d/cmdline + */ + snprintf(fn, sizeof fn, "/proc/%d/cmdline", pid); + if ((fd = open(fn, O_RDONLY)) < 0) { + return; + } + + if (fstat(fd, &stat_buf) == 0) { + unix_getUserInfo(env, jinfo, stat_buf.st_uid); + } + + do { // Block to break out of on errors + int i; + char *s; + + cmdline = (char*)malloc(PATH_MAX); + if (cmdline == NULL) { + break; + } + + /* + * The path to the executable command is the link in /proc//exe. + */ + snprintf(fn, sizeof fn, "/proc/%d/exe", pid); + if ((cmdlen = readlink(fn, cmdline, PATH_MAX - 1)) > 0) { + // null terminate and create String to store for command + cmdline[cmdlen] = '\0'; + cmdexe = JNU_NewStringPlatform(env, cmdline); + (*env)->ExceptionClear(env); // unconditionally clear any exception + } + + /* + * The buffer format is the arguments nul terminated with an extra nul. + */ + cmdlen = read(fd, cmdline, PATH_MAX-1); + if (cmdlen < 0) { + break; + } + + // Terminate the buffer and count the arguments + cmdline[cmdlen] = '\0'; + cmdEnd = &cmdline[cmdlen + 1]; + for (s = cmdline,i = 0; *s != '\0' && (s < cmdEnd); i++) { + s += strnlen(s, (cmdEnd - s)) + 1; + } + + unix_fillArgArray(env, jinfo, i, cmdline, cmdEnd, cmdexe); + } while (0); + + if (cmdline != NULL) { + free(cmdline); + } + if (fd >= 0) { + close(fd); + } +} + +/** + * Read the boottime from /proc/stat. + */ +static long long getBoottime(JNIEnv *env) { + FILE *fp; + char *line = NULL; + size_t len = 0; + long long bootTime = 0; + + fp = fopen("/proc/stat", "r"); + if (fp == NULL) { + return -1; + } + + while (getline(&line, &len, fp) != -1) { + if (sscanf(line, "btime %llu", &bootTime) == 1) { + break; + } + } + free(line); + + if (fp != 0) { + fclose(fp); + } + + return bootTime * 1000; +} diff --git a/src/java.base/macosx/native/libjava/ProcessHandleImpl_macosx.c b/src/java.base/macosx/native/libjava/ProcessHandleImpl_macosx.c --- a/src/java.base/macosx/native/libjava/ProcessHandleImpl_macosx.c +++ b/src/java.base/macosx/native/libjava/ProcessHandleImpl_macosx.c @@ -28,6 +28,8 @@ #include "java_lang_ProcessHandleImpl.h" #include "java_lang_ProcessHandleImpl_Info.h" +#include "ProcessHandleImpl_unix.h" + #include #include #include @@ -38,144 +40,15 @@ #include /** - * Implementations of ProcessHandleImpl functions for MAC OS X; - * are NOT common to all Unix variants. + * Implementation of native ProcessHandleImpl functions for MAC OS X. + * See ProcessHandleImpl_unix.c for more details. */ -static void getStatInfo(JNIEnv *env, jobject jinfo, pid_t pid); -static void getCmdlineInfo(JNIEnv *env, jobject jinfo, pid_t pid); - -/* - * Common Unix function to lookup the uid and return the user name. - */ -extern jstring uidToUser(JNIEnv* env, uid_t uid); - -/* Field id for jString 'command' in java.lang.ProcessHandle.Info */ -static jfieldID ProcessHandleImpl_Info_commandID; - -/* Field id for jString[] 'arguments' in java.lang.ProcessHandle.Info */ -static jfieldID ProcessHandleImpl_Info_argumentsID; - -/* Field id for jlong 'totalTime' in java.lang.ProcessHandle.Info */ -static jfieldID ProcessHandleImpl_Info_totalTimeID; - -/* Field id for jlong 'startTime' in java.lang.ProcessHandle.Info */ -static jfieldID ProcessHandleImpl_Info_startTimeID; - -/* Field id for jString 'user' in java.lang.ProcessHandleImpl.Info */ -static jfieldID ProcessHandleImpl_Info_userID; - -/* static value for clock ticks per second. */ -static long clock_ticks_per_second; - -/************************************************************** - * Static method to initialize field IDs and the ticks per second rate. - * - * Class: java_lang_ProcessHandleImpl_Info - * Method: initIDs - * Signature: ()V - */ -JNIEXPORT void JNICALL -Java_java_lang_ProcessHandleImpl_00024Info_initIDs(JNIEnv *env, jclass clazz) { - CHECK_NULL(ProcessHandleImpl_Info_commandID = - (*env)->GetFieldID(env, clazz, "command", "Ljava/lang/String;")); - CHECK_NULL(ProcessHandleImpl_Info_argumentsID = - (*env)->GetFieldID(env, clazz, "arguments", "[Ljava/lang/String;")); - CHECK_NULL(ProcessHandleImpl_Info_totalTimeID = - (*env)->GetFieldID(env, clazz, "totalTime", "J")); - CHECK_NULL(ProcessHandleImpl_Info_startTimeID = - (*env)->GetFieldID(env, clazz, "startTime", "J")); - CHECK_NULL(ProcessHandleImpl_Info_userID = - (*env)->GetFieldID(env, clazz, "user", "Ljava/lang/String;")); -} -/************************************************************** - * Static method to initialize the ticks per second rate. - * - * Class: java_lang_ProcessHandleImpl - * Method: initNative - * Signature: ()V - */ -JNIEXPORT void JNICALL -Java_java_lang_ProcessHandleImpl_initNative(JNIEnv *env, jclass clazz) { - clock_ticks_per_second = sysconf(_SC_CLK_TCK); -} - -/* - * Check if a process is alive. - * Return the start time (ms since 1970) if it is available. - * If the start time is not available return 0. - * If the pid is invalid, return -1. - * - * Class: java_lang_ProcessHandleImpl - * Method: isAlive0 - * Signature: (J)J - */ -JNIEXPORT jlong JNICALL -Java_java_lang_ProcessHandleImpl_isAlive0(JNIEnv *env, jobject obj, jlong jpid) { - pid_t pid = (pid_t) jpid; - struct kinfo_proc kp; - size_t bufSize = sizeof kp; - - // Read the process info for the specific pid - int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}; - - if (sysctl(mib, 4, &kp, &bufSize, NULL, 0) < 0) { - return (errno == EINVAL) ? -1 : 0; - } else { - return (bufSize == 0) ? -1 : - (jlong) (kp.kp_proc.p_starttime.tv_sec * 1000 - + kp.kp_proc.p_starttime.tv_usec / 1000); - } -} - -/* - * Returns the parent pid of the requested pid. - * - * Class: java_lang_ProcessHandleImpl - * Method: parent0 - * Signature: (J)J - */ -JNIEXPORT jlong JNICALL -Java_java_lang_ProcessHandleImpl_parent0(JNIEnv *env, - jobject obj, - jlong jpid, - jlong startTime) { - pid_t pid = (pid_t) jpid; - pid_t ppid = -1; - - if (pid == getpid()) { - ppid = getppid(); - } else { - const pid_t pid = (pid_t) jpid; - struct kinfo_proc kp; - size_t bufSize = sizeof kp; - - // Read the process info for the specific pid - int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}; - if (sysctl(mib, 4, &kp, &bufSize, NULL, 0) < 0) { - JNU_ThrowByNameWithLastError(env, - "java/lang/RuntimeException", "sysctl failed"); - return -1; - } - // If the buffer is full and for the pid requested then check the start - if (bufSize > 0 && kp.kp_proc.p_pid == pid) { - jlong start = (jlong) (kp.kp_proc.p_starttime.tv_sec * 1000 - + kp.kp_proc.p_starttime.tv_usec / 1000); - if (start == startTime || start == 0 || startTime == 0) { - ppid = kp.kp_eproc.e_ppid; - } - } - } - return (jlong) ppid; -} +void os_initNative(JNIEnv *env, jclass clazz) {} /* * Returns the children of the requested pid and optionally each parent. * - * Class: java_lang_ProcessHandleImpl - * Method: getProcessPids0 - * Signature: (J[J[J)I - * * Use sysctl to accumulate any process whose parent pid is zero or matches. * The resulting pids are stored into the array of longs. * The number of pids is returned if they all fit. @@ -183,13 +56,8 @@ * If the array is too short, excess pids are not stored and * the desired length is returned. */ -JNIEXPORT jint JNICALL -Java_java_lang_ProcessHandleImpl_getProcessPids0(JNIEnv *env, - jclass clazz, - jlong jpid, - jlongArray jarray, - jlongArray jparentArray, - jlongArray jstimesArray) { +jint os_getChildren(JNIEnv *env, jlong jpid, jlongArray jarray, + jlongArray jparentArray, jlongArray jstimesArray) { jlong* pids = NULL; jlong* ppids = NULL; jlong* stimes = NULL; @@ -303,35 +171,17 @@ return count; } -/************************************************************** - * Implementation of ProcessHandleImpl_Info native methods. +/** + * Use sysctl and return the ppid, total cputime and start time. + * Return: -1 is fail; zero is unknown; > 0 is parent pid + * 'total' will contain the running time of 'pid' in nanoseconds. + * 'start' will contain the start time of 'pid' in milliseconds since epoch. */ - -/* - * Fill in the Info object from the OS information about the process. - * - * Class: java_lang_ProcessHandleImpl - * Method: info0 - * Signature: (J)I - */ -JNIEXPORT void JNICALL -Java_java_lang_ProcessHandleImpl_00024Info_info0(JNIEnv *env, - jobject jinfo, - jlong jpid) { - pid_t pid = (pid_t) jpid; - getStatInfo(env, jinfo, pid); - getCmdlineInfo(env, jinfo, pid); -} - -/** - * Read /proc//stat and fill in the fields of the Info object. - * The executable name, plus the user, system, and start times are gathered. - */ -static void getStatInfo(JNIEnv *env, jobject jinfo, pid_t jpid) { - jlong totalTime; // nanoseconds - unsigned long long startTime; // milliseconds +pid_t os_getParentPidAndTimings(JNIEnv *env, pid_t jpid, + jlong *totalTime, jlong *startTime) { const pid_t pid = (pid_t) jpid; + pid_t ppid = -1; struct kinfo_proc kp; size_t bufSize = sizeof kp; @@ -339,85 +189,64 @@ int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}; if (sysctl(mib, 4, &kp, &bufSize, NULL, 0) < 0) { - if (errno == EINVAL) { - return; - } else { - JNU_ThrowByNameWithLastError(env, - "java/lang/RuntimeException", "sysctl failed"); - } - return; + JNU_ThrowByNameWithLastError(env, + "java/lang/RuntimeException", "sysctl failed"); + return -1; } - // Convert the UID to the username - jstring name = NULL; - CHECK_NULL((name = uidToUser(env, kp.kp_eproc.e_ucred.cr_uid))); - (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_userID, name); - JNU_CHECK_EXCEPTION(env); - - startTime = kp.kp_proc.p_starttime.tv_sec * 1000 + - kp.kp_proc.p_starttime.tv_usec / 1000; - - (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_startTimeID, startTime); - JNU_CHECK_EXCEPTION(env); + if (bufSize > 0 && kp.kp_proc.p_pid == pid) { + *startTime = kp.kp_proc.p_starttime.tv_sec * 1000 + + kp.kp_proc.p_starttime.tv_usec / 1000; + ppid = kp.kp_eproc.e_ppid; + } // Get cputime if for current process if (pid == getpid()) { struct rusage usage; - if (getrusage(RUSAGE_SELF, &usage) != 0) { - return; + if (getrusage(RUSAGE_SELF, &usage) == 0) { + jlong microsecs = + usage.ru_utime.tv_sec * 1000 * 1000 + usage.ru_utime.tv_usec + + usage.ru_stime.tv_sec * 1000 * 1000 + usage.ru_stime.tv_usec; + *totalTime = microsecs * 1000; } - jlong microsecs = - usage.ru_utime.tv_sec * 1000 * 1000 + usage.ru_utime.tv_usec + - usage.ru_stime.tv_sec * 1000 * 1000 + usage.ru_stime.tv_usec; - totalTime = microsecs * 1000; - (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_totalTimeID, totalTime); - JNU_CHECK_EXCEPTION(env); } + + return ppid; + } /** - * Construct the argument array by parsing the arguments from the sequence of arguments. + * Return the uid of a process or -1 on error */ -static int fillArgArray(JNIEnv *env, jobject jinfo, int nargs, - const char *cp, const char *argsEnd) { - jstring str = NULL; - jobject argsArray; - int i; +static uid_t getUID(pid_t pid) { + struct kinfo_proc kp; + size_t bufSize = sizeof kp; - if (nargs < 1) { - return 0; + // Read the process info for the specific pid + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}; + + if (sysctl(mib, 4, &kp, &bufSize, NULL, 0) == 0) { + if (bufSize > 0 && kp.kp_proc.p_pid == pid) { + return kp.kp_eproc.e_ucred.cr_uid; + } } - // Create a String array for nargs-1 elements - CHECK_NULL_RETURN((argsArray = (*env)->NewObjectArray(env, - nargs - 1, JNU_ClassString(env), NULL)), -1); - - for (i = 0; i < nargs - 1; i++) { - // skip to the next argument; omits arg[0] - cp += strnlen(cp, (argsEnd - cp)) + 1; - - if (cp > argsEnd || *cp == '\0') { - return -2; // Off the end pointer or an empty argument is an error - } - - CHECK_NULL_RETURN((str = JNU_NewStringPlatform(env, cp)), -1); - - (*env)->SetObjectArrayElement(env, argsArray, i, str); - JNU_CHECK_EXCEPTION_RETURN(env, -3); - } - (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_argumentsID, argsArray); - JNU_CHECK_EXCEPTION_RETURN(env, -4); - return 0; + return (uid_t)-1; } /** * Retrieve the command and arguments for the process and store them * into the Info object. */ -static void getCmdlineInfo(JNIEnv *env, jobject jinfo, pid_t pid) { +void os_getCmdlineAndUserInfo(JNIEnv *env, jobject jinfo, pid_t pid) { int mib[3], maxargs, nargs, i; size_t size; char *args, *cp, *sp, *np; + // Get the UID first. This is done here because it is cheap to do it here + // on other platforms like Linux/Solaris/AIX where the uid comes from the + // same source like the command line info. + unix_getUserInfo(env, jinfo, getUID(pid)); + // Get the maximum size of the arguments mib[0] = CTL_KERN; mib[1] = KERN_ARGMAX; @@ -437,7 +266,7 @@ do { // a block to break out of on error char *argsEnd; - jstring str = NULL; + jstring cmdexe = NULL; mib[0] = CTL_KERN; mib[1] = KERN_PROCARGS2; @@ -456,11 +285,7 @@ argsEnd = &args[size]; // Store the command executable path - if ((str = JNU_NewStringPlatform(env, cp)) == NULL) { - break; - } - (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_commandID, str); - if ((*env)->ExceptionCheck(env)) { + if ((cmdexe = JNU_NewStringPlatform(env, cp)) == NULL) { break; } @@ -471,7 +296,7 @@ } } - fillArgArray(env, jinfo, nargs, cp, argsEnd); + unix_fillArgArray(env, jinfo, nargs, cp, argsEnd, cmdexe); } while (0); // Free the arg buffer free(args); diff --git a/src/java.base/share/classes/java/lang/ProcessHandle.java b/src/java.base/share/classes/java/lang/ProcessHandle.java --- a/src/java.base/share/classes/java/lang/ProcessHandle.java +++ b/src/java.base/share/classes/java/lang/ProcessHandle.java @@ -219,6 +219,11 @@ /** * Returns the executable pathname of the process. * + * @implNote Note that the returned pathname may be truncated on some + * platforms due to system limitations. It may also only + * contain the name of the executable without the full path + * information. + * * @return an {@code Optional} of the executable pathname * of the process */ @@ -227,6 +232,9 @@ /** * Returns an array of Strings of the arguments of the process. * + * @implNote Note that the argument list may be truncated on some + * platforms due to system limitations. + * * @return an {@code Optional} of the arguments of the process */ public Optional arguments(); diff --git a/src/java.base/share/classes/java/lang/ProcessHandleImpl.java b/src/java.base/share/classes/java/lang/ProcessHandleImpl.java --- a/src/java.base/share/classes/java/lang/ProcessHandleImpl.java +++ b/src/java.base/share/classes/java/lang/ProcessHandleImpl.java @@ -472,7 +472,7 @@ /** * Implementation of ProcessHandle.Info. * Information snapshot about a process. - * The attributes of a process vary by operating system and not available + * The attributes of a process vary by operating system and are not available * in all implementations. Additionally, information about other processes * is limited by the operating system privileges of the process making the request. * If a value is not available, either a {@code null} or {@code -1} is stored. diff --git a/src/java.base/solaris/native/libjava/ProcessHandleImpl_solaris.c b/src/java.base/solaris/native/libjava/ProcessHandleImpl_solaris.c --- a/src/java.base/solaris/native/libjava/ProcessHandleImpl_solaris.c +++ b/src/java.base/solaris/native/libjava/ProcessHandleImpl_solaris.c @@ -28,364 +28,29 @@ #include "java_lang_ProcessHandleImpl.h" #include "java_lang_ProcessHandleImpl_Info.h" +#include "ProcessHandleImpl_unix.h" + #include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -/** - * Implementations of ProcessHandleImpl functions that are - * NOT common to all Unix variants: - * - getProcessPids0(pid, pidArray) - * - * Implementations of ProcessHandleImpl_Info - * - totalTime, startTime - * - Command - * - Arguments +/* + * Implementation of native ProcessHandleImpl functions for Solaris. + * See ProcessHandleImpl_unix.c for more details. */ -/* - * Signatures for internal OS specific functions. - */ -static pid_t getStatInfo(JNIEnv *env, pid_t pid, - jlong *totalTime, jlong* startTime, - uid_t *uid); -static void getCmdlineInfo(JNIEnv *env, jobject jinfo, pid_t pid); +void os_initNative(JNIEnv *env, jclass clazz) {} -extern jstring uidToUser(JNIEnv* env, uid_t uid); - -/* Field id for jString 'command' in java.lang.ProcessHandle.Info */ -static jfieldID ProcessHandleImpl_Info_commandID; - -/* Field id for jString[] 'arguments' in java.lang.ProcessHandle.Info */ -static jfieldID ProcessHandleImpl_Info_argumentsID; - -/* Field id for jlong 'totalTime' in java.lang.ProcessHandle.Info */ -static jfieldID ProcessHandleImpl_Info_totalTimeID; - -/* Field id for jlong 'startTime' in java.lang.ProcessHandle.Info */ -static jfieldID ProcessHandleImpl_Info_startTimeID; - -/* Field id for jString 'user' in java.lang.ProcessHandleImpl.Info */ -static jfieldID ProcessHandleImpl_Info_userID; - -/* static value for clock ticks per second. */ -static long clock_ticks_per_second; - -/************************************************************** - * Static method to initialize field IDs and the ticks per second rate. - * - * Class: java_lang_ProcessHandleImpl_Info - * Method: initIDs - * Signature: ()V - */ -JNIEXPORT void JNICALL -Java_java_lang_ProcessHandleImpl_00024Info_initIDs(JNIEnv *env, jclass clazz) { - CHECK_NULL(ProcessHandleImpl_Info_commandID = (*env)->GetFieldID(env, - clazz, "command", "Ljava/lang/String;")); - CHECK_NULL(ProcessHandleImpl_Info_argumentsID = (*env)->GetFieldID(env, - clazz, "arguments", "[Ljava/lang/String;")); - CHECK_NULL(ProcessHandleImpl_Info_totalTimeID = (*env)->GetFieldID(env, - clazz, "totalTime", "J")); - CHECK_NULL(ProcessHandleImpl_Info_startTimeID = (*env)->GetFieldID(env, - clazz, "startTime", "J")); - CHECK_NULL(ProcessHandleImpl_Info_userID = (*env)->GetFieldID(env, - clazz, "user", "Ljava/lang/String;")); +jint os_getChildren(JNIEnv *env, jlong jpid, jlongArray jarray, + jlongArray jparentArray, jlongArray jstimesArray) { + return unix_getChildren(env, jpid, jarray, jparentArray, jstimesArray); } -/************************************************************** - * Static method to initialize the ticks per second rate. - * - * Class: java_lang_ProcessHandleImpl - * Method: initNative - * Signature: ()V - */ -JNIEXPORT void JNICALL -Java_java_lang_ProcessHandleImpl_initNative(JNIEnv *env, jclass clazz) { - clock_ticks_per_second = sysconf(_SC_CLK_TCK); +pid_t os_getParentPidAndTimings(JNIEnv *env, pid_t pid, jlong *total, jlong *start) { + return unix_getParentPidAndTimings(env, pid, total, start); } -/* - * Check if a process is alive. - * Return the start time (ms since 1970) if it is available. - * If the start time is not available return 0. - * If the pid is invalid, return -1. - * - * Class: java_lang_ProcessHandleImpl - * Method: isAlive0 - * Signature: (J)J - */ -JNIEXPORT jlong JNICALL -Java_java_lang_ProcessHandleImpl_isAlive0(JNIEnv *env, jobject obj, jlong jpid) { - pid_t pid = (pid_t) jpid; - jlong startTime = 0L; - jlong totalTime = 0L; - uid_t uid = -1; - pid_t ppid = getStatInfo(env, pid, &totalTime, &startTime, &uid); - return (ppid < 0) ? -1 : startTime; +void os_getCmdlineAndUserInfo(JNIEnv *env, jobject jinfo, pid_t pid) { + unix_getCmdlineAndUserInfo(env, jinfo, pid); } -/* - * Returns the parent pid of the requested pid. - * - * Class: java_lang_ProcessHandleImpl - * Method: parent0 - * Signature: (J)J - */ -JNIEXPORT jlong JNICALL -Java_java_lang_ProcessHandleImpl_parent0(JNIEnv *env, - jobject obj, - jlong jpid, - jlong startTime) { - pid_t pid = (pid_t) jpid; - pid_t ppid = -1; - - if (pid == getpid()) { - ppid = getppid(); - } else { - jlong start = 0L; - jlong total = 0L; - uid_t uid = -1; - - pid_t ppid = getStatInfo(env, pid, &total, &start, &uid); - if (start != startTime - && start != 0 - && startTime != 0) { - ppid = -1; - } - } - return (jlong) ppid; -} - -/* - * Returns the children of the requested pid and optionally each parent. - * - * Class: java_lang_ProcessHandleImpl - * Method: getChildPids - * Signature: (J[J)I - * - * Reads /proc and accumulates any process who parent pid matches. - * The resulting pids are stored into the array of longs. - * The number of pids is returned if they all fit. - * If the array is too short, the desired length is returned. - */ -JNIEXPORT jint JNICALL -Java_java_lang_ProcessHandleImpl_getProcessPids0(JNIEnv *env, - jclass clazz, - jlong jpid, - jlongArray jarray, - jlongArray jparentArray, - jlongArray jstimesArray) { - DIR* dir; - struct dirent* ptr; - pid_t pid = (pid_t) jpid; - jlong* pids = NULL; - jlong* ppids = NULL; - jlong* stimes = NULL; - jsize parentArraySize = 0; - jsize arraySize = 0; - jsize stimesSize = 0; - jsize count = 0; - char procname[32]; - - arraySize = (*env)->GetArrayLength(env, jarray); - JNU_CHECK_EXCEPTION_RETURN(env, 0); - if (jparentArray != NULL) { - parentArraySize = (*env)->GetArrayLength(env, jparentArray); - JNU_CHECK_EXCEPTION_RETURN(env, 0); - - if (arraySize != parentArraySize) { - JNU_ThrowIllegalArgumentException(env, "array sizes not equal"); - return 0; - } - } - if (jstimesArray != NULL) { - stimesSize = (*env)->GetArrayLength(env, jstimesArray); - JNU_CHECK_EXCEPTION_RETURN(env, -1); - - if (arraySize != stimesSize) { - JNU_ThrowIllegalArgumentException(env, "array sizes not equal"); - return 0; - } - } - - /* - * To locate the children we scan /proc looking for files that have a - * positive integer as a filename. - */ - if ((dir = opendir("/proc")) == NULL) { - JNU_ThrowByNameWithLastError(env, - "java/lang/Runtime", "Unable to open /proc"); - return 0; - } - - do { // Block to break out of on Exception - pids = (*env)->GetLongArrayElements(env, jarray, NULL); - if (pids == NULL) { - break; - } - if (jparentArray != NULL) { - ppids = (*env)->GetLongArrayElements(env, jparentArray, NULL); - if (ppids == NULL) { - break; - } - } - if (jstimesArray != NULL) { - stimes = (*env)->GetLongArrayElements(env, jstimesArray, NULL); - if (stimes == NULL) { - break; - } - } - - while ((ptr = readdir(dir)) != NULL) { - pid_t ppid = 0; - jlong totalTime = 0L; - jlong startTime = 0L; - uid_t uid; // value unused - - /* skip files that aren't numbers */ - pid_t childpid = (pid_t) atoi(ptr->d_name); - if ((int) childpid <= 0) { - continue; - } - - // Read /proc/pid/stat and get the parent pid, and start time - ppid = getStatInfo(env, childpid, &totalTime, &startTime, &uid); - if (ppid >= 0 && (pid == 0 || ppid == pid)) { - if (count < arraySize) { - // Only store if it fits - pids[count] = (jlong) childpid; - - if (ppids != NULL) { - // Store the parent Pid - ppids[count] = (jlong) ppid; - } - if (stimes != NULL) { - // Store the process start time - stimes[count] = startTime; - } - } - count++; // Count to tabulate size needed - } - } - } while (0); - - if (pids != NULL) { - (*env)->ReleaseLongArrayElements(env, jarray, pids, 0); - } - if (ppids != NULL) { - (*env)->ReleaseLongArrayElements(env, jparentArray, ppids, 0); - } - if (stimes != NULL) { - (*env)->ReleaseLongArrayElements(env, jstimesArray, stimes, 0); - } - - closedir(dir); - // If more pids than array had size for; count will be greater than array size - return count; -} - -/************************************************************** - * Implementation of ProcessHandleImpl_Info native methods. - */ - -/* - * Fill in the Info object from the OS information about the process. - * - * Class: java_lang_ProcessHandleImpl_Info - * Method: info0 - * Signature: (J)V - */ -JNIEXPORT void JNICALL -Java_java_lang_ProcessHandleImpl_00024Info_info0(JNIEnv *env, - jobject jinfo, - jlong jpid) { - pid_t pid = (pid_t) jpid; - jlong startTime = 0L; - jlong totalTime = 0L; - uid_t uid = -1; - pid_t ppid = getStatInfo(env, pid, &totalTime, &startTime, &uid); - - getCmdlineInfo(env, jinfo, pid); - - if (ppid > 0) { - jstring str; - (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_startTimeID, startTime); - JNU_CHECK_EXCEPTION(env); - - (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_totalTimeID, totalTime); - JNU_CHECK_EXCEPTION(env); - - CHECK_NULL((str = uidToUser(env, uid))); - (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_userID, str); - JNU_CHECK_EXCEPTION(env); - } -} - -/** - * Read /proc//status and return the ppid, total cputime and start time. - * Return: -1 is fail; zero is unknown; > 0 is parent pid - */ -static pid_t getStatInfo(JNIEnv *env, pid_t pid, - jlong *totalTime, jlong* startTime, - uid_t* uid) { - FILE* fp; - psinfo_t psinfo; - char fn[32]; - int ret; - - /* - * Try to open /proc/%d/status - */ - snprintf(fn, sizeof fn, "/proc/%d/psinfo", pid); - fp = fopen(fn, "r"); - if (fp == NULL) { - return -1; - } - - ret = fread(&psinfo, 1, (sizeof psinfo), fp); - fclose(fp); - if (ret < (sizeof psinfo)) { - return -1; - } - - *totalTime = psinfo.pr_time.tv_sec * 1000000000L + psinfo.pr_time.tv_nsec; - - *startTime = psinfo.pr_start.tv_sec * (jlong)1000 + - psinfo.pr_start.tv_nsec / 1000000; - - *uid = psinfo.pr_uid; - - return (pid_t) psinfo.pr_ppid; -} - -static void getCmdlineInfo(JNIEnv *env, jobject jinfo, pid_t pid) { - char fn[32]; - char exePath[PATH_MAX]; - jstring str = NULL; - int ret; - - /* - * The path to the executable command is the link in /proc//paths/a.out. - */ - snprintf(fn, sizeof fn, "/proc/%d/path/a.out", pid); - if ((ret = readlink(fn, exePath, PATH_MAX - 1)) < 0) { - return; - } - - // null terminate and create String to store for command - exePath[ret] = '\0'; - CHECK_NULL(str = JNU_NewStringPlatform(env, exePath)); - (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_commandID, str); - JNU_CHECK_EXCEPTION(env); -} - diff --git a/src/java.base/unix/native/libjava/ProcessHandleImpl_unix.c b/src/java.base/unix/native/libjava/ProcessHandleImpl_unix.c --- a/src/java.base/unix/native/libjava/ProcessHandleImpl_unix.c +++ b/src/java.base/unix/native/libjava/ProcessHandleImpl_unix.c @@ -28,31 +28,87 @@ #include "java_lang_ProcessHandleImpl.h" #include "java_lang_ProcessHandleImpl_Info.h" +#include "ProcessHandleImpl_unix.h" + #include - #include #include #include #include #include #include +#include +#include +#include +#include #include #include #include -#include -#include -#include +#ifdef _AIX +#include +#endif +#ifdef __solaris__ +#include +#endif /** - * Implementations of ProcessHandleImpl functions that are common to all - * Unix variants: - * - waitForProcessExit0(pid, reap) - * - getCurrentPid0() - * - destroy0(pid, force) + * This file contains the implementation of the native ProcessHandleImpl + * functions which are common to all Unix variants. + * + * The currently supported Unix variants are Solaris, Linux, MaxOS X and AIX. + * The various similarities and differences between these systems make it hard + * to find a clear boundary between platform specific and shared code. + * + * In order to ease code sharing between the platforms while still keeping the + * code as clean as possible (i.e. free of preprocessor macros) we use the + * following source code layout (remember that ProcessHandleImpl_unix.c will + * be compiled on EVERY Unix platform while ProcessHandleImpl_.c will be + * only compiled on the specific OS): + * + * - all the JNI wrappers for the ProcessHandleImpl functions go into this file + * - if their implementation is common on ALL the supported Unix platforms it + * goes right into the JNI wrappers + * - if the whole function or substantial parts of it are platform dependent, + * the implementation goes into os_ functions in + * ProcessHandleImpl_.c + * - if at least two platforms implement an os_ function in the + * same way, this implementation is factored out into unix_, + * placed into this file and called from the corresponding os_ + * function. + * - For convenience, all the os_ and unix_ functions are declared in + * ProcessHandleImpl_unix.h which is included into every + * ProcessHandleImpl_.c file. + * + * Example 1: + * ---------- + * The implementation of Java_java_lang_ProcessHandleImpl_initNative() + * is the same on all platforms except on Linux where it initilizes one + * additional field. So we place the implementation right into + * Java_java_lang_ProcessHandleImpl_initNative() but add call to + * os_init() at the end of the function which is empty on all platforms + * except Linux where it performs the additionally initializations. + * + * Example 2: + * ---------- + * The implementation of Java_java_lang_ProcessHandleImpl_00024Info_info0 is the + * same on Solaris and AIX but different on Linux and MacOSX. We therefore simply + * call the helpers os_getParentPidAndTimings() and os_getCmdlineAndUserInfo(). + * The Linux and MaxOS X versions of these functions (in the corresponding files + * ProcessHandleImpl_linux.c and ProcessHandleImpl_macosx.c) directly contain + * the platform specific implementations while the Solaris and AIX + * implementations simply call back to unix_getParentPidAndTimings() and + * unix_getCmdlineAndUserInfo() which are implemented right in this file. + * + * The term "same implementation" is still a question of interpretation. It my + * be acceptable to have a few ifdef'ed lines if that allows the sharing of a + * huge function. On the other hand, if the platform specific code in a shared + * function grows over a certain limit, it may be better to refactor that + * functionality into corresponding, platform-specific os_ functions. */ + #ifndef WIFEXITED #define WIFEXITED(status) (((status)&0xFF) == 0) #endif @@ -69,6 +125,19 @@ #define WTERMSIG(status) ((status)&0x7F) #endif +#ifdef __solaris__ +/* The child exited because of a signal. + * The best value to return is 0x80 + signal number, + * because that is what all Unix shells do, and because + * it allows callers to distinguish between process exit and + * process death by signal. + * Unfortunately, the historical behavior on Solaris is to return + * the signal number, and we preserve this for compatibility. */ +#define WTERMSIG_RETURN(status) WTERMSIG(status) +#else +#define WTERMSIG_RETURN(status) (WTERMSIG(status) + 0x80) +#endif + #define RESTARTABLE(_cmd, _result) do { \ do { \ _result = _cmd; \ @@ -81,21 +150,72 @@ } while((_result == NULL) && (errno == EINTR)); \ } while(0) -#ifdef __solaris__ - #define STAT_FILE "/proc/%d/status" -#else - #define STAT_FILE "/proc/%d/stat" -#endif + +/* Field id for jString 'command' in java.lang.ProcessHandleImpl.Info */ +jfieldID ProcessHandleImpl_Info_commandID; + +/* Field id for jString[] 'arguments' in java.lang.ProcessHandleImpl.Info */ +jfieldID ProcessHandleImpl_Info_argumentsID; + +/* Field id for jlong 'totalTime' in java.lang.ProcessHandleImpl.Info */ +jfieldID ProcessHandleImpl_Info_totalTimeID; + +/* Field id for jlong 'startTime' in java.lang.ProcessHandleImpl.Info */ +jfieldID ProcessHandleImpl_Info_startTimeID; + +/* Field id for jString 'user' in java.lang.ProcessHandleImpl.Info */ +jfieldID ProcessHandleImpl_Info_userID; + +/* static value for clock ticks per second. */ +long clock_ticks_per_second; + +/************************************************************** + * Static method to initialize field IDs and the ticks per second rate. + * + * Class: java_lang_ProcessHandleImpl_Info + * Method: initIDs + * Signature: ()V + */ +JNIEXPORT void JNICALL +Java_java_lang_ProcessHandleImpl_00024Info_initIDs(JNIEnv *env, jclass clazz) { + + CHECK_NULL(ProcessHandleImpl_Info_commandID = + (*env)->GetFieldID(env, clazz, "command", "Ljava/lang/String;")); + CHECK_NULL(ProcessHandleImpl_Info_argumentsID = + (*env)->GetFieldID(env, clazz, "arguments", "[Ljava/lang/String;")); + CHECK_NULL(ProcessHandleImpl_Info_totalTimeID = + (*env)->GetFieldID(env, clazz, "totalTime", "J")); + CHECK_NULL(ProcessHandleImpl_Info_startTimeID = + (*env)->GetFieldID(env, clazz, "startTime", "J")); + CHECK_NULL(ProcessHandleImpl_Info_userID = + (*env)->GetFieldID(env, clazz, "user", "Ljava/lang/String;")); +} + +/************************************************************** + * Static method to initialize the ticks per second rate. + * + * Class: java_lang_ProcessHandleImpl + * Method: initNative + * Signature: ()V + */ +JNIEXPORT void JNICALL +Java_java_lang_ProcessHandleImpl_initNative(JNIEnv *env, jclass clazz) { + clock_ticks_per_second = sysconf(_SC_CLK_TCK); + os_initNative(env, clazz); +} /* Block until a child process exits and return its exit code. * Note, can only be called once for any given pid if reapStatus = true. + * + * Class: java_lang_ProcessHandleImpl + * Method: waitForProcessExit0 + * Signature: (JZ)I */ JNIEXPORT jint JNICALL Java_java_lang_ProcessHandleImpl_waitForProcessExit0(JNIEnv* env, jclass junk, jlong jpid, - jboolean reapStatus) -{ + jboolean reapStatus) { pid_t pid = (pid_t)jpid; errno = 0; @@ -117,18 +237,7 @@ if (WIFEXITED(status)) { return WEXITSTATUS(status); } else if (WIFSIGNALED(status)) { - /* The child exited because of a signal. - * The best value to return is 0x80 + signal number, - * because that is what all Unix shells do, and because - * it allows callers to distinguish between process exit and - * process death by signal. - * Unfortunately, the historical behavior on Solaris is to return - * the signal number, and we preserve this for compatibility. */ -#ifdef __solaris__ - return WTERMSIG(status); -#else - return 0x80 + WTERMSIG(status); -#endif + return WTERMSIG_RETURN(status); } else { return status; } @@ -156,18 +265,7 @@ */ return siginfo.si_status; } else if (siginfo.si_code == CLD_KILLED || siginfo.si_code == CLD_DUMPED) { - /* The child exited because of a signal. - * The best value to return is 0x80 + signal number, - * because that is what all Unix shells do, and because - * it allows callers to distinguish between process exit and - * process death by signal. - * Unfortunately, the historical behavior on Solaris is to return - * the signal number, and we preserve this for compatibility. */ - #ifdef __solaris__ - return WTERMSIG(siginfo.si_status); - #else - return 0x80 + WTERMSIG(siginfo.si_status); - #endif + return WTERMSIG_RETURN(siginfo.si_status); } else { /* * Unknown exit code; pass it through. @@ -191,7 +289,7 @@ /* * Class: java_lang_ProcessHandleImpl * Method: destroy0 - * Signature: (Z)Z + * Signature: (JJZ)Z */ JNIEXPORT jboolean JNICALL Java_java_lang_ProcessHandleImpl_destroy0(JNIEnv *env, @@ -210,119 +308,52 @@ } } -/** - * Size of password or group entry when not available via sysconf +/* + * Returns the children of the requested pid and optionally each parent and + * start time. + * Accumulates any process who parent pid matches. + * The resulting pids are stored into the array of longs. + * The number of pids is returned if they all fit. + * If the array is too short, the negative of the desired length is returned. + * Class: java_lang_ProcessHandleImpl + * Method: getProcessPids0 + * Signature: (J[J[J[J)I */ -#define ENT_BUF_SIZE 1024 - -/** - * Return a strong username for the uid_t or null. - */ -jstring uidToUser(JNIEnv* env, uid_t uid) { - int result = 0; - int buflen; - char* pwbuf; - jstring name = NULL; - - /* allocate buffer for password record */ - buflen = (int)sysconf(_SC_GETPW_R_SIZE_MAX); - if (buflen == -1) - buflen = ENT_BUF_SIZE; - pwbuf = (char*)malloc(buflen); - if (pwbuf == NULL) { - JNU_ThrowOutOfMemoryError(env, "Unable to open getpwent"); - } else { - struct passwd pwent; - struct passwd* p = NULL; - -#ifdef __solaris__ - RESTARTABLE_RETURN_PTR(getpwuid_r(uid, &pwent, pwbuf, (size_t)buflen), p); -#else - RESTARTABLE(getpwuid_r(uid, &pwent, pwbuf, (size_t)buflen, &p), result); -#endif - - // Return the Java String if a name was found - if (result == 0 && p != NULL && - p->pw_name != NULL && *(p->pw_name) != '\0') { - name = JNU_NewStringPlatform(env, p->pw_name); - } - free(pwbuf); - } - return name; +JNIEXPORT jint JNICALL +Java_java_lang_ProcessHandleImpl_getProcessPids0(JNIEnv *env, + jclass clazz, + jlong jpid, + jlongArray jarray, + jlongArray jparentArray, + jlongArray jstimesArray) { + return os_getChildren(env, jpid, jarray, jparentArray, jstimesArray); } -/** - * Implementations of ProcessHandleImpl functions that are common to - * (some) Unix variants: - * - getProcessPids0(pid, pidArray, parentArray) - */ - -#if defined(__linux__) || defined(__AIX__) - /* - * Signatures for internal OS specific functions. - */ -static pid_t getStatInfo(JNIEnv *env, pid_t pid, - jlong *totalTime, jlong* startTime); -static void getCmdlineInfo(JNIEnv *env, pid_t pid, jobject jinfo); -static long long getBoottime(JNIEnv *env); - -jstring uidToUser(JNIEnv* env, uid_t uid); - -/* Field id for jString 'command' in java.lang.ProcessHandleImpl.Info */ -static jfieldID ProcessHandleImpl_Info_commandID; - -/* Field id for jString[] 'arguments' in java.lang.ProcessHandleImpl.Info */ -static jfieldID ProcessHandleImpl_Info_argumentsID; - -/* Field id for jlong 'totalTime' in java.lang.ProcessHandleImpl.Info */ -static jfieldID ProcessHandleImpl_Info_totalTimeID; - -/* Field id for jlong 'startTime' in java.lang.ProcessHandleImpl.Info */ -static jfieldID ProcessHandleImpl_Info_startTimeID; - -/* Field id for jString 'user' in java.lang.ProcessHandleImpl.Info */ -static jfieldID ProcessHandleImpl_Info_userID; - -/* static value for clock ticks per second. */ -static long clock_ticks_per_second; - -/* A static offset in milliseconds since boot. */ -static long long bootTime_ms; - -/************************************************************** - * Static method to initialize field IDs and the ticks per second rate. + * Fill in the Info object from the OS information about the process. * * Class: java_lang_ProcessHandleImpl_Info - * Method: initIDs - * Signature: ()V + * Method: info0 + * Signature: (Ljava/lang/ProcessHandle/Info;J)I */ JNIEXPORT void JNICALL -Java_java_lang_ProcessHandleImpl_00024Info_initIDs(JNIEnv *env, jclass clazz) { +Java_java_lang_ProcessHandleImpl_00024Info_info0(JNIEnv *env, + jobject jinfo, + jlong jpid) { + pid_t pid = (pid_t) jpid; + pid_t ppid; + jlong totalTime = -1L; + jlong startTime = -1L; - CHECK_NULL(ProcessHandleImpl_Info_commandID = (*env)->GetFieldID(env, - clazz, "command", "Ljava/lang/String;")); - CHECK_NULL(ProcessHandleImpl_Info_argumentsID = (*env)->GetFieldID(env, - clazz, "arguments", "[Ljava/lang/String;")); - CHECK_NULL(ProcessHandleImpl_Info_totalTimeID = (*env)->GetFieldID(env, - clazz, "totalTime", "J")); - CHECK_NULL(ProcessHandleImpl_Info_startTimeID = (*env)->GetFieldID(env, - clazz, "startTime", "J")); - CHECK_NULL(ProcessHandleImpl_Info_userID = (*env)->GetFieldID(env, - clazz, "user", "Ljava/lang/String;")); -} + ppid = os_getParentPidAndTimings(env, pid, &totalTime, &startTime); + if (ppid > 0) { + (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_totalTimeID, totalTime); + JNU_CHECK_EXCEPTION(env); -/************************************************************** - * Static method to initialize the ticks per second rate. - * - * Class: java_lang_ProcessHandleImpl - * Method: initNative - * Signature: ()V - */ -JNIEXPORT void JNICALL -Java_java_lang_ProcessHandleImpl_initNative(JNIEnv *env, jclass clazz) { - clock_ticks_per_second = sysconf(_SC_CLK_TCK); - bootTime_ms = getBoottime(env); + (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_startTimeID, startTime); + JNU_CHECK_EXCEPTION(env); + } + os_getCmdlineAndUserInfo(env, jinfo, pid); } /* @@ -340,7 +371,7 @@ pid_t pid = (pid_t) jpid; jlong startTime = 0L; jlong totalTime = 0L; - pid_t ppid = getStatInfo(env, pid, &totalTime, &startTime); + pid_t ppid = os_getParentPidAndTimings(env, pid, &totalTime, &startTime); return (ppid <= 0) ? -1 : startTime; } @@ -350,7 +381,7 @@ * * Class: java_lang_ProcessHandleImpl * Method: parent0 - * Signature: (J)J + * Signature: (JJ)J */ JNIEXPORT jlong JNICALL Java_java_lang_ProcessHandleImpl_parent0(JNIEnv *env, @@ -364,9 +395,9 @@ if (pid == mypid) { ppid = getppid(); } else { - jlong start = 0L;; + jlong start = 0L; jlong total = 0L; // unused - ppid = getStatInfo(env, pid, &total, &start); + ppid = os_getParentPidAndTimings(env, pid, &total, &start); if (start != startTime && start != 0 && startTime != 0) { ppid = -1; } @@ -374,24 +405,101 @@ return (jlong) ppid; } +/** + * Construct the argument array by parsing the arguments from the sequence + * of arguments. The zero'th arg is the command executable but it will only + * be used if the 'cmdexe' argument is NULL. + */ +void unix_fillArgArray(JNIEnv *env, jobject jinfo, int nargs, + char *cp, char *argsEnd, jstring cmdexe) { + jobject argsArray; + int i; + + if (cmdexe == NULL && nargs > 0) { + // Create a string from arg[0] + CHECK_NULL((cmdexe = JNU_NewStringPlatform(env, cp))); + } + (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_commandID, cmdexe); + JNU_CHECK_EXCEPTION(env); + + if (nargs > 1) { + // Create a String array for nargs-1 elements + argsArray = (*env)->NewObjectArray(env, nargs - 1, JNU_ClassString(env), NULL); + CHECK_NULL(argsArray); + + for (i = 0; i < nargs - 1; i++) { + jstring str = NULL; + + cp += strlen(cp) + 1; + if (cp > argsEnd || *cp == '\0') { + return; // Off the end pointer or an empty argument is an error + } + + CHECK_NULL((str = JNU_NewStringPlatform(env, cp))); + + (*env)->SetObjectArrayElement(env, argsArray, i, str); + JNU_CHECK_EXCEPTION(env); + } + (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_argumentsID, argsArray); + JNU_CHECK_EXCEPTION(env); + } +} + +/* Size of password or group entry when not available via sysconf */ +#define ENT_BUF_SIZE 1024 + +void unix_getUserInfo(JNIEnv* env, jobject jinfo, uid_t uid) { + int result = 0; + int buflen; + char* pwbuf; + jstring name = NULL; + + /* allocate buffer for password record */ + buflen = (int)sysconf(_SC_GETPW_R_SIZE_MAX); + if (buflen == -1) { + buflen = ENT_BUF_SIZE; + } + pwbuf = (char*)malloc(buflen); + if (pwbuf == NULL) { + JNU_ThrowOutOfMemoryError(env, "Unable to open getpwent"); + } else { + struct passwd pwent; + struct passwd* p = NULL; + +#ifdef __solaris__ + RESTARTABLE_RETURN_PTR(getpwuid_r(uid, &pwent, pwbuf, (size_t)buflen), p); +#else + RESTARTABLE(getpwuid_r(uid, &pwent, pwbuf, (size_t)buflen, &p), result); +#endif + + // Create the Java String if a name was found + if (result == 0 && p != NULL && + p->pw_name != NULL && *(p->pw_name) != '\0') { + name = JNU_NewStringPlatform(env, p->pw_name); + } + free(pwbuf); + } + if (name != NULL) { + (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_userID, name); + } +} + /* - * Returns the children of the requested pid and optionally each parent. + * The following fuctions are common on Solaris, Linux and AIX. + */ + +#if defined(__solaris__) || defined (__linux__) || defined(_AIX) + +/* + * Returns the children of the requested pid and optionally each parent and + * start time. * Reads /proc and accumulates any process who parent pid matches. * The resulting pids are stored into the array of longs. * The number of pids is returned if they all fit. - * If the array is too short, the negative of the desired length is returned. * - * Class: java_lang_ProcessHandleImpl - * Method: getChildPids - * Signature: (J[J[J)I + * If the array is too short, the negative of the desired length is returned. */ -JNIEXPORT jint JNICALL -Java_java_lang_ProcessHandleImpl_getProcessPids0(JNIEnv *env, - jclass clazz, - jlong jpid, - jlongArray jarray, - jlongArray jparentArray, - jlongArray jstimesArray) { - +jint unix_getChildren(JNIEnv *env, jlong jpid, jlongArray jarray, + jlongArray jparentArray, jlongArray jstimesArray) { DIR* dir; struct dirent* ptr; pid_t pid = (pid_t) jpid; @@ -462,8 +570,9 @@ if ((int) childpid <= 0) { continue; } - // Read /proc/pid/stat and get the parent pid, and start time - ppid = getStatInfo(env, childpid, &totalTime, &startTime); + + // Get the parent pid, and start time + ppid = os_getParentPidAndTimings(env, childpid, &totalTime, &startTime); if (ppid > 0 && (pid == 0 || ppid == pid)) { if (count < arraySize) { // Only store if it fits @@ -498,293 +607,124 @@ return count; } +#endif // defined(__solaris__) || defined (__linux__) || defined(_AIX) -/************************************************************** - * Implementation of ProcessHandleImpl_Info native methods. +/* + * The following fuctions are common on Solaris and AIX. */ -/* - * Fill in the Info object from the OS information about the process. - * - * Class: java_lang_ProcessHandleImpl_Info - * Method: info0 - * Signature: (JLjava/lang/ProcessHandle/Info;)I - */ -JNIEXPORT void JNICALL -Java_java_lang_ProcessHandleImpl_00024Info_info0(JNIEnv *env, - jobject jinfo, - jlong jpid) { - pid_t pid = (pid_t) jpid; - pid_t ppid; - jlong totalTime = 0L; - jlong startTime = -1L; - - ppid = getStatInfo(env, pid, &totalTime, &startTime); - if (ppid > 0) { - (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_totalTimeID, totalTime); - JNU_CHECK_EXCEPTION(env); - - (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_startTimeID, startTime); - JNU_CHECK_EXCEPTION(env); - - getCmdlineInfo(env, pid, jinfo); - } -} +#if defined(__solaris__) || defined(_AIX) /** - * Read /proc//stat and return the ppid, total cputime and start time. - * -1 is fail; zero is unknown; > 0 is parent pid + * Helper function to get the 'psinfo_t' data from "/proc/%d/psinfo". + * Returns 0 on success and -1 on error. */ -static pid_t getStatInfo(JNIEnv *env, pid_t pid, - jlong *totalTime, jlong* startTime) { +static int getPsinfo(pid_t pid, psinfo_t *psinfo) { FILE* fp; - char buffer[2048]; - int statlen; char fn[32]; - char* s; - int parentPid; - long unsigned int utime = 0; // clock tics - long unsigned int stime = 0; // clock tics - long long unsigned int start = 0; // microseconds + int ret; /* - * Try to stat and then open /proc/%d/stat + * Try to open /proc/%d/psinfo */ - snprintf(fn, sizeof fn, STAT_FILE, pid); - + snprintf(fn, sizeof fn, "/proc/%d/psinfo", pid); fp = fopen(fn, "r"); if (fp == NULL) { - return -1; // fail, no such /proc/pid/stat - } - - /* - * The format is: pid (command) state ppid ... - * As the command could be anything we must find the right most - * ")" and then skip the white spaces that follow it. - */ - statlen = fread(buffer, 1, (sizeof buffer - 1), fp); - fclose(fp); - if (statlen < 0) { - return 0; // parent pid is not available - } - - buffer[statlen] = '\0'; - s = strchr(buffer, '('); - if (s == NULL) { - return 0; // parent pid is not available - } - // Found start of command, skip to end - s++; - s = strrchr(s, ')'); - if (s == NULL) { - return 0; // parent pid is not available - } - s++; - - // Scan the needed fields from status, retaining only ppid(4), - // utime (14), stime(15), starttime(22) - if (4 != sscanf(s, " %*c %d %*d %*d %*d %*d %*d %*u %*u %*u %*u %lu %lu %*d %*d %*d %*d %*d %*d %llu", - &parentPid, &utime, &stime, &start)) { - return 0; // not all values parsed; return error - } - - *totalTime = (utime + stime) * (jlong)(1000000000 / clock_ticks_per_second); - - *startTime = bootTime_ms + ((start * 1000) / clock_ticks_per_second); - - return parentPid; -} - -/** - * Construct the argument array by parsing the arguments from the sequence - * of arguments. The zero'th arg is the command executable - */ -static int fillArgArray(JNIEnv *env, jobject jinfo, - int nargs, char *cp, char *argsEnd, jstring cmdexe) { - jobject argsArray; - int i; - - if (nargs < 1) { - return 0; - } - - if (cmdexe == NULL) { - // Create a string from arg[0] - CHECK_NULL_RETURN((cmdexe = JNU_NewStringPlatform(env, cp)), -1); - } - (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_commandID, cmdexe); - JNU_CHECK_EXCEPTION_RETURN(env, -3); - - // Create a String array for nargs-1 elements - argsArray = (*env)->NewObjectArray(env, nargs - 1, JNU_ClassString(env), NULL); - CHECK_NULL_RETURN(argsArray, -1); - - for (i = 0; i < nargs - 1; i++) { - jstring str = NULL; - - cp += strnlen(cp, (argsEnd - cp)) + 1; - if (cp > argsEnd || *cp == '\0') { - return -2; // Off the end pointer or an empty argument is an error - } - - CHECK_NULL_RETURN((str = JNU_NewStringPlatform(env, cp)), -1); - - (*env)->SetObjectArrayElement(env, argsArray, i, str); - JNU_CHECK_EXCEPTION_RETURN(env, -3); - } - (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_argumentsID, argsArray); - JNU_CHECK_EXCEPTION_RETURN(env, -4); - return 0; -} - - -static void getCmdlineInfo(JNIEnv *env, pid_t pid, jobject jinfo) { - int fd; - int cmdlen = 0; - char *cmdline = NULL, *cmdEnd; // used for command line args and exe - jstring cmdexe = NULL; - char fn[32]; - struct stat stat_buf; - - /* - * Try to open /proc/%d/cmdline - */ - snprintf(fn, sizeof fn, "/proc/%d/cmdline", pid); - if ((fd = open(fn, O_RDONLY)) < 0) { - return; - } - - do { // Block to break out of on errors - int i; - char *s; - - cmdline = (char*)malloc(PATH_MAX); - if (cmdline == NULL) { - break; - } - - /* - * The path to the executable command is the link in /proc//exe. - */ - snprintf(fn, sizeof fn, "/proc/%d/exe", pid); - if ((cmdlen = readlink(fn, cmdline, PATH_MAX - 1)) > 0) { - // null terminate and create String to store for command - cmdline[cmdlen] = '\0'; - cmdexe = JNU_NewStringPlatform(env, cmdline); - (*env)->ExceptionClear(env); // unconditionally clear any exception - } - - /* - * The buffer format is the arguments nul terminated with an extra nul. - */ - cmdlen = read(fd, cmdline, PATH_MAX-1); - if (cmdlen < 0) { - break; - } - - // Terminate the buffer and count the arguments - cmdline[cmdlen] = '\0'; - cmdEnd = &cmdline[cmdlen + 1]; - for (s = cmdline,i = 0; *s != '\0' && (s < cmdEnd); i++) { - s += strnlen(s, (cmdEnd - s)) + 1; - } - - if (fillArgArray(env, jinfo, i, cmdline, cmdEnd, cmdexe) < 0) { - break; - } - - // Get and store the user name - if (fstat(fd, &stat_buf) == 0) { - jstring name = uidToUser(env, stat_buf.st_uid); - if (name != NULL) { - (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_userID, name); - } - } - } while (0); - - if (cmdline != NULL) { - free(cmdline); - } - if (fd >= 0) { - close(fd); - } -} - -/** - * Read the boottime from /proc/stat. - */ -static long long getBoottime(JNIEnv *env) { - FILE *fp; - char *line = NULL; - size_t len = 0; - long long bootTime = 0; - - fp = fopen("/proc/stat", "r"); - if (fp == NULL) { return -1; } - while (getline(&line, &len, fp) != -1) { - if (sscanf(line, "btime %llu", &bootTime) == 1) { - break; - } + ret = fread(psinfo, 1, sizeof(psinfo_t), fp); + fclose(fp); + if (ret < sizeof(psinfo_t)) { + return -1; } - free(line); + return 0; +} - if (fp != 0) { - fclose(fp); +/** + * Read /proc//psinfo and return the ppid, total cputime and start time. + * Return: -1 is fail; zero is unknown; > 0 is parent pid + * 'total' will contain the running time of 'pid' in nanoseconds. + * 'start' will contain the start time of 'pid' in milliseconds since epoch. + */ +pid_t unix_getParentPidAndTimings(JNIEnv *env, pid_t pid, + jlong *totalTime, jlong* startTime) { + psinfo_t psinfo; + + if (getPsinfo(pid, &psinfo) < 0) { + return -1; } - return bootTime * 1000; + *totalTime = psinfo.pr_time.tv_sec * 1000000000L + psinfo.pr_time.tv_nsec; + + *startTime = psinfo.pr_start.tv_sec * (jlong)1000 + + psinfo.pr_start.tv_nsec / 1000000; + + return (pid_t) psinfo.pr_ppid; } -#endif // defined(__linux__) || defined(__AIX__) +void unix_getCmdlineAndUserInfo(JNIEnv *env, jobject jinfo, pid_t pid) { + psinfo_t psinfo; + char fn[32]; + char exePath[PATH_MAX]; + jstring str = NULL, cmdexe = NULL; + int ret; + /* + * On Solaris, the full path to the executable command is the link in + * /proc//paths/a.out. But it is only readable for processes we own. + */ +#if defined(__solaris__) + snprintf(fn, sizeof fn, "/proc/%d/path/a.out", pid); + if ((ret = readlink(fn, exePath, PATH_MAX - 1)) > 0) { + // null terminate and create String to store for command + exePath[ret] = '\0'; + CHECK_NULL(cmdexe = JNU_NewStringPlatform(env, exePath)); + } +#endif -/* Block until a child process exits and return its exit code. - Note, can only be called once for any given pid. */ -JNIEXPORT jint JNICALL -Java_java_lang_ProcessImpl_waitForProcessExit(JNIEnv* env, - jobject junk, - jint pid) -{ - /* We used to use waitid() on Solaris, waitpid() on Linux, but - * waitpid() is more standard, so use it on all POSIX platforms. */ - int status; - /* Wait for the child process to exit. This returns immediately if - the child has already exited. */ - while (waitpid(pid, &status, 0) < 0) { - switch (errno) { - case ECHILD: return 0; - case EINTR: break; - default: return -1; - } + /* + * Now try to open /proc/%d/psinfo + */ + if (getPsinfo(pid, &psinfo) < 0) { + unix_fillArgArray(env, jinfo, 0, NULL, NULL, cmdexe); + return; } - if (WIFEXITED(status)) { - /* - * The child exited normally; get its exit code. - */ - return WEXITSTATUS(status); - } else if (WIFSIGNALED(status)) { - /* The child exited because of a signal. - * The best value to return is 0x80 + signal number, - * because that is what all Unix shells do, and because - * it allows callers to distinguish between process exit and - * process death by signal. - * Unfortunately, the historical behavior on Solaris is to return - * the signal number, and we preserve this for compatibility. */ -#ifdef __solaris__ - return WTERMSIG(status); -#else - return 0x80 + WTERMSIG(status); -#endif - } else { - /* - * Unknown exit code; pass it through. - */ - return status; + unix_getUserInfo(env, jinfo, psinfo.pr_uid); + + /* + * Now read psinfo.pr_psargs which contains the first PRARGSZ characters of the + * argument list (i.e. arg[0] arg[1] ...). Unfortunately, PRARGSZ is usually set + * to 80 characters only. Nevertheless it's better than nothing :) + */ + { + char prargs[PRARGSZ + 1]; + char *cp, *argsEnd; + int nargs = 1; + strncpy(prargs, psinfo.pr_psargs, PRARGSZ); + prargs[PRARGSZ] = '\0'; + argsEnd = &prargs[strlen(prargs)]; + if (argsEnd == prargs) { + /* If psinfo.pr_psargs didn't contain any strings, use psinfo.pr_fname + * (which only contains the last component of exec()ed pathname) as a + * last resort. This is true for AIX kernel processes for example. + */ + strncpy(prargs, psinfo.pr_fname, PRARGSZ); + prargs[PRARGSZ] = '\0'; + argsEnd = &prargs[strlen(prargs)]; + } else { + /* The arguments are separated by a single whitespace character but we want + * them to be distinct, null-terminated strings. */ + for (cp = prargs; *cp != '\0'; cp++) { + if (*cp == ' ') { + *cp = '\0'; + nargs++; + } + } + } + unix_fillArgArray(env, jinfo, nargs, prargs, argsEnd, cmdexe); } } - +#endif // defined(__solaris__) || defined(_AIX) diff --git a/src/java.base/unix/native/libjava/ProcessHandleImpl_unix.h b/src/java.base/unix/native/libjava/ProcessHandleImpl_unix.h new file mode 100644 --- /dev/null +++ b/src/java.base/unix/native/libjava/ProcessHandleImpl_unix.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2015, 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. + */ + +#include + +/* + * Declaration of ProcessHandleImpl functions common on all Unix platforms. + * 'unix_' functions have a single implementation in ProcessHandleImpl_unix.c + * 'os_' prefixed functions have different, os-specific implementations in the + * various ProcessHandleImpl_{linux,macosx,solaris,aix}.c files. + * See ProcessHandleImpl_unix.c for more details. + */ + +/* Field id for jString 'command' in java.lang.ProcessHandleImpl.Info */ +extern jfieldID ProcessHandleImpl_Info_commandID; + +/* Field id for jString[] 'arguments' in java.lang.ProcessHandleImpl.Info */ +extern jfieldID ProcessHandleImpl_Info_argumentsID; + +/* Field id for jlong 'totalTime' in java.lang.ProcessHandleImpl.Info */ +extern jfieldID ProcessHandleImpl_Info_totalTimeID; + +/* Field id for jlong 'startTime' in java.lang.ProcessHandleImpl.Info */ +extern jfieldID ProcessHandleImpl_Info_startTimeID; + +/* Field id for jString 'user' in java.lang.ProcessHandleImpl.Info */ +extern jfieldID ProcessHandleImpl_Info_userID; + +/* extern value for clock ticks per second. */ +extern long clock_ticks_per_second; + +/** + * Returns the parent pid, -1 on failure or 0 if parent pid can't be found. + * 'total' will contain the running time of 'pid' in nanoseconds. + * 'start' will contain the start time of 'pid' in milliseconds since epoch. + */ +extern pid_t unix_getParentPidAndTimings(JNIEnv *env, pid_t pid, + jlong *total, jlong *start); +extern pid_t os_getParentPidAndTimings(JNIEnv *env, pid_t pid, + jlong *total, jlong *start); + +extern void unix_getCmdlineAndUserInfo(JNIEnv *env, jobject jinfo, pid_t pid); +extern void os_getCmdlineAndUserInfo(JNIEnv *env, jobject jinfo, pid_t pid); + +extern jint unix_getChildren(JNIEnv *env, jlong jpid, jlongArray array, + jlongArray jparentArray, jlongArray jstimesArray); +extern jint os_getChildren(JNIEnv *env, jlong jpid, jlongArray array, + jlongArray jparentArray, jlongArray jstimesArray); + +extern void unix_getUserInfo(JNIEnv* env, jobject jinfo, uid_t uid); +extern void unix_fillArgArray(JNIEnv *env, jobject jinfo, int nargs, + char *cp, char *argsEnd, jstring cmdexe); + +extern void os_initNative(JNIEnv *env, jclass clazz); diff --git a/test/java/lang/ProcessHandle/InfoTest.java b/test/java/lang/ProcessHandle/InfoTest.java --- a/test/java/lang/ProcessHandle/InfoTest.java +++ b/test/java/lang/ProcessHandle/InfoTest.java @@ -136,7 +136,17 @@ } } - + if (Platform.isAix()) { + // Unfortunately, on AIX the usr/sys times reported through + // /proc//psinfo which are used by ProcessHandle.Info + // are running slow compared to the corresponding times reported + // by the times()/getrusage() system calls which are used by + // OperatingSystemMXBean.getProcessCpuTime() and returned by + // the JavaChild for the "cputime" command. + // This is because /proc//status is only updated once a second. + // So we better wait a little bit to get plausible values here. + Thread.sleep(1000); + } ProcessHandle.Info info = p1.info(); System.out.printf(" info: %s%n", info); @@ -166,12 +176,10 @@ Assert.assertEquals(args[offset + i], extraArgs[i], "Actual argument mismatch, index: " + i); } - } else if (Platform.isSolaris()) { - Assert.assertEquals(args.length, 1, - "Expected argument list length: 1"); - Assert.assertNotNull(args[0], - "Expected an argument"); } else { + // Arguments on Solaris and AIX come from /proc/pid/psinfo and + // are usually truncated to 80 characters so there's not much + // we can check here. System.out.printf("No argument test for OS: %s%n", Platform.getOsName()); } @@ -242,9 +250,13 @@ expected + "\', actual: " + command); // Verify the command exists and is executable - File exe = new File(command); - Assert.assertTrue(exe.exists(), "command must exist: " + exe); - Assert.assertTrue(exe.canExecute(), "command must be executable: " + exe); + if (!Platform.isAix()) { + // On Aix, Info.command() only returns arg[0] as command which + // doesn't necessarily contains the full path to the executable. + File exe = new File(command); + Assert.assertTrue(exe.exists(), "command must exist: " + exe); + Assert.assertTrue(exe.canExecute(), "command must be executable: " + exe); + } } if (info.arguments().isPresent()) { String[] args = info.arguments().get(); @@ -269,10 +281,27 @@ public static void test4() { Duration myCputime1 = ProcessUtil.MXBeanCpuTime(); + if (Platform.isAix()) { + // Unfortunately, on AIX the usr/sys times reported through + // /proc//psinfo which are used by ProcessHandle.Info + // are running slow compared to the corresponding times reported + // by the times()/getrusage() system calls which are used by + // OperatingSystemMXBean.getProcessCpuTime() and returned by + // the JavaChild for the "cputime" command. + // So we better wait a little bit to get plausible values here. + try { + Thread.sleep(1000); + } catch (InterruptedException ex) {} + } Optional dur1 = ProcessHandle.current().info().totalCpuDuration(); Duration myCputime2 = ProcessUtil.MXBeanCpuTime(); + if (Platform.isAix()) { + try { + Thread.sleep(1000); + } catch (InterruptedException ex) {} + } Optional dur2 = ProcessHandle.current().info().totalCpuDuration(); if (dur1.isPresent() && dur2.isPresent()) { diff --git a/test/java/lang/ProcessHandle/OnExitTest.java b/test/java/lang/ProcessHandle/OnExitTest.java --- a/test/java/lang/ProcessHandle/OnExitTest.java +++ b/test/java/lang/ProcessHandle/OnExitTest.java @@ -126,6 +126,12 @@ } while (processes.size() < expected && Instant.now().isBefore(endTimeout)); + if (processes.size() < expected) { + printf("WARNING: not all children have been started. Can't complete test.%n"); + printf(" You can try to increase the timeout or%n"); + printf(" you can try to use a faster VM (i.e. not a debug version).%n"); + return; + } children = getAllChildren(procHandle); ConcurrentHashMap> completions =