1/*
2 * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24import java.io.IOException;
25import java.lang.management.ManagementFactory;
26import java.time.Duration;
27import java.util.List;
28import java.util.stream.Collectors;
29import java.util.stream.Stream;
30
31import com.sun.management.OperatingSystemMXBean;
32import jdk.test.lib.Platform;
33
34/**
35 * Useful utilities for testing Process and ProcessHandle.
36 */
37public abstract class ProcessUtil {
38    /**
39     * Constructor
40     */
41    public ProcessUtil() {}
42
43    /**
44     * Returns the list of direct children.
45     * WIndows conhost.exe children are filtered out.
46     * @param ph the Process to get children of
47     * @return a list of child ProcessHandles
48     */
49    public static List<ProcessHandle> getChildren(ProcessHandle ph) {
50        return ph.children()
51                .filter(ProcessUtil::isNotWindowsConsole)
52                .collect(Collectors.toList());
53    }
54
55    /**
56     * Returns the list of all direct and indirect children.
57     * WIndows conhost.exe children are filtered out.
58     * @param ph the Process to get children of
59     * @return a list of child ProcessHandles
60     */
61    public static List<ProcessHandle> getDescendants(ProcessHandle ph) {
62        return ph.descendants()
63                .filter(ProcessUtil::isNotWindowsConsole)
64                .collect(Collectors.toList());
65    }
66
67    /**
68     * Waits for and returns the direct expected Children of a ProcessHandle.
69     * For Windows, the conhost.exe children are filtered out.
70     *
71     * @param ph the process to get the children of
72     * @param nchildren the minimum number of children to expect
73     * @return a list of ProcessHandles of the children.
74     */
75    public static List<ProcessHandle> waitForChildren(ProcessHandle ph, long nchildren) {
76        List<ProcessHandle> subprocesses = null;
77        long count = 0;
78        do {
79            if (subprocesses != null) {
80                // Only wait if this is not the first time looking
81                try {
82                    Thread.sleep(500L);     // It will happen but don't burn the cpu
83                } catch (InterruptedException ie) {
84                    // ignore
85                }
86            }
87            subprocesses = getChildren(ph);
88            count = subprocesses.size();
89            System.out.printf(" waiting for subprocesses of %s to start," +
90                    " expected: %d, current: %d%n", ph, nchildren, count);
91        } while (count < nchildren);
92        return subprocesses;
93    }
94
95    /**
96     * Waits for and returns all expected Children of a ProcessHandle.
97     * For Windows, the conhost.exe children are filtered out.
98     *
99     * @param ph the process to get the children of
100     * @param nchildren the minimum number of children to expect
101     * @return a list of ProcessHandles of the children.
102     */
103    public static List<ProcessHandle> waitForAllChildren(ProcessHandle ph, long nchildren) {
104        List<ProcessHandle> subprocesses = null;
105        long count = 0;
106        do {
107            if (subprocesses != null) {
108                // Only wait if this is not the first time looking
109                try {
110                    Thread.sleep(500L);     // It will happen but don't burn the cpu
111                } catch (InterruptedException ie) {
112                    // ignore
113                }
114            }
115            subprocesses = getDescendants(ph);
116            count = subprocesses.size();
117            System.out.printf(" waiting for subprocesses of %s to start," +
118                    " expected: %d, current: %d%n", ph, nchildren, count);
119        } while (count < nchildren);
120        return subprocesses;
121    }
122
123    /**
124     * Destroy all children of the ProcessHandle.
125     * (Except the conhost.exe on Windows)
126     *
127     * @param p a ProcessHandle
128     * @return the ProcessHandle
129     */
130    public static ProcessHandle destroyProcessTree(ProcessHandle p) {
131        Stream<ProcessHandle> children = p.descendants().filter(ProcessUtil::isNotWindowsConsole);
132        children.forEach(ph -> {
133            System.out.printf("destroyProcessTree destroyForcibly%n");
134            printProcess(ph);
135            ph.destroyForcibly();
136        });
137        return p;
138    }
139
140    /**
141     * The OSMXBean for this process.
142     */
143    public static final OperatingSystemMXBean osMbean =
144            (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
145
146    /**
147     * Return the CPU time of the current process according to the OperatingSystemMXBean.
148     *
149     * @return the CPU time of the current process
150     */
151    public static Duration MXBeanCpuTime() {
152        return Duration.ofNanos(osMbean.getProcessCpuTime());
153    }
154
155    /**
156     * Return true if the ProcessHandle is a Windows i586 conhost.exe process.
157     *
158     * @param p the processHandle of the Process
159     * @return Return true if the ProcessHandle is for a Windows i586 conhost.exe process
160     */
161    static boolean isWindowsConsole(ProcessHandle p) {
162        return Platform.isWindows() && p.info().command().orElse("").endsWith("C:\\Windows\\System32\\conhost.exe");
163    }
164
165    /**
166     * Return true if the ProcessHandle is NOT  a Windows i586 conhost.exe process.
167     *
168     * @param p the processHandle of the Process
169     * @return Return true if the ProcessHandle is NOT for a Windows i586 conhost.exe process
170     */
171    static boolean isNotWindowsConsole(ProcessHandle p) {
172        return !isWindowsConsole(p);
173    }
174
175    /**
176     * Print a formatted string to System.out.
177     * @param format the format
178     * @param args the argument array
179     */
180    static void printf(String format, Object... args) {
181        String s = String.format(format, args);
182        System.out.print(s);
183    }
184
185    /**
186     * Print information about a process.
187     * Prints the pid, if it is alive, and information about the process.
188     * @param ph the processHandle at the top
189     */
190    static void printProcess(ProcessHandle ph) {
191        printProcess(ph, "");
192    }
193
194    /**
195     * Print information about a process.
196     * Prints the pid, if it is alive, and information about the process.
197     * @param ph the processHandle at the top
198     * @param prefix the String to prefix the output with
199     */
200    static void printProcess(ProcessHandle ph, String prefix) {
201        printf("%spid %s, alive: %s; parent: %s, %s%n", prefix,
202                ph.pid(), ph.isAlive(), ph.parent(), ph.info());
203    }
204
205    /**
206     * Print the process hierarchy as visible via ProcessHandle.
207     * Prints the pid, if it is alive, and information about the process.
208     * @param ph the processHandle at the top
209     * @param prefix the String to prefix the output with
210     */
211    static void printDeep(ProcessHandle ph, String prefix) {
212        printProcess(ph, prefix);
213        ph.children().forEach(p -> printDeep(p, prefix + "   "));
214    }
215
216    /**
217     * Use the native command to list the active processes.
218     */
219    static void logTaskList() {
220        String[] windowsArglist = {"tasklist.exe", "/v"};
221        String[] unixArglist = {"ps", "-ef"};
222
223        String[] argList = null;
224        if (Platform.isWindows()) {
225            argList = windowsArglist;
226        } else if (Platform.isLinux() || Platform.isOSX()) {
227            argList = unixArglist;
228        } else {
229            return;
230        }
231
232        ProcessBuilder pb = new ProcessBuilder(argList);
233        pb.inheritIO();
234        try {
235            Process proc = pb.start();
236            proc.waitFor();
237        } catch (IOException | InterruptedException ex) {
238            ex.printStackTrace();
239        }
240    }
241}
242