1 /* 2 * Copyright (c) 2013, 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 24 package jdk.testlibrary; 25 26 import java.io.ByteArrayOutputStream; 27 import java.io.IOException; 28 import java.io.PrintStream; 29 import java.lang.management.ManagementFactory; 30 import java.lang.management.RuntimeMXBean; 31 import java.lang.reflect.Field; 32 import java.lang.reflect.Method; 33 import java.util.ArrayList; 34 import java.util.Collections; 35 import java.util.Map; 36 import java.util.concurrent.ExecutionException; 37 import java.util.concurrent.Future; 38 import java.util.concurrent.Phaser; 39 import java.util.concurrent.TimeUnit; 40 import java.util.concurrent.TimeoutException; 41 42 import sun.management.VMManagement; 43 44 public final class ProcessTools { 45 private static final class LineForwarder extends StreamPumper.LinePump { 46 private final PrintStream ps; 47 private final String prefix; 48 LineForwarder(String prefix, PrintStream os) { 49 this.ps = os; 50 this.prefix = prefix; 51 } 52 @Override 53 protected void processLine(String line) { 54 ps.println("[" + prefix + "] " + line); 55 } 56 } 57 58 private ProcessTools() { 59 } 60 61 /** 62 * <p>Starts a process from its builder.</p> 63 * <span>The default redirects of STDOUT and STDERR are started</span> 64 * @param name The process name 65 * @param processBuilder The process builder 66 * @return Returns the initialized process 67 * @throws IOException 68 */ 69 public static Process startProcess(String name, 70 ProcessBuilder processBuilder) 71 throws IOException { 72 Process p = null; 73 try { 74 p = startProcess(name, processBuilder, -1, TimeUnit.NANOSECONDS); 75 } catch (InterruptedException | TimeoutException e) { 76 // can't ever happen 77 } 78 return p; 79 } 80 81 /** 82 * <p>Starts a process from its builder.</p> 83 * <span>The default redirects of STDOUT and STDERR are started</span> 84 * @param name The process name 85 * @param processBuilder The process builder 86 * @param timeout The timeout for the warmup waiting 87 * @param unit The timeout {@linkplain TimeUnit} 88 * @return Returns the initialized {@linkplain Process} 89 * @throws IOException 90 * @throws InterruptedException 91 * @throws TimeoutException 92 */ 93 public static Process startProcess(String name, 94 ProcessBuilder processBuilder, 95 long timeout, 96 TimeUnit unit) 97 throws IOException, InterruptedException, TimeoutException { 98 Process p = processBuilder.start(); 99 StreamPumper stdout = new StreamPumper(p.getInputStream()); 100 StreamPumper stderr = new StreamPumper(p.getErrorStream()); 101 102 stdout.addPump(new LineForwarder(name, System.out)); 103 stderr.addPump(new LineForwarder(name, System.err)); 104 final Phaser phs = new Phaser(1); 105 Future<Void> stdoutTask = stdout.process(); 106 Future<Void> stderrTask = stderr.process(); 107 108 try { 109 if (timeout > -1) { 110 phs.awaitAdvanceInterruptibly(0, timeout, unit); 111 } 112 } catch (TimeoutException | InterruptedException e) { 113 System.err.println("Failed to start a process (thread dump follows)"); 114 for(Map.Entry<Thread, StackTraceElement[]> s : Thread.getAllStackTraces().entrySet()) { 115 printStack(s.getKey(), s.getValue()); 116 } 117 stdoutTask.cancel(true); 118 stderrTask.cancel(true); 119 throw e; 120 } 121 122 return p; 123 } 124 125 /** 126 * Pumps stdout and stderr from running the process into a String. 127 * 128 * @param processBuilder 129 * ProcessHandler to run. 130 * @return Output from process. 131 * @throws IOException 132 * If an I/O error occurs. 133 */ 134 public static OutputBuffer getOutput(ProcessBuilder processBuilder) 135 throws IOException { 136 return getOutput(processBuilder.start()); 137 } 138 139 /** 140 * Pumps stdout and stderr the running process into a String. 141 * 142 * @param process 143 * Process to pump. 144 * @return Output from process. 145 * @throws IOException 146 * If an I/O error occurs. 147 */ 148 public static OutputBuffer getOutput(Process process) throws IOException { 149 ByteArrayOutputStream stderrBuffer = new ByteArrayOutputStream(); 150 ByteArrayOutputStream stdoutBuffer = new ByteArrayOutputStream(); 151 StreamPumper outPumper = new StreamPumper(process.getInputStream(), 152 stdoutBuffer); 153 StreamPumper errPumper = new StreamPumper(process.getErrorStream(), 154 stderrBuffer); 155 156 Future<Void> outTask = outPumper.process(); 157 Future<Void> errTask = errPumper.process(); 158 159 try { 160 process.waitFor(); 161 outTask.get(); 162 errTask.get(); 163 } catch (InterruptedException e) { 164 Thread.currentThread().interrupt(); 165 return null; 166 } catch (ExecutionException e) { 167 throw new IOException(e); 168 } 169 170 return new OutputBuffer(stdoutBuffer.toString(), 171 stderrBuffer.toString()); 172 } 173 174 /** 175 * Get the process id of the current running Java process 176 * 177 * @return Process id 178 */ 179 public static int getProcessId() throws Exception { 180 181 // Get the current process id using a reflection hack 182 RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean(); 183 Field jvm = runtime.getClass().getDeclaredField("jvm"); 184 185 jvm.setAccessible(true); 186 VMManagement mgmt = (sun.management.VMManagement) jvm.get(runtime); 187 188 Method pid_method = mgmt.getClass().getDeclaredMethod("getProcessId"); 189 190 pid_method.setAccessible(true); 191 192 int pid = (Integer) pid_method.invoke(mgmt); 193 194 return pid; 195 } 196 197 /** 198 * Get platform specific VM arguments (e.g. -d64 on 64bit Solaris) 199 * 200 * @return String[] with platform specific arguments, empty if there are 201 * none 202 */ 203 public static String[] getPlatformSpecificVMArgs() { 204 String osName = System.getProperty("os.name"); 205 String dataModel = System.getProperty("sun.arch.data.model"); 206 207 if (osName.equals("SunOS") && dataModel.equals("64")) { 208 return new String[] { "-d64" }; 209 } 210 211 return new String[] {}; 212 } 213 214 /** 215 * Create ProcessBuilder using the java launcher from the jdk to be tested 216 * and with any platform specific arguments prepended 217 */ 218 public static ProcessBuilder createJavaProcessBuilder(String... command) 219 throws Exception { 220 String javapath = JDKToolFinder.getJDKTool("java"); 221 222 ArrayList<String> args = new ArrayList<>(); 223 args.add(javapath); 224 Collections.addAll(args, getPlatformSpecificVMArgs()); 225 Collections.addAll(args, command); 226 227 // Reporting 228 StringBuilder cmdLine = new StringBuilder(); 229 for (String cmd : args) 230 cmdLine.append(cmd).append(' '); 231 System.out.println("Command line: [" + cmdLine.toString() + "]"); 232 233 return new ProcessBuilder(args.toArray(new String[args.size()])); 234 } 235 236 private static void printStack(Thread t, StackTraceElement[] stack) { 237 System.out.println("\t" + t + 238 " stack: (length = " + stack.length + ")"); 239 if (t != null) { 240 for (StackTraceElement stack1 : stack) { 241 System.out.println("\t" + stack1); 242 } 243 System.out.println(); 244 } 245 } 246 247 /** 248 * Executes a test jvm process, waits for it to finish and returns the process output. 249 * The default jvm options from jtreg, test.vm.opts and test.java.opts, are added. 250 * The java from the test.jdk is used to execute the command. 251 * 252 * The command line will be like: 253 * {test.jdk}/bin/java {test.vm.opts} {test.java.opts} cmds 254 * 255 * @param cmds User specifed arguments. 256 * @return The output from the process. 257 */ 258 public static OutputAnalyzer executeTestJvm(String... cmds) throws Throwable { 259 ProcessBuilder pb = createJavaProcessBuilder(Utils.addTestJavaOpts(cmds)); 260 return executeProcess(pb); 261 } 262 263 /** 264 * Executes a process, waits for it to finish and returns the process output. 265 * @param pb The ProcessBuilder to execute. 266 * @return The output from the process. 267 */ 268 public static OutputAnalyzer executeProcess(ProcessBuilder pb) throws Throwable { 269 OutputAnalyzer output = null; 270 try { 271 output = new OutputAnalyzer(pb.start()); 272 return output; 273 } catch (Throwable t) { 274 System.out.println("executeProcess() failed: " + t); 275 throw t; 276 } finally { 277 System.out.println(getProcessLog(pb, output)); 278 } 279 } 280 281 /** 282 * Executes a process, waits for it to finish and returns the process output. 283 * @param cmds The command line to execute. 284 * @return The output from the process. 285 */ 286 public static OutputAnalyzer executeProcess(String... cmds) throws Throwable { 287 return executeProcess(new ProcessBuilder(cmds)); 288 } 289 290 /** 291 * Used to log command line, stdout, stderr and exit code from an executed process. 292 * @param pb The executed process. 293 * @param output The output from the process. 294 */ 295 public static String getProcessLog(ProcessBuilder pb, OutputAnalyzer output) { 296 String stderr = output == null ? "null" : output.getStderr(); 297 String stdout = output == null ? "null" : output.getStdout(); 298 String exitValue = output == null ? "null": Integer.toString(output.getExitValue()); 299 StringBuilder logMsg = new StringBuilder(); 300 final String nl = System.getProperty("line.separator"); 301 logMsg.append("--- ProcessLog ---" + nl); 302 logMsg.append("cmd: " + getCommandLine(pb) + nl); 303 logMsg.append("exitvalue: " + exitValue + nl); 304 logMsg.append("stderr: " + stderr + nl); 305 logMsg.append("stdout: " + stdout + nl); 306 return logMsg.toString(); 307 } 308 309 /** 310 * @return The full command line for the ProcessBuilder. 311 */ 312 public static String getCommandLine(ProcessBuilder pb) { 313 if (pb == null) { 314 return "null"; 315 } 316 StringBuilder cmd = new StringBuilder(); 317 for (String s : pb.command()) { 318 cmd.append(s).append(" "); 319 } 320 return cmd.toString().trim(); 321 } 322 }