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.jdwp;
25 
26 import nsk.share.*;
27 import nsk.share.jpda.*;
28 
29 import java.io.*;
30 
31 /**
32  * This class provides debugger with connection to debugee VM
33  * using JDWP protocol.
34  * <p>
35  * This class provides abilities to launch and bind to debugee VM
36  * as described for base <code>DebugeeBinder</code> class,
37  * using raw JDWP protocol.
38  * <p>
39  * When <code>Binder</code> is asked to bind to debugee by invoking
40  * <code>bindToBebugee()</code> method it launches process
41  * with debugee VM and makes connection to it using JDWP transport
42  * corresponding to value of command line options <code>-connector</code>
43  * and <code>-transport</code>.
44  * After debugee is launched and connection is established
45  * <code>Binder</code> constructs <code>Debugee</code> object,
46  * that provides abilities to interact with debugee VM.
47  *
48  * @see Debugee
49  * @see DebugeeBinder
50  */
51 final public class Binder extends DebugeeBinder {
52 
53     /**
54      * Default message prefix for <code>Binder</code> object.
55      */
56     public static final String LOG_PREFIX = "binder> ";
57 
58     /**
59      * Get version string.
60      */
getVersion()61     public static String getVersion () {
62         return "@(#)Binder.java %I% %E%";
63     }
64 
65     // -------------------------------------------------- //
66 
67     /**
68      * Handler of command line arguments.
69      */
70     private ArgumentHandler argumentHandler = null;
71 
72     /**
73      * Return <code>argumentHandler</code> of this binder.
74      */
getArgumentHandler()75     public ArgumentHandler getArgumentHandler() {
76         return argumentHandler;
77     }
78 
79     // -------------------------------------------------- //
80 
81     /**
82      * Make new <code>Binder</code> object with specified
83      * <code>argumentHandler</code> and <code>log</code>.
84      */
Binder(ArgumentHandler argumentHandler, Log log)85     public Binder (ArgumentHandler argumentHandler, Log log) {
86         super(argumentHandler, log);
87         this.argumentHandler = argumentHandler;
88     }
89 
90     // -------------------------------------------------- //
91 
92     /**
93      * Start debugee VM and establish JDWP connection to it.
94      */
bindToDebugee(String classToExecute)95     public Debugee bindToDebugee (String classToExecute) {
96 
97         Debugee debugee = null;
98 
99         prepareForPipeConnection(argumentHandler);
100 
101         if (argumentHandler.isLaunchedRemotely()) {
102             connectToBindServer(classToExecute);
103             debugee = launchDebugee(classToExecute);
104         } else {
105             debugee = launchDebugee(classToExecute);
106             debugee.redirectOutput(log);
107         }
108 
109         Finalizer finalizer = new Finalizer(debugee);
110         finalizer.activate();
111 
112         Transport transport = debugee.connect();
113 
114         return debugee;
115     }
116 
117     /**
118      * Launch debugee VM for specified class.
119      */
launchDebugee(String classToExecute)120     public Debugee launchDebugee (String classToExecute) {
121 
122         try {
123 
124             if (argumentHandler.isLaunchedLocally()) {
125                 LocalLaunchedDebugee debugee = new LocalLaunchedDebugee(this);
126                 String address = debugee.prepareTransport(argumentHandler);
127                 if (address == null)
128                     address = makeTransportAddress();
129                 String[] argsArray = makeCommandLineArgs(classToExecute, address);
130                 debugee.launch(argsArray);
131                 return debugee;
132             }
133 
134             if (argumentHandler.isLaunchedRemotely()) {
135                 RemoteLaunchedDebugee debugee = new RemoteLaunchedDebugee(this);
136                 String address = debugee.prepareTransport(argumentHandler);
137                 if (address == null)
138                     address = makeTransportAddress();
139                 String[] argsArray = makeCommandLineArgs(classToExecute, address);
140                 debugee.launch(argsArray);
141                 return debugee;
142             }
143 
144             if (argumentHandler.isLaunchedManually()) {
145                 ManualLaunchedDebugee debugee = new ManualLaunchedDebugee(this);
146                 String address = debugee.prepareTransport(argumentHandler);
147                 if (address == null)
148                     address = makeTransportAddress();
149                 String cmdLine = makeCommandLineString(classToExecute, address, "\"");
150                 debugee.launch(cmdLine);
151                 return debugee;
152             }
153 
154             throw new TestBug("Unexpected launching mode: "
155                             + argumentHandler.getLaunchMode());
156         } catch (IOException e) {
157             e.printStackTrace(log.getOutStream());
158             throw new Failure("Caught exception while launching debugee:\n\t" + e);
159         }
160     }
161 
162 }
163 
164 /**
165  * Mirror of locally launched debugee.
166  */
167 final class LocalLaunchedDebugee extends Debugee {
168 
169     /** Enwrap the existing <code>VM</code> mirror. */
LocalLaunchedDebugee(Binder binder)170     public LocalLaunchedDebugee (Binder binder) {
171         super(binder);
172         checkTermination = true;
173     }
174 
175     // ---------------------------------------------- //
176 
launch(String[] args)177     public void launch(String[] args) throws IOException {
178         String cmdLine = ArgumentHandler.joinArguments(args, "\"");
179         display("Starting java process:\n" + cmdLine);
180         process = binder.launchProcess(args);
181     }
182 
183     /** Return exit status of the debugee VM. */
getStatus()184     public int getStatus () {
185         return process.exitValue();
186     }
187 
188     /** Check whether the debugee VM has been terminated. */
terminated()189     public boolean terminated () {
190         if (process == null)
191             return true;
192 
193         try {
194             int value = process.exitValue();
195             return true;
196         } catch (IllegalThreadStateException e) {
197             return false;
198         }
199     }
200 
201     // ---------------------------------------------- //
202 
203     /** Kill the debugee VM. */
killDebugee()204     protected void killDebugee () {
205         super.killDebugee();
206         if (!terminated()) {
207             log.display("Killing debugee VM process");
208             process.destroy();
209         }
210     }
211 
212     /** Wait until the debugee VM shutdown or crash. */
waitForDebugee()213     protected int waitForDebugee () throws InterruptedException {
214         return process.waitFor();
215     }
216 
217     /** Get a pipe to write to the debugee's stdin stream. */
getInPipe()218     protected OutputStream getInPipe () {
219         return process.getOutputStream();
220     }
221 
222     /** Get a pipe to read the debugee's stdout stream. */
getOutPipe()223     protected InputStream getOutPipe () {
224         return process.getInputStream();
225     }
226 
227     /** Get a pipe to read the debugee's stderr stream. */
getErrPipe()228     protected InputStream getErrPipe () {
229         return process.getErrorStream();
230     }
231 }
232 
233 
234 /**
235  * Mirror of remotely launched debugee.
236  */
237 final class RemoteLaunchedDebugee extends Debugee {
238 
239     /** Enwrap the existing <code>VM</code> mirror. */
RemoteLaunchedDebugee(Binder binder)240     public RemoteLaunchedDebugee (Binder binder) {
241         super(binder);
242     }
243 
244     // ---------------------------------------------- //
245 
launch(String[] args)246     public void launch(String[] args) throws IOException {
247         String cmdLine = ArgumentHandler.joinArguments(args, "\"");
248         display("Starting remote java process:\n" + cmdLine);
249         binder.launchRemoteProcess(args);
250     }
251 
252     /** Return exit status of the debugee VM. */
getStatus()253     public int getStatus () {
254         return binder.getRemoteProcessStatus();
255     }
256 
257     /** Check whether the debugee VM has been terminated. */
terminated()258     public boolean terminated () {
259         return binder.isRemoteProcessTerminated();
260     }
261 
262     // ---------------------------------------------- //
263 
264     /** Kill the debugee VM. */
killDebugee()265     protected void killDebugee () {
266         super.killDebugee();
267         if (!terminated()) {
268             log.display("Killing debugee VM process");
269             binder.killRemoteProcess();
270         }
271     }
272 
273     /** Wait until the debugee VM shutdown or crash. */
waitForDebugee()274     protected int waitForDebugee () {
275         return binder.waitForRemoteProcess();
276     }
277 
278     /** Get a pipe to write to the debugee's stdin stream. */
getInPipe()279     protected OutputStream getInPipe () {
280         return null;
281     }
282 
283     /** Get a pipe to read the debugee's stdout stream. */
getOutPipe()284     protected InputStream getOutPipe () {
285         return null;
286     }
287 
288     /** Get a pipe to read the debugee's stderr stream. */
getErrPipe()289     protected InputStream getErrPipe () {
290         return null;
291     }
292 
redirectStdout(OutputStream out)293     public void redirectStdout(OutputStream out) {
294     }
295 
redirectStdout(Log log, String prefix)296     public void redirectStdout(Log log, String prefix) {
297     }
298 
redirectStderr(OutputStream out)299     public void redirectStderr(OutputStream out) {
300     }
301 
redirectStderr(Log log, String prefix)302     public void redirectStderr(Log log, String prefix) {
303     }
304 }
305 
306 
307 /**
308  * Mirror of manually launched debugee.
309  */
310 final class ManualLaunchedDebugee extends Debugee {
311 
312     private int exitCode = 0;
313     private boolean finished = false;
314     private static BufferedReader bin = new BufferedReader(new InputStreamReader(System.in));
315 
316     /** Enwrap the existing <code>VM</code> mirror. */
ManualLaunchedDebugee(Binder binder)317     public ManualLaunchedDebugee (Binder binder) {
318         super(binder);
319     }
320 
321     // ---------------------------------------------- //
322 
launch(String commandLine)323     public void launch(String commandLine) throws IOException {
324         putMessage("Launch target VM using such command line:\n"
325                     + commandLine);
326         String answer = askQuestion("Has the VM successfully started? (yes/no)", "yes");
327         for ( ; ; ) {
328             if (answer.equals("yes"))
329                 break;
330             if (answer.equals("no"))
331                 throw new Failure ("Unable to manually launch debugee VM");
332             answer = askQuestion("Wrong answer. Please type yes or no", "yes");
333         }
334     }
335 
putMessage(String msg)336     private void putMessage(String msg) {
337         System.out.println("\n>>> " + msg);
338     }
339 
askQuestion(String question, String defaultAnswer)340     private String askQuestion(String question, String defaultAnswer) {
341         try {
342             System.out.print("\n>>> " + question);
343             System.out.print(" [" + defaultAnswer + "] ");
344             System.out.flush();
345             String answer = bin.readLine();
346             if (answer.equals(""))
347                 return defaultAnswer;
348             return answer;
349         } catch (IOException e) {
350             e.printStackTrace(log.getOutStream());
351             throw new Failure("Caught exception while reading answer:\n\t" + e);
352         }
353     }
354 
355     /** Return exit status of the debugee VM. */
getStatus()356     public int getStatus () {
357         if (! terminated()) {
358             throw new Failure("Unable to get status of debugee VM: process still alive");
359         }
360         return exitCode;
361     }
362 
363     /** Check whether the debugee VM has been terminated. */
terminated()364     public boolean terminated () {
365         if(! finished) {
366             String answer = askQuestion("Has the VM exited?", "no");
367             for ( ; ; ) {
368                 if (answer.equals("no"))
369                     return false;
370                 if (answer.equals("yes")) {
371                     finished = true;
372                     waitForDebugee();
373                     break;
374                 }
375                 answer = askQuestion("Wrong answer. Please type yes or no", "yes");
376             }
377         }
378         return finished;
379     }
380 
381     // ---------------------------------------------- //
382 
383     /** Kill the debugee VM. */
killDebugee()384     protected void killDebugee () {
385         super.killDebugee();
386         if (!terminated()) {
387             putMessage("Kill launched VM");
388             String answer = askQuestion("Has the VM successfully terminated? (yes/no)", "yes");
389             for ( ; ; ) {
390                 if (answer.equals("yes")) {
391                     finished = true;
392                     break;
393                 }
394                 if (answer.equals("no"))
395                     throw new Failure ("Unable to manually kill debugee VM");
396                 answer = askQuestion("Wrong answer. Please type yes or no", "yes");
397             }
398         }
399     }
400 
401     /** Wait until the debugee VM shutdown or crash. */
waitForDebugee()402     protected int waitForDebugee () {
403         putMessage("Wait for launched VM to exit.");
404         String answer = askQuestion("What is VM exit code?", "95");
405         for ( ; ; ) {
406             try {
407                 exitCode = Integer.parseInt(answer);
408                 break;
409             } catch (NumberFormatException e) {
410                 answer = askQuestion("Wrong answer. Please type integer value", "95");
411             }
412         }
413         finished = true;
414         return exitCode;
415     }
416 
417     /** Get a pipe to write to the debugee's stdin stream. */
getInPipe()418     protected OutputStream getInPipe () {
419         return null;
420     }
421 
422     /** Get a pipe to read the debugee's stdout stream. */
getOutPipe()423     protected InputStream getOutPipe () {
424         return null;
425     }
426 
427     /** Get a pipe to read the debugee's stderr stream. */
getErrPipe()428     protected InputStream getErrPipe () {
429         return null;
430     }
431 
redirectStdout(OutputStream out)432     public void redirectStdout(OutputStream out) {
433     }
434 
redirectStdout(Log log, String prefix)435     public void redirectStdout(Log log, String prefix) {
436     }
437 
redirectStderr(OutputStream out)438     public void redirectStderr(OutputStream out) {
439     }
440 
redirectStderr(Log log, String prefix)441     public void redirectStderr(Log log, String prefix) {
442     }
443 
close()444     public void close() {
445         try {
446             bin.close();
447         } catch (IOException e) {
448             log.display("WARNING: Caught IOException while closing InputStream");
449         }
450         bin = null;
451         super.close();
452     }
453 }
454