# HG changeset patch # User simonis # Date 1439541345 -7200 # Node ID 24bb680a160920f0dcd08ca656cb5e4d8a82338f # Parent c8753d0be1778944dc512ec86a459941ea1ad2c3 8131168: Refactor ProcessHandleImpl_*.c and add implememtation for AIX Reviewed-by: rriggs, smarks diff -r c8753d0be177 -r 24bb680a1609 jdk/src/java.base/aix/native/libjava/ProcessHandleImpl_aix.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/aix/native/libjava/ProcessHandleImpl_aix.c Fri Aug 14 10:35:45 2015 +0200 @@ -0,0 +1,50 @@ +/* + * 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 "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 -r c8753d0be177 -r 24bb680a1609 jdk/src/java.base/linux/native/libjava/ProcessHandleImpl_linux.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/linux/native/libjava/ProcessHandleImpl_linux.c Fri Aug 14 10:35:45 2015 +0200 @@ -0,0 +1,266 @@ +/* + * 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; +static long clock_ticks_per_second; +static int pageSize; + +void os_initNative(JNIEnv *env, jclass clazz) { + bootTime_ms = getBoottime(env); + clock_ticks_per_second = sysconf(_SC_CLK_TCK); + pageSize = sysconf(_SC_PAGESIZE); +} + +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; >= 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 -1; // parent pid is not available + } + + buffer[statlen] = '\0'; + s = strchr(buffer, '('); + if (s == NULL) { + return -1; // parent pid is not available + } + // Found start of command, skip to end + s++; + s = strrchr(s, ')'); + if (s == NULL) { + return -1; // 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 = NULL; // used for command line args and exe + char *args = NULL; + jstring cmdexe = NULL; + char fn[32]; + struct stat stat_buf; + + /* + * Try to open /proc//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, truncated = 0; + int count; + char *s; + + /* + * The path name read by readlink() is limited to PATH_MAX characters. + * The content of /proc//cmdline is limited to PAGE_SIZE characters. + */ + cmdline = (char*)malloc((PATH_MAX > pageSize ? PATH_MAX : pageSize) + 1); + if (cmdline == NULL) { + break; + } + + /* + * On Linux, the full path to the executable command is the link in + * /proc//exe. But it is only readable for processes we own. + */ + snprintf(fn, sizeof fn, "/proc/%d/exe", pid); + if ((cmdlen = readlink(fn, cmdline, PATH_MAX)) > 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 command-line arguments appear as a set of strings separated by + * null bytes ('\0'), with a further null byte after the last + * string. The last string is only null terminated if the whole command + * line is not exceeding (PAGE_SIZE - 1) characters. + */ + cmdlen = 0; + s = cmdline; + while ((count = read(fd, s, pageSize - cmdlen)) > 0) { + cmdlen += count; + s += count; + } + if (count < 0) { + break; + } + // We have to null-terminate because the process may have changed argv[] + // or because the content in /proc//cmdline is truncated. + cmdline[cmdlen] = '\0'; + if (cmdlen == pageSize && cmdline[pageSize - 1] != '\0') { + truncated = 1; + } else if (cmdlen == 0) { + // /proc//cmdline was empty. This usually happens for kernel processes + // like '[kthreadd]'. We could try to read /proc//comm in the future. + } + if (cmdlen > 0 && (cmdexe == NULL || truncated)) { + // We have no exact command or the arguments are truncated. + // In this case we save the command line from /proc//cmdline. + args = (char*)malloc(pageSize + 1); + if (args != NULL) { + memcpy(args, cmdline, cmdlen + 1); + for (i = 0; i < cmdlen; i++) { + if (args[i] == '\0') { + args[i] = ' '; + } + } + } + } + i = 0; + if (!truncated) { + // Count the arguments + cmdEnd = &cmdline[cmdlen]; + for (s = cmdline; *s != '\0' && (s < cmdEnd); i++) { + s += strnlen(s, (cmdEnd - s)) + 1; + } + } + unix_fillArgArray(env, jinfo, i, cmdline, cmdEnd, cmdexe, args); + } while (0); + + if (cmdline != NULL) { + free(cmdline); + } + if (args != NULL) { + free(args); + } + 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 -r c8753d0be177 -r 24bb680a1609 jdk/src/java.base/macosx/native/libjava/ProcessHandleImpl_macosx.c --- a/jdk/src/java.base/macosx/native/libjava/ProcessHandleImpl_macosx.c Wed Jul 05 20:45:52 2017 +0200 +++ b/jdk/src/java.base/macosx/native/libjava/ProcessHandleImpl_macosx.c Fri Aug 14 10:35:45 2015 +0200 @@ -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. - */ - -/* - * Fill in the Info object from the OS information about the process. - * - * Class: java_lang_ProcessHandleImpl - * Method: info0 - * Signature: (J)I +/** + * Use sysctl and return the ppid, total cputime and start time. + * Return: -1 is fail; >= 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. */ -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,92 +189,70 @@ 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 = (jlong) (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; - - if (nargs < 1) { - return 0; - } - // Create a String array for nargs-1 elements - CHECK_NULL_RETURN((argsArray = (*env)->NewObjectArray(env, - nargs - 1, JNU_ClassString(env), NULL)), -1); +static uid_t getUID(pid_t pid) { + struct kinfo_proc kp; + size_t bufSize = sizeof kp; - 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 - } + // Read the process info for the specific pid + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}; - CHECK_NULL_RETURN((str = JNU_NewStringPlatform(env, cp)), -1); - - (*env)->SetObjectArrayElement(env, argsArray, i, str); - JNU_CHECK_EXCEPTION_RETURN(env, -3); + 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; + } } - (*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; size = sizeof(maxargs); if (sysctl(mib, 2, &maxargs, &size, NULL, 0) == -1) { JNU_ThrowByNameWithLastError(env, - "java/lang/RuntimeException", "sysctl failed"); + "java/lang/RuntimeException", "sysctl failed"); return; } @@ -437,7 +265,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; @@ -445,7 +273,7 @@ size = (size_t) maxargs; if (sysctl(mib, 3, args, &size, NULL, 0) == -1) { if (errno != EINVAL) { - JNU_ThrowByNameWithLastError(env, + JNU_ThrowByNameWithLastError(env, "java/lang/RuntimeException", "sysctl failed"); } break; @@ -456,11 +284,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 +295,7 @@ } } - fillArgArray(env, jinfo, nargs, cp, argsEnd); + unix_fillArgArray(env, jinfo, nargs, cp, argsEnd, cmdexe, NULL); } while (0); // Free the arg buffer free(args); diff -r c8753d0be177 -r 24bb680a1609 jdk/src/java.base/share/classes/java/lang/ProcessHandle.java --- a/jdk/src/java.base/share/classes/java/lang/ProcessHandle.java Wed Jul 05 20:45:52 2017 +0200 +++ b/jdk/src/java.base/share/classes/java/lang/ProcessHandle.java Fri Aug 14 10:35:45 2015 +0200 @@ -225,8 +225,34 @@ public Optional command(); /** + * Returns the command line of the process. + *

+ * If {@link #command command()} and {@link #arguments arguments()} return + * non-empty optionals, this is simply a convenience method which concatenates + * the values of the two functions separated by spaces. Otherwise it will return a + * best-effort, platform dependent representation of the command line. + * + * @apiNote Note that the returned executable pathname and the + * arguments may be truncated on some platforms due to system + * limitations. + *

+ * The executable pathname may contain only the + * name of the executable without the full path information. + * It is undecideable whether white space separates different + * arguments or is part of a single argument. + * + * @return an {@code Optional} of the command line + * of the process + */ + public Optional commandLine(); + + /** * Returns an array of Strings of the arguments of the process. * + * @apiNote On some platforms, native applications are free to change + * the arguments array after startup and this method may only + * show the changed values. + * * @return an {@code Optional} of the arguments of the process */ public Optional arguments(); diff -r c8753d0be177 -r 24bb680a1609 jdk/src/java.base/share/classes/java/lang/ProcessHandleImpl.java --- a/jdk/src/java.base/share/classes/java/lang/ProcessHandleImpl.java Wed Jul 05 20:45:52 2017 +0200 +++ b/jdk/src/java.base/share/classes/java/lang/ProcessHandleImpl.java Fri Aug 14 10:35:45 2015 +0200 @@ -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. @@ -496,6 +496,7 @@ private native void info0(long pid); String command; + String commandLine; String[] arguments; long startTime; long totalTime; @@ -503,6 +504,7 @@ Info() { command = null; + commandLine = null; arguments = null; startTime = -1L; totalTime = -1L; @@ -539,6 +541,15 @@ } @Override + public Optional commandLine() { + if (command != null && arguments != null) { + return Optional.of(command + " " + String.join(" ", arguments)); + } else { + return Optional.ofNullable(commandLine); + } + } + + @Override public Optional arguments() { return Optional.ofNullable(arguments); } @@ -580,6 +591,11 @@ sb.append("args: "); sb.append(Arrays.toString(arguments)); } + if (commandLine != null) { + if (sb.length() != 0) sb.append(", "); + sb.append("cmdLine: "); + sb.append(commandLine); + } if (startTime > 0) { if (sb.length() != 0) sb.append(", "); sb.append("startTime: "); diff -r c8753d0be177 -r 24bb680a1609 jdk/src/java.base/solaris/native/libjava/ProcessHandleImpl_solaris.c --- a/jdk/src/java.base/solaris/native/libjava/ProcessHandleImpl_solaris.c Wed Jul 05 20:45:52 2017 +0200 +++ b/jdk/src/java.base/solaris/native/libjava/ProcessHandleImpl_solaris.c Fri Aug 14 10:35:45 2015 +0200 @@ -24,368 +24,28 @@ */ #include "jni.h" -#include "jni_util.h" -#include "java_lang_ProcessHandleImpl.h" -#include "java_lang_ProcessHandleImpl_Info.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 - */ - -/* - * 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); - -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); -} +#include "ProcessHandleImpl_unix.h" -/* - * 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; -} - -/* - * 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; -} +#include /* - * 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. + * Implementation of native ProcessHandleImpl functions for Solaris. + * See ProcessHandleImpl_unix.c for more details. */ -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; - } +void os_initNative(JNIEnv *env, jclass clazz) {} - // 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; +jint os_getChildren(JNIEnv *env, jlong jpid, jlongArray jarray, + jlongArray jparentArray, jlongArray jstimesArray) { + return unix_getChildren(env, jpid, jarray, jparentArray, jstimesArray); } -/************************************************************** - * 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); - } +pid_t os_getParentPidAndTimings(JNIEnv *env, pid_t pid, jlong *total, jlong *start) { + return unix_getParentPidAndTimings(env, pid, total, start); } -/** - * 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; +void os_getCmdlineAndUserInfo(JNIEnv *env, jobject jinfo, pid_t pid) { + unix_getCmdlineAndUserInfo(env, jinfo, pid); } -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 -r c8753d0be177 -r 24bb680a1609 jdk/src/java.base/unix/native/libjava/ProcessHandleImpl_unix.c --- a/jdk/src/java.base/unix/native/libjava/ProcessHandleImpl_unix.c Wed Jul 05 20:45:52 2017 +0200 +++ b/jdk/src/java.base/unix/native/libjava/ProcessHandleImpl_unix.c Fri Aug 14 10:35:45 2015 +0200 @@ -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,83 @@ } 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 'commandLine' in java.lang.ProcessHandleImpl.Info */ +jfieldID ProcessHandleImpl_Info_commandLineID; + +/* 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; + +/* Size of password or group entry when not available via sysconf */ +#define ENT_BUF_SIZE 1024 +/* The value for the size of the buffer used by getpwuid_r(). The result of */ +/* sysconf(_SC_GETPW_R_SIZE_MAX) if available or ENT_BUF_SIZE otherwise. */ +static long getpw_buf_size; + +/************************************************************** + * 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_commandLineID = + (*env)->GetFieldID(env, clazz, "commandLine", "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 platform dependent constants. + * + * Class: java_lang_ProcessHandleImpl + * Method: initNative + * Signature: ()V + */ +JNIEXPORT void JNICALL +Java_java_lang_ProcessHandleImpl_initNative(JNIEnv *env, jclass clazz) { + getpw_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX); + if (getpw_buf_size == -1) { + getpw_buf_size = ENT_BUF_SIZE; + } + 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 +248,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 +276,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 +300,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 +319,52 @@ } } -/** - * Size of password or group entry when not available via sysconf - */ -#define ENT_BUF_SIZE 1024 - -/** - * Return a strong username for the uid_t or null. +/* + * 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 */ -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,8 +382,8 @@ pid_t pid = (pid_t) jpid; jlong startTime = 0L; jlong totalTime = 0L; - pid_t ppid = getStatInfo(env, pid, &totalTime, &startTime); - return (ppid <= 0) ? -1 : startTime; + pid_t ppid = os_getParentPidAndTimings(env, pid, &totalTime, &startTime); + return (ppid < 0) ? -1 : startTime; } /* @@ -350,7 +392,7 @@ * * Class: java_lang_ProcessHandleImpl * Method: parent0 - * Signature: (J)J + * Signature: (JJ)J */ JNIEXPORT jlong JNICALL Java_java_lang_ProcessHandleImpl_parent0(JNIEnv *env, @@ -360,13 +402,12 @@ pid_t pid = (pid_t) jpid; pid_t ppid; - pid_t mypid = getpid(); - if (pid == mypid) { + if (pid == getpid()) { 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 +415,94 @@ return (jlong) ppid; } +/** + * Construct the argument array by parsing the arguments from the sequence + * of arguments. + */ +void unix_fillArgArray(JNIEnv *env, jobject jinfo, int nargs, char *cp, + char *argsEnd, jstring cmdexe, char *cmdline) { + jobject argsArray; + int i; + + (*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); + } + if (cmdline != NULL) { + jstring commandLine = NULL; + CHECK_NULL((commandLine = JNU_NewStringPlatform(env, cmdline))); + (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_commandLineID, commandLine); + JNU_CHECK_EXCEPTION(env); + } +} + +void unix_getUserInfo(JNIEnv* env, jobject jinfo, uid_t uid) { + int result = 0; + char* pwbuf; + jstring name = NULL; + + /* allocate buffer for password record */ + pwbuf = (char*)malloc(getpw_buf_size); + 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)getpw_buf_size), p); +#else + RESTARTABLE(getpwuid_r(uid, &pwent, pwbuf, (size_t)getpw_buf_size, &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 functions 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,9 +573,10 @@ if ((int) childpid <= 0) { continue; } - // Read /proc/pid/stat and get the parent pid, and start time - ppid = getStatInfo(env, childpid, &totalTime, &startTime); - if (ppid > 0 && (pid == 0 || ppid == pid)) { + + // 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 pids[count] = (jlong) childpid; @@ -498,293 +610,110 @@ return count; } - -/************************************************************** - * Implementation of ProcessHandleImpl_Info native methods. - */ +#endif // defined(__solaris__) || defined (__linux__) || defined(_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 + * The following functions are common on Solaris and AIX. */ -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); - } -} - -/** - * Read /proc//stat and return the ppid, total cputime and start time. - * -1 is fail; zero is unknown; > 0 is parent pid - */ -static pid_t getStatInfo(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, STAT_FILE, 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; -} +#if defined(__solaris__) || defined(_AIX) /** - * Construct the argument array by parsing the arguments from the sequence - * of arguments. The zero'th arg is the command executable + * Helper function to get the 'psinfo_t' data from "/proc/%d/psinfo". + * Returns 0 on success and -1 on error. */ -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; +static int getPsinfo(pid_t pid, psinfo_t *psinfo) { + FILE* fp; char fn[32]; - struct stat stat_buf; + int ret; /* - * Try to open /proc/%d/cmdline + * Try to open /proc/%d/psinfo */ - 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"); + snprintf(fn, sizeof fn, "/proc/%d/psinfo", pid); + fp = fopen(fn, "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; >= 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]; + char prargs[PRARGSZ + 1]; + jstring cmdexe = NULL; + int ret; -/* 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; - } + /* + * 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 + + /* + * Now try to open /proc/%d/psinfo + */ + if (getPsinfo(pid, &psinfo) < 0) { + unix_fillArgArray(env, jinfo, 0, NULL, NULL, cmdexe, NULL); + return; } - if (WIFEXITED(status)) { - /* - * The child exited normally; get its exit code. + 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 :) + */ + strncpy(prargs, psinfo.pr_psargs, PRARGSZ); + prargs[PRARGSZ] = '\0'; + if (prargs[0] == '\0') { + /* 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. */ - 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; + strncpy(prargs, psinfo.pr_fname, PRARGSZ); + prargs[PRARGSZ] = '\0'; } + unix_fillArgArray(env, jinfo, 0, NULL, NULL, cmdexe, + prargs[0] == '\0' ? NULL : prargs); } - +#endif // defined(__solaris__) || defined(_AIX) diff -r c8753d0be177 -r 24bb680a1609 jdk/src/java.base/unix/native/libjava/ProcessHandleImpl_unix.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/unix/native/libjava/ProcessHandleImpl_unix.h Fri Aug 14 10:35:45 2015 +0200 @@ -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 'commandLine' in java.lang.ProcessHandleImpl.Info */ +extern jfieldID ProcessHandleImpl_Info_commandLineID; + +/* 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; + +/** + * Return: -1 is fail; >= 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. + */ +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, char *cmdline); + +extern void os_initNative(JNIEnv *env, jclass clazz); diff -r c8753d0be177 -r 24bb680a1609 jdk/src/java.base/windows/native/libjava/ProcessHandleImpl_win.c --- a/jdk/src/java.base/windows/native/libjava/ProcessHandleImpl_win.c Wed Jul 05 20:45:52 2017 +0200 +++ b/jdk/src/java.base/windows/native/libjava/ProcessHandleImpl_win.c Fri Aug 14 10:35:45 2015 +0200 @@ -45,6 +45,9 @@ /* Field id for jString 'command' in java.lang.ProcessHandle.Info */ static jfieldID ProcessHandleImpl_Info_commandID; +/* Field id for jString 'commandLine' in java.lang.ProcessHandleImpl.Info */ +static jfieldID ProcessHandleImpl_Info_commandLineID; + /* Field id for jString[] 'arguments' in java.lang.ProcessHandle.Info */ static jfieldID ProcessHandleImpl_Info_argumentsID; @@ -69,6 +72,8 @@ CHECK_NULL(ProcessHandleImpl_Info_commandID = (*env)->GetFieldID(env, clazz, "command", "Ljava/lang/String;")); + CHECK_NULL(ProcessHandleImpl_Info_commandLineID = (*env)->GetFieldID(env, + clazz, "commandLine", "Ljava/lang/String;")); CHECK_NULL(ProcessHandleImpl_Info_argumentsID = (*env)->GetFieldID(env, clazz, "arguments", "[Ljava/lang/String;")); CHECK_NULL(ProcessHandleImpl_Info_totalTimeID = (*env)->GetFieldID(env, diff -r c8753d0be177 -r 24bb680a1609 jdk/test/java/lang/ProcessHandle/InfoTest.java --- a/jdk/test/java/lang/ProcessHandle/InfoTest.java Wed Jul 05 20:45:52 2017 +0200 +++ b/jdk/test/java/lang/ProcessHandle/InfoTest.java Fri Aug 14 10:35:45 2015 +0200 @@ -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); @@ -160,19 +170,10 @@ if (info.arguments().isPresent()) { String[] args = info.arguments().get(); - if (Platform.isLinux() || Platform.isOSX()) { - int offset = args.length - extraArgs.length; - for (int i = 0; i < extraArgs.length; i++) { - 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 { - System.out.printf("No argument test for OS: %s%n", Platform.getOsName()); + int offset = args.length - extraArgs.length; + for (int i = 0; i < extraArgs.length; i++) { + Assert.assertEquals(args[offset + i], extraArgs[i], + "Actual argument mismatch, index: " + i); } // Now check that the first argument is not the same as the executed command @@ -183,6 +184,46 @@ } } + if (command.isPresent() && info.arguments().isPresent()) { + // If both, 'command' and 'arguments' are present, + // 'commandLine' is just the concatenation of the two. + Assert.assertTrue(info.commandLine().isPresent(), + "commandLine() must be available"); + + String javaExe = System.getProperty("test.jdk") + + File.separator + "bin" + File.separator + "java"; + String expected = Platform.isWindows() ? javaExe + ".exe" : javaExe; + Path expectedPath = Paths.get(expected); + String commandLine = info.commandLine().get(); + String commandLineCmd = commandLine.split(" ")[0]; + Path commandLineCmdPath = Paths.get(commandLineCmd); + Assert.assertTrue(Files.isSameFile(commandLineCmdPath, expectedPath), + "commandLine() should start with: " + expectedPath + + " but starts with " + commandLineCmdPath); + + List allArgs = p1.getArgs(); + for (int i = 0; i < allArgs.size(); i++) { + Assert.assertTrue(commandLine.contains(allArgs.get(i)), + "commandLine() must contain argument: " + allArgs.get(i)); + } + } else if (info.commandLine().isPresent()) { + // If we only have the commandLine() we can only do some basic checks... + String commandLine = info.commandLine().get(); + String javaExe = "java" + (Platform.isWindows() ? ".exe": ""); + int pos = commandLine.indexOf(javaExe); + Assert.assertTrue(pos > 0, "commandLine() should at least contain 'java'"); + + pos += javaExe.length() + 1; // +1 for the space after the command + List allArgs = p1.getArgs(); + // First argument is the command - skip it here as we've already checked that. + for (int i = 1; (i < allArgs.size()) && + (pos + allArgs.get(i).length() < commandLine.length()); i++) { + Assert.assertTrue(commandLine.contains(allArgs.get(i)), + "commandLine() must contain argument: " + allArgs.get(i)); + pos += allArgs.get(i).length() + 1; + } + } + if (info.totalCpuDuration().isPresent()) { Duration totalCPU = info.totalCpuDuration().get(); Duration epsilon = Duration.ofMillis(200L); @@ -269,10 +310,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 -r c8753d0be177 -r 24bb680a1609 jdk/test/java/lang/ProcessHandle/JavaChild.java --- a/jdk/test/java/lang/ProcessHandle/JavaChild.java Wed Jul 05 20:45:52 2017 +0200 +++ b/jdk/test/java/lang/ProcessHandle/JavaChild.java Fri Aug 14 10:35:45 2015 +0200 @@ -68,8 +68,9 @@ * {@link #forEachOutputLine} can be used to process output from the child * @param delegate the process to delegate and send commands to and get responses from */ - private JavaChild(Process delegate) { - this.delegate = delegate; + private JavaChild(ProcessBuilder pb) throws IOException { + allArgs = pb.command(); + delegate = pb.start(); // Initialize PrintWriter with autoflush (on println) inputWriter = new PrintWriter(delegate.getOutputStream(), true); outputReader = new BufferedReader(new InputStreamReader(delegate.getInputStream())); @@ -119,6 +120,10 @@ return "delegate: " + delegate.toString(); } + public List getArgs() { + return allArgs; + } + public CompletableFuture onJavaChildExit() { return onExit().thenApply(ph -> this); } @@ -187,7 +192,7 @@ } ProcessBuilder pb = build(stringArgs); pb.redirectError(ProcessBuilder.Redirect.INHERIT); - return new JavaChild(pb.start()); + return new JavaChild(pb); } /** @@ -236,6 +241,9 @@ "-classpath", absolutifyPath(classpath), "JavaChild"); + // Will hold the complete list of arguments which was given to Processbuilder.command() + private List allArgs; + private static String absolutifyPath(String path) { StringBuilder sb = new StringBuilder(); for (String file : path.split(File.pathSeparator)) { diff -r c8753d0be177 -r 24bb680a1609 jdk/test/java/lang/ProcessHandle/OnExitTest.java --- a/jdk/test/java/lang/ProcessHandle/OnExitTest.java Wed Jul 05 20:45:52 2017 +0200 +++ b/jdk/test/java/lang/ProcessHandle/OnExitTest.java Fri Aug 14 10:35:45 2015 +0200 @@ -126,6 +126,11 @@ } 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"); + } children = getAllChildren(procHandle); ConcurrentHashMap> completions =