8131168: Refactor ProcessHandleImpl_*.c and add implememtation for AIX
authorsimonis
Fri, 14 Aug 2015 10:35:45 +0200
changeset 32209 24bb680a1609
parent 32167 c8753d0be177
child 32210 958d823579c3
8131168: Refactor ProcessHandleImpl_*.c and add implememtation for AIX Reviewed-by: rriggs, smarks
jdk/src/java.base/aix/native/libjava/ProcessHandleImpl_aix.c
jdk/src/java.base/linux/native/libjava/ProcessHandleImpl_linux.c
jdk/src/java.base/macosx/native/libjava/ProcessHandleImpl_macosx.c
jdk/src/java.base/share/classes/java/lang/ProcessHandle.java
jdk/src/java.base/share/classes/java/lang/ProcessHandleImpl.java
jdk/src/java.base/solaris/native/libjava/ProcessHandleImpl_solaris.c
jdk/src/java.base/unix/native/libjava/ProcessHandleImpl_unix.c
jdk/src/java.base/unix/native/libjava/ProcessHandleImpl_unix.h
jdk/src/java.base/windows/native/libjava/ProcessHandleImpl_win.c
jdk/test/java/lang/ProcessHandle/InfoTest.java
jdk/test/java/lang/ProcessHandle/JavaChild.java
jdk/test/java/lang/ProcessHandle/OnExitTest.java
--- /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 <sys/procfs.h>
+
+/*
+ * 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);
+}
--- /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 <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <string.h>
+#include <ctype.h>
+
+/*
+ * 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/<pid>/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/<pid>/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/<pid>/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/<pid>/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/<pid>/cmdline is truncated.
+        cmdline[cmdlen] = '\0';
+        if (cmdlen == pageSize && cmdline[pageSize - 1] != '\0') {
+            truncated = 1;
+        } else if (cmdlen == 0) {
+            // /proc/<pid>/cmdline was empty. This usually happens for kernel processes
+            // like '[kthreadd]'. We could try to read /proc/<pid>/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/<pid>/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;
+}
--- 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 <stdio.h>
 #include <errno.h>
 #include <signal.h>
@@ -38,144 +40,15 @@
 #include <sys/sysctl.h>
 
 /**
- * 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/<pid>/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);
--- 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<String> command();
 
         /**
+         * Returns the command line of the process.
+         * <p>
+         * 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.
+         *          <p>
+         *          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<String>} of the command line
+         *         of the process
+         */
+        public Optional<String> 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<String[]>} of the arguments of the process
          */
         public Optional<String[]> arguments();
--- 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<String> commandLine() {
+            if (command != null && arguments != null) {
+                return Optional.of(command + " " + String.join(" ", arguments));
+            } else {
+                return Optional.ofNullable(commandLine);
+            }
+        }
+
+        @Override
         public Optional<String[]> 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: ");
--- 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 <stdio.h>
-#include <ctype.h>
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <procfs.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <limits.h>
-
-/**
- * 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 <procfs.h>
 
 /*
- * 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/<pid>/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/<pid>/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);
-}
-
--- 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 <stdio.h>
-
 #include <errno.h>
 #include <fcntl.h>
 #include <pwd.h>
 #include <signal.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <string.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <limits.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
 
-#include <string.h>
-#include <dirent.h>
-#include <ctype.h>
+#ifdef _AIX
+#include <sys/procfs.h>
+#endif
+#ifdef __solaris__
+#include <procfs.h>
+#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_<os>.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_<function_name> functions in
+ *   ProcessHandleImpl_<os>.c
+ * - if at least two platforms implement an os_<function_name> function in the
+ *   same way, this implementation is factored out into unix_<function_name>,
+ *   placed into this file and called from the corresponding os_<function_name>
+ *   function.
+ * - For convenience, all the os_ and unix_ functions are declared in
+ *   ProcessHandleImpl_unix.h which is included into every
+ *   ProcessHandleImpl_<os>.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/<pid>/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/<pid>/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/<pid>/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/<pid>/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)
--- /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 <sys/types.h>
+
+/*
+ * 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);
--- 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,
--- 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/<pid>/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/<pid>/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<String> 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<String> 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/<pid>/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<Duration> dur1 = ProcessHandle.current().info().totalCpuDuration();
 
         Duration myCputime2 = ProcessUtil.MXBeanCpuTime();
 
+        if (Platform.isAix()) {
+            try {
+                Thread.sleep(1000);
+            } catch (InterruptedException ex) {}
+        }
         Optional<Duration> dur2 = ProcessHandle.current().info().totalCpuDuration();
 
         if (dur1.isPresent() && dur2.isPresent()) {
--- 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<String> getArgs() {
+        return allArgs;
+    }
+
     public CompletableFuture<JavaChild> 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<String> allArgs;
+
     private static String absolutifyPath(String path) {
         StringBuilder sb = new StringBuilder();
         for (String file : path.split(File.pathSeparator)) {
--- 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<ProcessHandle, CompletableFuture<ProcessHandle>> completions =