1 /* 2 * Copyright (c) 2001, 2018, 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 nsk.share.jpda; 25 26 import nsk.share.*; 27 import java.io.*; 28 import java.net.*; 29 30 /** 31 * This class is used to control debugee VM process. 32 * <p> 33 * Object of this class is constructed by <code>DebugeeBinder</code> 34 * and is used as a mirror of debugee VM process. 35 * It provides abilities to launch such process, 36 * redirect standard output streams, wait for process terminates 37 * or kill the process, and so on. 38 * <p> 39 * This is an abstract class that declares abstract methods to control 40 * debugee VM process. 41 * Derived classes should implement these methods corresponding to the mode 42 * that the process should be started in (locally, remotely or manually). 43 * <p> 44 * Particular derived classes <code>nsk.share.jdi.Debugee</code> and 45 * <code>nsk.share.jdwp.Debugee</code> provides additional abilities 46 * to control debugee VM using JDI or JDWP specific features. 47 * 48 * @see DebugeeBinder 49 * 50 * @see nsk.share.jdi.Debugee 51 * @see nsk.share.jdwp.Debugee 52 */ 53 abstract public class DebugeeProcess extends FinalizableObject { 54 55 /** Default prefix for log messages. */ 56 public static final String LOG_PREFIX = "binder> "; 57 public static final String DEBUGEE_STDOUT_LOG_PREFIX = "debugee.stdout> "; 58 public static final String DEBUGEE_STDERR_LOG_PREFIX = "debugee.stderr> "; 59 60 /** Messages prefix. */ 61 protected String prefix = LOG_PREFIX; 62 63 /** Binder that creates this debugee process. */ 64 protected DebugeeBinder binder = null; 65 66 /** Messages log from binder. */ 67 protected Log log = null; 68 69 /** Communicational channel between debuger and debugee. */ 70 protected IOPipe pipe = null; 71 72 /** Argument handler from binder. */ 73 protected DebugeeArgumentHandler argumentHandler = null; 74 75 /** Need or not to check debuggee process termination at exit. */ 76 protected boolean checkTermination = false; 77 78 /** Debugee VM process or <i>null</i> if not available. */ 79 protected Process process = null; 80 81 /** Make new <code>Debugee</code> object for the given binder. */ DebugeeProcess(DebugeeBinder binder)82 protected DebugeeProcess (DebugeeBinder binder) { 83 this.binder = binder; 84 this.log = binder.getLog(); 85 } 86 87 /** 88 * Return already bound ServerSocket for IOPipe connection or null. 89 */ getPipeServerSocket()90 protected ServerSocket getPipeServerSocket() { 91 return binder.getPipeServerSocket(); 92 } 93 94 /** Return <code>DebugeeArgumentHandler</code> of the debugee object. */ getArgumentHandler()95 public DebugeeArgumentHandler getArgumentHandler() { 96 return binder.getArgumentHandler(); 97 } 98 99 /** Return <code>Log</code> of the debugee object. */ getLog()100 public Log getLog() { 101 return log; 102 } 103 104 /** Return <code>Process</code> object associated with debugee VM or <i>null</i>. */ getProcess()105 public Process getProcess() { 106 return process; 107 } 108 109 // --------------------------------------------------- // 110 111 /** Created and return new IOPipe channel to the debugee VM. */ createIOPipe()112 public IOPipe createIOPipe() { 113 if (pipe != null) { 114 throw new TestBug("IOPipe channel is already created"); 115 } 116 pipe = new IOPipe(this); 117 return pipe; 118 } 119 120 /** Return IOPipe channel or null if not yet ctreated. */ getIOPipe()121 public IOPipe getIOPipe() { 122 return pipe; 123 } 124 125 /** Receive and return signal from the debugee VM via IOPipe channel. 126 * 127 * @throws TestBug if IOPipe channel is not created 128 */ receiveSignal()129 public String receiveSignal() { 130 if ( pipe == null ) 131 throw new TestBug("IOPipe channel is not initialized"); 132 return pipe.readln(); 133 } 134 135 /** Receive an expected <code>signal</code> from the debugee VM via IOPipe channel. 136 * 137 * @throws Failure if received signal is not equal to the expected one 138 * @throws TestBug if IOPipe channel is not created 139 */ receiveExpectedSignal(String signal)140 public void receiveExpectedSignal(String signal) { 141 String line = receiveSignal(); 142 if (line == null || !line.equals(signal)) 143 throw new Failure("Received unexpected signal from debugee: " + line); 144 display("Received expected signal from debugee: " + signal); 145 } 146 147 /** Send <code>signal</code> to the debugee VM via IOPipe channel. 148 * 149 * @throws TestBug if IOPipe channel is not defined created. 150 */ sendSignal(String signal)151 public void sendSignal(String signal) { 152 if (pipe == null) 153 throw new TestBug("IOPipe channel is not initialized"); 154 pipe.println(signal); 155 } 156 157 158 // --------------------------------------------------- // 159 160 /** Wait until the debugee VM shutdown or crash. */ waitForDebugee()161 abstract protected int waitForDebugee () throws InterruptedException; 162 163 /** Kill the debugee VM. */ killDebugee()164 abstract protected void killDebugee (); 165 166 /** Check whether the debugee VM has been terminated. */ terminated()167 abstract public boolean terminated (); 168 169 /** Return the debugee VM exit status. */ getStatus()170 abstract public int getStatus (); 171 172 /** Get a pipe to write to the debugee's stdin stream. */ getInPipe()173 abstract protected OutputStream getInPipe (); 174 175 /** Get a pipe to read the debugee's stdout stream. */ getOutPipe()176 abstract protected InputStream getOutPipe (); 177 178 /** Get a pipe to read the debugee's stderr stream. */ getErrPipe()179 abstract protected InputStream getErrPipe (); 180 181 // --------------------------------------------------- // 182 183 /** 184 * Wait until the debugee VM shutdown or crash, 185 * and let finish its stdout, stderr, and stdin 186 * redirectors (if any). 187 * 188 * @return Debugee process exit code. 189 * @see #waitForRedirectors(long) 190 */ waitFor()191 public int waitFor () { 192 long timeout = binder.getArgumentHandler().getWaitTime() * 60 * 1000; 193 int exitCode = 0; 194 try { 195 exitCode = waitForDebugee(); 196 } catch (InterruptedException ie) { 197 ie.printStackTrace(log.getOutStream()); 198 throw new Failure("Caught exception while waiting for debuggee process: \n\t" + ie); 199 } 200 waitForRedirectors(timeout); 201 if (process != null) { 202 process.destroy(); 203 } 204 return exitCode; 205 } 206 207 /** 208 * Wait until the debugee VM redirectors to complete for specified <code>timeout</code>. 209 * 210 * @see #waitFor() 211 */ waitForRedirectors(long timeout)212 public void waitForRedirectors (long timeout) { 213 try { 214 if (stdinRedirector != null) { 215 if (stdinRedirector.isAlive()) { 216 stdinRedirector.join(timeout); 217 if (stdinRedirector.isAlive()) { 218 log.complain("Timeout for waiting STDIN redirector exceeded"); 219 stdinRedirector.interrupt(); 220 } 221 } 222 stdinRedirector = null; 223 }; 224 if (stdoutRedirector != null) { 225 if (stdoutRedirector.isAlive()) { 226 stdoutRedirector.join(timeout); 227 if (stdoutRedirector.isAlive()) { 228 log.complain("Timeout for waiting STDOUT redirector exceeded"); 229 stdoutRedirector.interrupt(); 230 } 231 } 232 stdoutRedirector = null; 233 }; 234 if (stderrRedirector != null) { 235 if (stderrRedirector.isAlive()) { 236 stderrRedirector.join(timeout); 237 if (stderrRedirector.isAlive()) { 238 log.complain("Timeout for waiting STDERR redirector exceeded"); 239 stderrRedirector.interrupt(); 240 } 241 } 242 stderrRedirector = null; 243 }; 244 } catch (InterruptedException ie) { 245 ie.printStackTrace(log.getOutStream()); 246 throw new Failure("Caught exception while waiting for debuggee output redirectors: \n\t" 247 + ie); 248 } 249 } 250 251 // --------------------------------------------------- // 252 253 /** 254 * Get a pipe to write to the debugee's stdin stream, 255 * or throw TestBug exception is redirected. 256 */ getStdin()257 final public OutputStream getStdin () { 258 if (stdinRedirector != null) 259 throw new TestBug("Debugee's stdin is redirected"); 260 return getInPipe(); 261 } 262 263 /** 264 * Get a pipe to read the debugee's stdout stream, 265 * or throw TestBug exception is redirected. 266 */ getStdout()267 final public InputStream getStdout () { 268 if (stdoutRedirector != null) 269 throw new TestBug("Debugee's stdout is redirected"); 270 return getOutPipe(); 271 } 272 273 /** 274 * Get a pipe to read the debugee's stderr stream, 275 * or throw TestBug exception is redirected. 276 */ getStderr()277 final public InputStream getStderr () { 278 if (stderrRedirector != null) 279 throw new TestBug("Debugee's stderr is redirected"); 280 return getErrPipe(); 281 } 282 283 // --------------------------------------------------- // 284 285 private IORedirector stdoutRedirector = null; 286 private IORedirector stderrRedirector = null; 287 private IORedirector stdinRedirector = null; 288 289 // /** 290 // * Start a thread redirecting the given <code>in</code> stream 291 // * to the debugee's stdin. If the debugee's stdin was already 292 // * redirected, the TestBug exception is thrown. 293 // */ 294 // final public void redirectStdin(InputStream in) { 295 // if (stdinRedirector != null) 296 // throw new TestBug("the debugee's stdin is already redirected"); 297 // stdinRedirector = new IORedirector(in,getInPipe()); 298 // stdinRedirector.setName("IORedirector for stdin"); 299 // stdinRedirector.setDaemon(true); 300 // stdinRedirector.start(); 301 // } 302 303 /** 304 * Start thread redirecting the debugee's stdout to the 305 * given <code>out</code> stream. If the debugee's stdout 306 * was already redirected, the TestBug exception is thrown. 307 * 308 * @deprecated Use redirectStdout(Log, String) instead. 309 */ redirectStdout(OutputStream out)310 public void redirectStdout(OutputStream out) { 311 if (stdoutRedirector != null) { 312 return; 313 } 314 // throw new TestBug("Debugee's stdout is already redirected"); 315 stdoutRedirector = new IORedirector(getOutPipe(),out); 316 stdoutRedirector.setPrefix(DEBUGEE_STDOUT_LOG_PREFIX); 317 stdoutRedirector.setName("IORedirector for stdout"); 318 stdoutRedirector.setDaemon(true); 319 stdoutRedirector.start(); 320 } 321 322 /** 323 * Start thread redirecting the debugee's stdout to the 324 * given <code>Log</code>. If the debugee's stdout 325 * was already redirected, the TestBug exception is thrown. 326 */ redirectStdout(Log log, String prefix)327 public void redirectStdout(Log log, String prefix) { 328 if (stdoutRedirector != null) { 329 // stdoutRedirector.setPrefix(prefix); 330 return; 331 // throw new TestBug("the debugee's stdout is already redirected"); 332 } 333 stdoutRedirector = new IORedirector(new BufferedReader(new InputStreamReader(getOutPipe())), log, prefix); 334 stdoutRedirector.setName("IORedirector for stdout"); 335 stdoutRedirector.setDaemon(true); 336 stdoutRedirector.start(); 337 } 338 339 /** 340 * Start thread redirecting the debugee's stderr to the 341 * given <code>err</code> stream. If the debugee's stderr 342 * was already redirected, the TestBug exception is thrown. 343 * 344 * @deprecated Use redirectStderr(Log, String) instead. 345 */ redirectStderr(OutputStream err)346 public void redirectStderr(OutputStream err) { 347 if (stderrRedirector != null) { 348 return; 349 } 350 // throw new TestBug("Debugee's stderr is already redirected"); 351 stderrRedirector = new IORedirector(getErrPipe(),err); 352 stderrRedirector.setPrefix(DEBUGEE_STDERR_LOG_PREFIX); 353 stdoutRedirector.setName("IORedirector for stderr"); 354 stderrRedirector.setDaemon(true); 355 stderrRedirector.start(); 356 } 357 358 /** 359 * Start thread redirecting the debugee's stderr to the 360 * given <code>Log</code>. If the debugee's stderr 361 * was already redirected, the TestBug exception is thrown. 362 */ redirectStderr(Log log, String prefix)363 public void redirectStderr(Log log, String prefix) { 364 if (stderrRedirector != null) { 365 // stderrRedirector.setPrefix(prefix); 366 return; 367 // throw new TestBug("Debugee's stderr is already redirected"); 368 } 369 stderrRedirector = new IORedirector(new BufferedReader(new InputStreamReader(getErrPipe())), log, prefix); 370 stdoutRedirector.setName("IORedirector for stderr"); 371 stderrRedirector.setDaemon(true); 372 stderrRedirector.start(); 373 } 374 375 /** 376 * Start thread redirecting the debugee's stdout/stderr to the 377 * given <code>Log</code> using standard prefixes. 378 * If the debugee's stdout/stderr were already redirected, 379 * the TestBug exception is thrown. 380 */ redirectOutput(Log log)381 public void redirectOutput(Log log) { 382 redirectStdout(log, "debugee.stdout> "); 383 redirectStderr(log, "debugee.stderr> "); 384 } 385 // --------------------------------------------------- // 386 387 /** 388 * Kill the debugee VM if it is not terminated yet. 389 * 390 * @throws Throwable if any throwable exception is thrown during finalization 391 */ close()392 public void close() { 393 if (checkTermination) { 394 if (!terminated()) { 395 complain("Debugee VM has not exited correctly: trying to kill it"); 396 killDebugee(); 397 } 398 checkTermination = false; 399 } 400 } 401 402 // --------------------------------------------------- // 403 404 /** 405 * Display log message with prefix. 406 */ display(String message)407 protected void display(String message) { 408 log.display(prefix + message); 409 } 410 411 /** 412 * Complain about error with specified message. 413 */ complain(String message)414 protected void complain(String message) { 415 log.complain(prefix + message); 416 } 417 418 /** 419 * Finalize debuggee VM wrapper by invoking <code>close()</code>. 420 * 421 * @throws Throwable if any throwable exception is thrown during finalization 422 */ finalize()423 protected void finalize() throws Throwable { 424 close(); 425 super.finalize(); 426 } 427 } 428