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 com.oracle.java.testlibrary; 25 26 import java.io.ByteArrayOutputStream; 27 import java.io.IOException; 28 import java.lang.management.ManagementFactory; 29 import java.lang.management.RuntimeMXBean; 30 import java.lang.reflect.Field; 31 import java.lang.reflect.Method; 32 import java.util.ArrayList; 33 import java.util.Collections; 34 import java.util.List; 35 36 import sun.management.VMManagement; 37 38 public final class ProcessTools { 39 40 private ProcessTools() { 41 } 42 43 /** 44 * Pumps stdout and stderr from running the process into a String. 45 * 46 * @param processHandler ProcessHandler to run. 47 * @return Output from process. 48 * @throws IOException If an I/O error occurs. 49 */ 50 public static OutputBuffer getOutput(ProcessBuilder processBuilder) throws IOException { 51 return getOutput(processBuilder.start()); 52 } 53 54 /** 55 * Pumps stdout and stderr the running process into a String. 56 * 57 * @param process Process to pump. 58 * @return Output from process. 59 * @throws IOException If an I/O error occurs. 60 */ 61 public static OutputBuffer getOutput(Process process) throws IOException { 62 ByteArrayOutputStream stderrBuffer = new ByteArrayOutputStream(); 63 ByteArrayOutputStream stdoutBuffer = new ByteArrayOutputStream(); 64 StreamPumper outPumper = new StreamPumper(process.getInputStream(), stdoutBuffer); 65 StreamPumper errPumper = new StreamPumper(process.getErrorStream(), stderrBuffer); 66 Thread outPumperThread = new Thread(outPumper); 67 Thread errPumperThread = new Thread(errPumper); 68 69 outPumperThread.setDaemon(true); 70 errPumperThread.setDaemon(true); 71 72 outPumperThread.start(); 73 errPumperThread.start(); 74 75 try { 76 process.waitFor(); 77 outPumperThread.join(); 78 errPumperThread.join(); 79 } catch (InterruptedException e) { 80 Thread.currentThread().interrupt(); 81 return null; 82 } 83 84 return new OutputBuffer(stdoutBuffer.toString(), stderrBuffer.toString()); 85 } 86 87 /** 88 * Get the process id of the current running Java process 89 * 90 * @return Process id 91 */ 92 public static int getProcessId() throws Exception { 93 94 // Get the current process id using a reflection hack 95 RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean(); 96 Field jvm = runtime.getClass().getDeclaredField("jvm"); 97 98 jvm.setAccessible(true); 99 VMManagement mgmt = (sun.management.VMManagement) jvm.get(runtime); 100 101 Method pid_method = mgmt.getClass().getDeclaredMethod("getProcessId"); 102 103 pid_method.setAccessible(true); 104 105 int pid = (Integer) pid_method.invoke(mgmt); 106 107 return pid; 108 } 109 110 /** 111 * Get the string containing input arguments passed to the VM 112 * 113 * @return arguments 114 */ 115 public static String getVmInputArguments() { 116 RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean(); 117 118 List<String> args = runtime.getInputArguments(); 119 StringBuilder result = new StringBuilder(); 120 for (String arg : args) 121 result.append(arg).append(' '); 122 123 return result.toString(); 124 } 125 126 /** 127 * Get platform specific VM arguments (e.g. -d64 on 64bit Solaris) 128 * 129 * @return String[] with platform specific arguments, empty if there are none 130 */ 131 public static String[] getPlatformSpecificVMArgs() { 132 133 if (Platform.is64bit() && Platform.isSolaris()) { 134 return new String[] { "-d64" }; 135 } 136 137 return new String[] {}; 138 } 139 140 /** 141 * Create ProcessBuilder using the java launcher from the jdk to be tested and 142 * with any platform specific arguments prepended 143 */ 144 public static ProcessBuilder createJavaProcessBuilder(String... command) throws Exception { 145 return createJavaProcessBuilder(false, command); 146 } 147 148 public static ProcessBuilder createJavaProcessBuilder(boolean addTestVmAndJavaOptions, String... command) throws Exception { 149 String javapath = JDKToolFinder.getJDKTool("java"); 150 151 ArrayList<String> args = new ArrayList<>(); 152 args.add(javapath); 153 Collections.addAll(args, getPlatformSpecificVMArgs()); 154 155 if (addTestVmAndJavaOptions) { 156 Collections.addAll(args, Utils.getTestJavaOpts()); 157 } 158 159 Collections.addAll(args, command); 160 161 // Reporting 162 StringBuilder cmdLine = new StringBuilder(); 163 for (String cmd : args) { 164 cmdLine.append(cmd).append(' '); 165 } 166 System.out.println("Command line: [" + cmdLine.toString() + "]"); 167 168 return new ProcessBuilder(args.toArray(new String[args.size()])); 169 } 170 171 /** 172 * Executes a test jvm process, waits for it to finish and returns the process output. 173 * The default jvm options from jtreg, test.vm.opts and test.java.opts, are added. 174 * The java from the test.jdk is used to execute the command. 175 * 176 * The command line will be like: 177 * {test.jdk}/bin/java {test.vm.opts} {test.java.opts} cmds 178 * 179 * @param cmds User specifed arguments. 180 * @return The output from the process. 181 */ 182 public static OutputAnalyzer executeTestJvm(String... cmds) throws Throwable { 183 ProcessBuilder pb = createJavaProcessBuilder(Utils.addTestJavaOpts(cmds)); 184 return executeProcess(pb); 185 } 186 187 /** 188 * Executes a process, waits for it to finish and returns the process output. 189 * The process will have exited before this method returns. 190 * @param pb The ProcessBuilder to execute. 191 * @return The {@linkplain OutputAnalyzer} instance wrapping the process. 192 */ 193 public static OutputAnalyzer executeProcess(ProcessBuilder pb) throws Exception { 194 OutputAnalyzer output = null; 195 Process p = null; 196 boolean failed = false; 197 try { 198 p = pb.start(); 199 output = new OutputAnalyzer(p); 200 p.waitFor(); 201 202 return output; 203 } catch (Throwable t) { 204 if (p != null) { 205 p.destroyForcibly().waitFor(); 206 } 207 208 failed = true; 209 System.out.println("executeProcess() failed: " + t); 210 throw t; 211 } finally { 212 if (failed) { 213 System.err.println(getProcessLog(pb, output)); 214 } 215 } 216 } 217 218 /** 219 * Executes a process, waits for it to finish and returns the process output. 220 * @param cmds The command line to execute. 221 * @return The output from the process. 222 */ 223 public static OutputAnalyzer executeProcess(String... cmds) throws Throwable { 224 return executeProcess(new ProcessBuilder(cmds)); 225 } 226 227 /** 228 * Used to log command line, stdout, stderr and exit code from an executed process. 229 * @param pb The executed process. 230 * @param output The output from the process. 231 */ 232 public static String getProcessLog(ProcessBuilder pb, OutputAnalyzer output) { 233 String stderr = output == null ? "null" : output.getStderr(); 234 String stdout = output == null ? "null" : output.getStdout(); 235 String exitValue = output == null ? "null": Integer.toString(output.getExitValue()); 236 StringBuilder logMsg = new StringBuilder(); 237 final String nl = System.getProperty("line.separator"); 238 logMsg.append("--- ProcessLog ---" + nl); 239 logMsg.append("cmd: " + getCommandLine(pb) + nl); 240 logMsg.append("exitvalue: " + exitValue + nl); 241 logMsg.append("stderr: " + stderr + nl); 242 logMsg.append("stdout: " + stdout + nl); 243 return logMsg.toString(); 244 } 245 246 /** 247 * @return The full command line for the ProcessBuilder. 248 */ 249 public static String getCommandLine(ProcessBuilder pb) { 250 if (pb == null) { 251 return "null"; 252 } 253 StringBuilder cmd = new StringBuilder(); 254 for (String s : pb.command()) { 255 cmd.append(s).append(" "); 256 } 257 return cmd.toString().trim(); 258 } 259 }