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