1 /*
2  * Copyright (c) 2001, 2020, 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.jdi;
25 
26 import jdk.test.lib.Platform;
27 import nsk.share.*;
28 import nsk.share.jpda.*;
29 
30 import com.sun.jdi.*;
31 import com.sun.jdi.connect.*;
32 
33 import com.sun.jdi.connect.Connector.Argument;
34 import java.io.*;
35 import java.net.*;
36 import java.util.*;
37 
38 /**
39  * This class provides debugger with connection to debugee VM
40  * using JDI connectors.
41  *<p>
42  * This class provides abilities to launch and bind to debugee VM
43  * as described for base <code>DebugeeBinder</code> class,
44  * using JDI connectors and <code>com.sun.VirtualMachine</code> mirror.
45  * <p>
46  * When <code>Binder</code> is asked to bind to debugee by invoking
47  * <code>bindToBebugee()</code> method it uses
48  * <code>com.sun.jdi.Connector</code> object corresponding to
49  * value of command line options <code>-connector</code> and
50  * <code>-transport</code> to launch and connect to debugee VM.
51  * After debugee is launched and connection is established
52  * <code>Binder</code> uses <code>com.sun.jdi.VirtualMachine</code>
53  * object to construct <code>Debugee</code> object, that
54  * provides abilities to interact with debugee VM.
55  *
56  * @see Debugee
57  * @see DebugeeBinder
58  */
59 public class Binder extends DebugeeBinder {
60 
61     /**
62      * Default message prefix for <code>Binder</code> object.
63      */
64     public static final String LOG_PREFIX = "binder> ";
65 
66     /**
67      * Get version string.
68      */
getVersion()69     public static String getVersion () {
70         return "@(#)Binder.java 1.14 03/10/08";
71     }
72 
73     // -------------------------------------------------- //
74 
75     /**
76      * Handler of command line arguments.
77      */
78     private ArgumentHandler argumentHandler = null;
79 
80     /**
81      * Return <code>argumentHandler</code> of this binder.
82      */
getArgumentHandler()83     public ArgumentHandler getArgumentHandler() {
84         return argumentHandler;
85     }
86 
87     // -------------------------------------------------- //
88 
89     /**
90      * Make <code>Binder</code> object and pass raw command line arguments.
91      *
92      * @deprecated  Use newer
93      *              <code>Binder(ArgumentHandler,Log)</code>
94      *              constructor.
95      */
Binder(String args[])96     public Binder (String args[]) {
97         this(args, new Log(System.err));
98     }
99 
100     /**
101      * Make <code>Binder</code> object for raw command line arguments
102      * and specified <code>log</code> object.
103      *
104      * @deprecated  Use newer
105      *              <code>Binder(ArgumentHandler,Log)</code>
106      *              constructor.
107      */
Binder(String args[], Log log)108     public Binder (String args[], Log log) {
109         this(new ArgumentHandler(args), log);
110     }
111 
112     /**
113      * Make <code>Binder</code> object for specified command line arguments
114      * and <code>log</code> object.
115      */
Binder(ArgumentHandler argumentHandler, Log log)116     public Binder (ArgumentHandler argumentHandler, Log log) {
117         super(argumentHandler, log);
118         this.argumentHandler = argumentHandler;
119     }
120 
121     // -------------------------------------------------- //
122 
123     /**
124      * Make initial <code>Debugee</code> object for local debuggee process
125      * started with launching connector.
126      */
makeLocalDebugee(Process process)127     public Debugee makeLocalDebugee(Process process) {
128         LocalLaunchedDebugee debugee = new LocalLaunchedDebugee(process, this);
129 
130         Finalizer finalizer = new Finalizer(debugee);
131         finalizer.activate();
132 
133         return debugee;
134     }
135 
136     /**
137      * Launch local debuggee process with specified command line
138      * and make initial <code>Debugee</code> object.
139      */
startLocalDebugee(String cmd)140     public Debugee startLocalDebugee(String cmd) {
141         Process process = null;
142 
143         try {
144             process = launchProcess(cmd);
145         } catch (IOException e) {
146             e.printStackTrace(log.getOutStream());
147             throw new Failure("Caught exception while launching local debuggee VM process:\n\t"
148                             + e);
149         }
150 
151         return makeLocalDebugee(process);
152     }
153 
154     /**
155      * Make debuggee wrapper for already launched debuggee VM.
156      * After enwraping debugee's output is redirected to Binder's log,
157      * VMStartEvent is received and debuggee is initialized.
158      */
enwrapDebugee(VirtualMachine vm, Process proc)159     public Debugee enwrapDebugee(VirtualMachine vm, Process proc) {
160         Debugee debugee = makeLocalDebugee(proc);
161 
162         display("Redirecting VM output");
163         debugee.redirectOutput(log);
164         debugee.setupVM(vm);
165 
166         long timeout = argumentHandler.getWaitTime() * 60 * 1000; // milliseconds
167 
168         display("Waiting for VM initialized");
169         debugee.waitForVMInit(timeout);
170 
171         return debugee;
172     }
173 
174     /**
175      * Launch debugee VM and establish connection to it without waiting for VMStartEvent.
176      * After launching debugee's output is redirected to Binder's log,
177      * but VMStartEvent is not received and so debuggee is not fully initialized.
178      *
179      * @see #bindToDebugee(String)
180      */
bindToDebugeeNoWait(String classToExecute)181     public Debugee bindToDebugeeNoWait(String classToExecute) {
182 
183         VirtualMachineManager vmm = Bootstrap.virtualMachineManager();
184         display("VirtualMachineManager: version "
185                 + vmm.majorInterfaceVersion() + "."
186                 + vmm.minorInterfaceVersion());
187 
188         Debugee debugee = null;
189 
190         String classPath = null;
191 //        classPath = System.getProperty("java.class.path");
192 
193         prepareForPipeConnection(argumentHandler);
194 
195         if (argumentHandler.isLaunchedLocally()) {
196 
197             if (argumentHandler.isDefaultConnector()) {
198                 debugee = localDefaultLaunchDebugee(vmm, classToExecute, classPath);
199             } else if (argumentHandler.isRawLaunchingConnector()) {
200                 debugee = localRawLaunchDebugee(vmm, classToExecute, classPath);
201             } else if (argumentHandler.isLaunchingConnector()) {
202                 debugee = localLaunchDebugee(vmm, classToExecute, classPath);
203             } else if (argumentHandler.isAttachingConnector()) {
204                 debugee = localLaunchAndAttachDebugee(vmm, classToExecute, classPath);
205             } else if (argumentHandler.isListeningConnector()) {
206                 debugee = localLaunchAndListenDebugee(vmm, classToExecute, classPath);
207             } else {
208                 throw new TestBug("Unexpected connector type for local debugee launch mode"
209                                   + argumentHandler.getConnectorType());
210             }
211 
212         } else if (argumentHandler.isLaunchedRemotely()) {
213 
214             connectToBindServer(classToExecute);
215 
216             if (argumentHandler.isAttachingConnector()) {
217                 debugee = remoteLaunchAndAttachDebugee(vmm, classToExecute, classPath);
218             } else if (argumentHandler.isListeningConnector()) {
219                 debugee = remoteLaunchAndListenDebugee(vmm, classToExecute, classPath);
220             } else {
221                 throw new TestBug("Unexpected connector type for remote debugee launch mode"
222                                   + argumentHandler.getConnectorType());
223             }
224 
225         } else if (argumentHandler.isLaunchedManually()) {
226 
227             if (argumentHandler.isAttachingConnector()) {
228                 debugee = manualLaunchAndAttachDebugee(vmm, classToExecute, classPath);
229             } else if (argumentHandler.isListeningConnector()) {
230                 debugee = manualLaunchAndListenDebugee(vmm, classToExecute, classPath);
231             } else {
232                 throw new TestBug("Unexpected connector type for manual debugee launch mode"
233                                   + argumentHandler.getConnectorType());
234             }
235 
236         } else {
237             throw new Failure("Unexpected debugee launching mode: " + argumentHandler.getLaunchMode());
238         }
239 
240         return debugee;
241     }
242 
243     /**
244      * Launch debugee VM and establish JDI connection.
245      * After launching debugee's output is redirected to Binder's log,
246      * VMStart event is received and debuggee is initialized.
247      *
248      * @see #bindToDebugeeNoWait(String)
249      */
bindToDebugee(String classToExecute)250     public Debugee bindToDebugee(String classToExecute) {
251         Debugee debugee = bindToDebugeeNoWait(classToExecute);
252 
253         if(argumentHandler.getOptions().getProperty("traceAll") != null)
254             debugee.VM().setDebugTraceMode(VirtualMachine.TRACE_ALL);
255 
256         long timeout = argumentHandler.getWaitTime() * 60 * 1000; // milliseconds
257 
258         display("Waiting for VM initialized");
259         debugee.waitForVMInit(timeout);
260 
261         return debugee;
262     }
263 
264     // -------------------------------------------------- //
265 
266     /**
267      * Launch debugee locally via the default LaunchingConnector.
268      */
localDefaultLaunchDebugee(VirtualMachineManager vmm, String classToExecute, String classPath)269     private Debugee localDefaultLaunchDebugee (VirtualMachineManager vmm,
270                                                 String classToExecute,
271                                                 String classPath) {
272         display("Finding connector: " + "default" );
273         LaunchingConnector connector = vmm.defaultConnector();
274         Map<String,? extends Argument> arguments = setupLaunchingConnector(connector, classToExecute, classPath);
275 
276         VirtualMachine vm;
277         try {
278             display("Launching debugee");
279             vm = connector.launch(arguments);
280         } catch (IllegalConnectorArgumentsException e) {
281             e.printStackTrace(log.getOutStream());
282             throw new TestBug("Wrong connector arguments used to launch debuggee VM:\n\t" + e);
283         } catch (VMStartException e) {
284             e.printStackTrace(log.getOutStream());
285             String msg = readVMStartExceptionOutput(e, log.getOutStream());
286             throw new Failure("Caught exception while starting debugee VM:\n\t" + e + "\n" + msg);
287         } catch (IOException e) {
288             e.printStackTrace(log.getOutStream());
289             throw new Failure("Caught exception while launching debugee VM:\n\t" + e);
290         };
291 
292         Process process = vm.process();
293         Debugee debugee = makeLocalDebugee(process);
294         debugee.redirectOutput(log);
295         debugee.setupVM(vm);
296 
297         return debugee;
298     }
299 
300 
301     /**
302      * Launch debugee locally via the default LaunchingConnector.
303      */
localLaunchDebugee(VirtualMachineManager vmm, String classToExecute, String classPath)304     private Debugee localLaunchDebugee (VirtualMachineManager vmm,
305                                             String classToExecute,
306                                             String classPath) {
307 
308         display("Finding connector: " + argumentHandler.getConnectorName() );
309         LaunchingConnector connector =
310             (LaunchingConnector) findConnector(argumentHandler.getConnectorName(),
311                                                 vmm.launchingConnectors());
312         Map<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> arguments = setupLaunchingConnector(connector, classToExecute, classPath);
313 
314         VirtualMachine vm;
315         try {
316             display("Launching debugee");
317             vm = connector.launch(arguments);
318         } catch (IllegalConnectorArgumentsException e) {
319             e.printStackTrace(log.getOutStream());
320             throw new TestBug("Wrong connector arguments used to launch debuggee VM:\n\t" + e);
321         } catch (VMStartException e) {
322             e.printStackTrace(log.getOutStream());
323             String msg = readVMStartExceptionOutput(e, log.getOutStream());
324             throw new Failure("Caught exception while starting debugee VM:\n\t" + e + "\nProcess output:\n\t" + msg);
325         } catch (IOException e) {
326             e.printStackTrace(log.getOutStream());
327             throw new Failure("Caught exception while launching debugee VM:\n\t" + e);
328         };
329 
330         Process process = vm.process();
331         Debugee debugee = makeLocalDebugee(process);
332         debugee.redirectOutput(log);
333         debugee.setupVM(vm);
334 
335         return debugee;
336     }
337 
338     /**
339      * Launch debugee locally via the RawLaunchingConnector.
340      */
localRawLaunchDebugee(VirtualMachineManager vmm, String classToExecute, String classPath)341     private Debugee localRawLaunchDebugee (VirtualMachineManager vmm,
342                                             String classToExecute,
343                                             String classPath) {
344         display("Finding connector: " + argumentHandler.getConnectorName() );
345         LaunchingConnector connector =
346             (LaunchingConnector) findConnector(argumentHandler.getConnectorName(),
347                                                 vmm.launchingConnectors());
348         Map<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> arguments = setupRawLaunchingConnector(connector, classToExecute, classPath);
349 
350         VirtualMachine vm;
351         try {
352             display("Launching debugee");
353             vm = connector.launch(arguments);
354         } catch (IllegalConnectorArgumentsException e) {
355             e.printStackTrace(log.getOutStream());
356             throw new TestBug("Wrong connector arguments used to launch debuggee VM:\n\t" + e);
357         } catch (VMStartException e) {
358             e.printStackTrace(log.getOutStream());
359             String msg = readVMStartExceptionOutput(e, log.getOutStream());
360             throw new Failure("Caught exception while starting debugee VM:\n\t" + e + "\nProcess output:\n\t" + msg);
361         } catch (IOException e) {
362             e.printStackTrace(log.getOutStream());
363             throw new Failure("Caught exception while launching debugee VM:\n\t" + e);
364         };
365 
366         Process process = vm.process();
367         Debugee debugee = makeLocalDebugee(process);
368         debugee.redirectOutput(log);
369         debugee.setupVM(vm);
370 
371         return debugee;
372     }
373 
374     /**
375      * Launch debugee VM locally as a local process and connect to it using
376      * <code>AttachingConnector</code>.
377      */
localLaunchAndAttachDebugee(VirtualMachineManager vmm, String classToExecute, String classPath)378     private Debugee localLaunchAndAttachDebugee (VirtualMachineManager vmm,
379                                                     String classToExecute,
380                                                     String classPath) {
381         display("FindingConnector: " + argumentHandler.getConnectorName() );
382         AttachingConnector connector =
383             (AttachingConnector) findConnector(argumentHandler.getConnectorName(),
384                                                 vmm.attachingConnectors());
385         Map<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> arguments = setupAttachingConnector(connector, classToExecute, classPath);
386 
387         String address = makeTransportAddress();
388         String[] cmdLineArgs = makeCommandLineArgs(classToExecute, address);
389         String javaCmdLine = makeCommandLineString(classToExecute, address, "\"");
390 
391         display("Starting java process:\n\t" + javaCmdLine);
392         Debugee debugee = startLocalDebugee(cmdLineArgs);
393         debugee.redirectOutput(log);
394 
395         display("Attaching to debugee");
396         VirtualMachine vm = null;
397         IOException ioe = null;
398         for (int i = 0; i < CONNECT_TRIES; i++) {
399             try {
400                 vm = connector.attach(arguments);
401                 display("Debugee attached");
402                 debugee.setupVM(vm);
403                 return debugee;
404             } catch (IOException e) {
405                 display("Attempt #" + i + " to connect to debugee VM failed:\n\t" + e);
406                 ioe = e;
407                 if (debugee.terminated()) {
408                     throw new Failure("Unable to connect to debuggee VM: VM process is terminated");
409                 }
410                 try {
411                     Thread.currentThread().sleep(CONNECT_TRY_DELAY);
412                 } catch (InterruptedException ie) {
413                     ie.printStackTrace(log.getOutStream());
414                     throw new Failure("Thread interrupted while pausing connection attempts:\n\t"
415                                     + ie);
416                 }
417             } catch (IllegalConnectorArgumentsException e) {
418                 e.printStackTrace(log.getOutStream());
419                 throw new TestBug("Wrong connector arguments used to attach to debuggee VM:\n\t" + e);
420             }
421         }
422         throw new Failure("Unable to connect to debugee VM after " + CONNECT_TRIES
423                         + " tries:\n\t" + ioe);
424     }
425 
426     /**
427      * Launch debugee VM locally as a local process and connect to it using
428      * <code>ListeningConnector</code>.
429      */
localLaunchAndListenDebugee(VirtualMachineManager vmm, String classToExecute, String classPath)430     private Debugee localLaunchAndListenDebugee (VirtualMachineManager vmm,
431                                                     String classToExecute,
432                                                     String classPath) {
433         display("Finding connector: " + argumentHandler.getConnectorName() );
434         ListeningConnector connector =
435             (ListeningConnector) findConnector(argumentHandler.getConnectorName(),
436                                                 vmm.listeningConnectors());
437         Map<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> arguments = setupListeningConnector(connector, classToExecute, classPath);
438 
439         String address = null;
440         try {
441             display("Listening for connection from debugee");
442             address = connector.startListening(arguments);
443         } catch (IllegalConnectorArgumentsException e) {
444             e.printStackTrace(log.getOutStream());
445             throw new TestBug("Wrong connector arguments used to listen debuggee VM:\n\t" + e);
446         } catch (IOException e) {
447             e.printStackTrace(log.getOutStream());
448             throw new Failure("Caught exception while starting listening debugee VM:\n\t" + e);
449         };
450 
451         String[] cmdLineArgs = makeCommandLineArgs(classToExecute, address);
452         String javaCmdLine = makeCommandLineString(classToExecute, address, "\"");
453 
454         display("Starting java process:\n\t" + javaCmdLine);
455         Debugee debugee = startLocalDebugee(cmdLineArgs);
456         debugee.redirectOutput(log);
457 
458         display("Waiting for connection from debugee");
459         VirtualMachine vm = null;
460         IOException ioe = null;
461         for (int i = 0; i < CONNECT_TRIES; i++) {
462             try {
463                 vm = connector.accept(arguments);
464                 connector.stopListening(arguments);
465                 display("Debugee attached");
466                 debugee.setupVM(vm);
467                 return debugee;
468             } catch (IOException e) {
469                 display("Attempt #" + i + " to listen debugee VM failed:\n\t" + e);
470                 ioe = e;
471                 if (debugee.terminated()) {
472                     throw new Failure("Unable to connect to debuggee VM: VM process is terminated");
473                 }
474                 try {
475                     Thread.currentThread().sleep(CONNECT_TRY_DELAY);
476                 } catch (InterruptedException ie) {
477                     ie.printStackTrace(log.getOutStream());
478                     throw new Failure("Thread interrupted while pausing connection attempts:\n\t"
479                                     + ie);
480                 }
481             } catch (IllegalConnectorArgumentsException e) {
482                 e.printStackTrace(log.getOutStream());
483                 throw new TestBug("Wrong connector arguments used to listen debuggee VM:\n\t" + e);
484             }
485         }
486         throw new Failure("Unable to connect to debugee VM after " + CONNECT_TRIES
487                         + " tries:\n\t" + ioe);
488     }
489 
490     // -------------------------------------------------- //
491 
492     /**
493      * Launch debugee VM remotely via <code>BindServer</code> and connect to it using
494      * <code>AttachingConnector</code>.
495      */
remoteLaunchAndAttachDebugee(VirtualMachineManager vmm, String classToExecute, String classPath)496     private Debugee remoteLaunchAndAttachDebugee (VirtualMachineManager vmm,
497                                                     String classToExecute,
498                                                     String classPath) {
499         display("Finding connector: " + argumentHandler.getConnectorName() );
500         AttachingConnector connector =
501             (AttachingConnector) findConnector(argumentHandler.getConnectorName(),
502                                                 vmm.attachingConnectors());
503 
504         Map<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> arguments = setupAttachingConnector(connector, classToExecute, classPath);
505 
506         String address = makeTransportAddress();
507         String[] cmdLineArgs = makeCommandLineArgs(classToExecute, address);
508         String javaCmdLine = makeCommandLineString(classToExecute, address, "\"");
509 
510         display("Starting remote java process:\n\t" + javaCmdLine);
511         Debugee debugee = startRemoteDebugee(cmdLineArgs);
512 
513         display("Attaching to debugee");
514         VirtualMachine vm;
515         IOException ioe = null;
516         for (int i = 0; i < CONNECT_TRIES; i++) {
517             try {
518                 vm = connector.attach(arguments);
519                 display("Debugee attached");
520                 debugee.setupVM(vm);
521                 return debugee;
522             } catch (IOException e) {
523                 display("Attempt #" + i + " to connect to debugee VM failed:\n\t" + e);
524                 ioe = e;
525                 if (debugee.terminated()) {
526                     throw new Failure("Unable to connect to debuggee VM: VM process is terminated");
527                 }
528                 try {
529                     Thread.currentThread().sleep(CONNECT_TRY_DELAY);
530                 } catch (InterruptedException ie) {
531                     ie.printStackTrace(log.getOutStream());
532                     throw new Failure("Thread interrupted while pausing connection attempts:\n\t"
533                                     + ie);
534                 }
535             } catch (IllegalConnectorArgumentsException e) {
536                 e.printStackTrace(log.getOutStream());
537                 throw new TestBug("Wrong connector arguments used to attach to debuggee VM:\n\t" + e);
538             }
539         }
540         throw new Failure("Unable to connect to debugee VM after " + CONNECT_TRIES
541                         + " tries:\n\t" + ioe);
542     }
543 
544     /**
545      * Launch debugee VM remotely via <code>BindServer</code> and connect to it using
546      * <code>ListeningConnector</code>.
547      */
remoteLaunchAndListenDebugee(VirtualMachineManager vmm, String classToExecute, String classPath)548     private Debugee remoteLaunchAndListenDebugee (VirtualMachineManager vmm,
549                                                     String classToExecute,
550                                                     String classPath) {
551         display("Finding connector: " + argumentHandler.getConnectorName() );
552         ListeningConnector connector =
553             (ListeningConnector) findConnector(argumentHandler.getConnectorName(),
554                                                 vmm.listeningConnectors());
555         Map<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> arguments = setupListeningConnector(connector, classToExecute, classPath);
556 
557         String address = null;
558         try {
559             display("Listening for connection from debugee");
560             address = connector.startListening(arguments);
561         } catch (IllegalConnectorArgumentsException e) {
562             e.printStackTrace(log.getOutStream());
563             throw new TestBug("Wrong connector arguments used to listen debuggee VM:\n\t" + e);
564         } catch (IOException e) {
565             e.printStackTrace(log.getOutStream());
566             throw new Failure("Caught exception while starting listening debugee VM:\n\t" + e);
567         };
568 
569         String[] cmdLineArgs = makeCommandLineArgs(classToExecute, address);
570         String javaCmdLine = makeCommandLineString(classToExecute, address, "\"");
571 
572         display("Starting remote java process:\n\t" + javaCmdLine);
573         Debugee debugee = startRemoteDebugee(cmdLineArgs);
574 
575         display("Waiting for connection from debugee");
576         VirtualMachine vm;
577         IOException ioe = null;
578         for (int i = 0; i < CONNECT_TRIES; i++) {
579             try {
580                 vm = connector.accept(arguments);
581                 connector.stopListening(arguments);
582                 display("Debugee attached");
583                 debugee.setupVM(vm);
584                 return debugee;
585             } catch (IOException e) {
586                 display("Attempt #" + i + " to listen debugee VM failed:\n\t" + e);
587                 ioe = e;
588                 if (debugee.terminated()) {
589                     throw new Failure("Unable to connect to debuggee VM: VM process is terminated");
590                 }
591                 try {
592                     Thread.currentThread().sleep(CONNECT_TRY_DELAY);
593                 } catch (InterruptedException ie) {
594                     ie.printStackTrace(log.getOutStream());
595                     throw new Failure("Thread interrupted while pausing connection attempts:\n\t"
596                                     + ie);
597                 }
598             } catch (IllegalConnectorArgumentsException e) {
599                 e.printStackTrace(log.getOutStream());
600                 throw new TestBug("Wrong connector arguments used to listen debuggee VM:\n\t" + e);
601             }
602         }
603         throw new Failure("Unable to connect to debugee VM after " + CONNECT_TRIES
604                         + " tries:\n\t" + ioe);
605     }
606 
607     // -------------------------------------------------- //
608 
609     /**
610      * Prompt to manually launch debugee VM and connect to it using
611      * <code>AttachingConnector</code>.
612      */
manualLaunchAndAttachDebugee(VirtualMachineManager vmm, String classToExecute, String classPath)613     private Debugee manualLaunchAndAttachDebugee (VirtualMachineManager vmm,
614                                                     String classToExecute,
615                                                     String classPath) {
616         display("Finding connector: " + argumentHandler.getConnectorName() );
617         AttachingConnector connector =
618             (AttachingConnector) findConnector(argumentHandler.getConnectorName(),
619                                                 vmm.attachingConnectors());
620         Map<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> arguments = setupAttachingConnector(connector, classToExecute, classPath);
621 
622         String address = makeTransportAddress();
623         String javaCmdLine = makeCommandLineString(classToExecute, address, "\"");
624 
625         display("Starting manual java process:\n\t" + javaCmdLine);
626         ManualLaunchedDebugee debugee = startManualDebugee(javaCmdLine);
627 
628         VirtualMachine vm;
629         try {
630             display("Attaching to debugee");
631             vm = connector.attach(arguments);
632         } catch (IllegalConnectorArgumentsException e) {
633             e.printStackTrace(log.getOutStream());
634             throw new TestBug("Wrong connector arguments used to attach to debuggee VM:\n\t" + e);
635         } catch (IOException e) {
636             e.printStackTrace(log.getOutStream());
637             throw new Failure("Caught exception while attaching to debugee VM:\n\t" + e);
638         };
639         display("Debugee attached");
640 
641         debugee.setupVM(vm);
642         return debugee;
643     }
644 
645     /**
646      * Prompt to manually launch debugee VM and connect to it using
647      * <code>ListeningConnector</code>.
648      */
manualLaunchAndListenDebugee(VirtualMachineManager vmm, String classToExecute, String classPath)649     private Debugee manualLaunchAndListenDebugee (VirtualMachineManager vmm,
650                                                     String classToExecute,
651                                                     String classPath) {
652         display("Finding connector: " + argumentHandler.getConnectorName() );
653         ListeningConnector connector =
654             (ListeningConnector) findConnector(argumentHandler.getConnectorName(),
655                                                 vmm.listeningConnectors());
656         Map<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> arguments = setupListeningConnector(connector, classToExecute, classPath);
657 
658         VirtualMachine vm;
659         try {
660             display("Listening for connection from debugee");
661             String address = connector.startListening(arguments);
662             String javaCmdLine = makeCommandLineString(classToExecute, address, "\"");
663             display("Starting manual java process:\n\t" + javaCmdLine);
664             ManualLaunchedDebugee debugee = startManualDebugee(javaCmdLine);
665             display("Waiting for connection from debugee");
666             vm = connector.accept(arguments);
667             display("Debugee attached");
668             connector.stopListening(arguments);
669             debugee.setupVM(vm);
670             return debugee;
671         } catch (IllegalConnectorArgumentsException e) {
672             e.printStackTrace(log.getOutStream());
673             throw new TestBug("Wrong connector arguments used to listen debuggee VM:\n\t" + e);
674         } catch (IOException e) {
675             e.printStackTrace(log.getOutStream());
676             throw new Failure("Caught exception while listening to debugee VM:\n\t" + e);
677         }
678     }
679 
680     // -------------------------------------------------- //
681 
682     /**
683      * Make proper arguments for LaunchingConnector.
684      */
setupLaunchingConnector(LaunchingConnector connector, String classToExecute, String classPath)685     private Map<String,? extends Argument> setupLaunchingConnector(LaunchingConnector connector,
686                                                 String classToExecute,
687                                                 String classPath) {
688         display("LaunchingConnector:");
689         display("    name: " + connector.name());
690         display("    description: " + connector.description());
691         display("    transport: " + connector.transport());
692 
693         Hashtable<String,? extends Argument> arguments = new Hashtable<String,Argument>(connector.defaultArguments());
694 
695         Connector.Argument arg;
696 
697         arg = (Connector.StringArgument) arguments.get("quote");
698         String quote = "\0";
699         arg.setValue(quote);
700 
701         String[] rawArgs = argumentHandler.getRawArguments();
702         if (Platform.isWindows()) {
703             // " has to be escaped on windows
704             rawArgs = Arrays.stream(rawArgs)
705                             .map(s -> s.replace("\"", "\\\""))
706                             .toArray(String[]::new);
707         }
708 
709         String cmdline = classToExecute + " " + ArgumentHandler.joinArguments(rawArgs, quote);
710 
711         arg = (Connector.StringArgument) arguments.get("main");
712         arg.setValue(cmdline);
713 
714         if (! argumentHandler.willDebugeeSuspended()) {
715             Connector.BooleanArgument barg = (Connector.BooleanArgument) arguments.get("suspend");
716             barg.setValue(true);
717         }
718 
719 /*
720         if (! argumentHandler.isJVMDIStrictMode()) {
721             arg = (Connector.StringArgument) arguments.get("options");
722             arg.setValue("strict=y");
723         }
724  */
725 
726         if (! argumentHandler.isDefaultDebugeeJavaHome()) {
727             arg = (Connector.StringArgument) arguments.get("home");
728             arg.setValue(argumentHandler.getDebugeeJavaHome());
729         }
730 
731         if (! argumentHandler.isDefaultLaunchExecName()) {
732             arg = (Connector.StringArgument) arguments.get("vmexec");
733             arg.setValue(argumentHandler.getLaunchExecName());
734         }
735 
736         String vmArgs = "";
737 
738         String vmUserArgs = argumentHandler.getLaunchOptions();
739 
740         if (vmUserArgs != null) {
741             vmArgs = vmUserArgs;
742         }
743 
744 /*
745         if (classPath != null) {
746             vmArgs += " -classpath " + quote + classPath + quote;
747         }
748  */
749 
750         if (vmArgs.length() > 0) {
751             arg = (Connector.StringArgument) arguments.get("options");
752             arg.setValue(vmArgs);
753         }
754 
755         display("Connector arguments:");
756         Iterator iterator = arguments.values().iterator();
757         while (iterator.hasNext()) {
758             display("    " + iterator.next());
759         }
760         return arguments;
761     }
762 
763     /**
764      * Make proper arguments for RawLaunchingConnector.
765      */
setupRawLaunchingConnector(LaunchingConnector connector, String classToExecute, String classPath)766     private Map<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> setupRawLaunchingConnector(LaunchingConnector connector,
767                                                 String classToExecute,
768                                                 String classPath) {
769         display("RawLaunchingConnector:");
770         display("    name: " + connector.name());
771         display("    description: " + connector.description());
772         display("    transport: " + connector.transport());
773 
774         Hashtable<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> arguments = new Hashtable<java.lang.String, com.sun.jdi.connect.Connector.Argument>(connector.defaultArguments());
775 
776         String connectorAddress;
777         String vmAddress;
778 
779         if (argumentHandler.isSocketTransport()) {
780             connectorAddress = argumentHandler.getTransportPort();
781             vmAddress = argumentHandler.getDebugeeHost()
782                         + ":" + argumentHandler.getTransportPort();
783         } else if (argumentHandler.isShmemTransport() ) {
784             connectorAddress = argumentHandler.getTransportSharedName();
785             vmAddress=connectorAddress;
786         } else {
787             throw new TestBug("Undefined transport type for AttachingConnector");
788         }
789 
790         Connector.Argument arg;
791 
792         arg = (Connector.StringArgument) arguments.get("quote");
793         String quote = arg.value();
794 
795         String javaCmdLine = makeCommandLineString(classToExecute, quote);
796 
797         arg = (Connector.StringArgument) arguments.get("command");
798         arg.setValue(javaCmdLine);
799 
800         arg = (Connector.StringArgument) arguments.get("address");
801         arg.setValue(connectorAddress);
802 
803         display("Connector arguments:");
804         Iterator iterator = arguments.values().iterator();
805         while (iterator.hasNext()) {
806             display("    " + iterator.next());
807         }
808         return arguments;
809     }
810 
811     /**
812      * Make proper arguments for AttachingConnector.
813      */
setupAttachingConnector(AttachingConnector connector, String classToExecute, String classPath)814     private Map<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> setupAttachingConnector(AttachingConnector connector,
815                                                 String classToExecute,
816                                                 String classPath) {
817         display("AttachingConnector:");
818         display("    name: " + connector.name());
819         display("    description: " + connector.description());
820         display("    transport: " + connector.transport());
821 
822         Hashtable<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> arguments = new Hashtable<java.lang.String,com.sun.jdi.connect.Connector.Argument>(connector.defaultArguments());
823 
824         Connector.Argument arg;
825         if (argumentHandler.isSocketTransport()) {
826             arg = (Connector.StringArgument) arguments.get("hostname");
827             arg.setValue(argumentHandler.getDebugeeHost());
828             Connector.IntegerArgument iarg = (Connector.IntegerArgument) arguments.get("port");
829             iarg.setValue(argumentHandler.getTransportPortNumber());
830         } else {
831             arg = (Connector.StringArgument) arguments.get("name");
832             arg.setValue(argumentHandler.getTransportSharedName());
833         }
834 
835         display("Connector arguments:");
836         Iterator iterator = arguments.values().iterator();
837         while (iterator.hasNext()) {
838             display("    " + iterator.next());
839         }
840         return arguments;
841     }
842 
843     /**
844      * Make proper arguments for ListeningConnector.
845      */
setupListeningConnector(ListeningConnector connector, String classToExecute, String classPath)846     private Map<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> setupListeningConnector(ListeningConnector connector,
847                                                 String classToExecute,
848                                                 String classPath) {
849         display("ListeningConnector:");
850         display("    name: " + connector.name());
851         display("    description: " + connector.description());
852         display("    transport: " + connector.transport());
853 
854         Hashtable<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> arguments = new Hashtable<java.lang.String,com.sun.jdi.connect.Connector.Argument>(connector.defaultArguments());
855 
856         Connector.Argument arg;
857         if (argumentHandler.isSocketTransport()) {
858             if (!argumentHandler.isTransportAddressDynamic()) {
859                 int port = argumentHandler.getTransportPortNumber();
860                 Connector.IntegerArgument iarg = (Connector.IntegerArgument) arguments.get("port");
861                 iarg.setValue(port);
862             }
863         } else {
864             String sharedName = argumentHandler.getTransportSharedName();
865             arg = (Connector.StringArgument) arguments.get("name");
866             arg.setValue(sharedName);
867         }
868 
869         display("Connector arguments:");
870         Iterator iterator = arguments.values().iterator();
871         while (iterator.hasNext()) {
872             display("    " + iterator.next());
873         }
874         return arguments;
875     }
876 
877     // -------------------------------------------------- //
878 
879     /**
880      * Find connector by name from given connectors list.
881      */
findConnector(String connectorName, List connectors)882     private Connector findConnector(String connectorName, List connectors) {
883         Iterator iter = connectors.iterator();
884 
885         while (iter.hasNext()) {
886             Connector connector = (Connector) iter.next();
887             if (connector.name().equals(connectorName)) {
888                 return connector;
889             }
890         }
891         throw new Failure("JDI connector not found: " + connectorName);
892     }
893 
894     // -------------------------------------------------- //
895 
896     /**
897      * Launch local debuggee process with specified command line arguments
898      * and make initial <code>Debugee</code> mirror.
899      */
startLocalDebugee(String[] cmdArgs)900     protected Debugee startLocalDebugee(String[] cmdArgs) {
901         Process process = null;
902 
903         try {
904             process = launchProcess(cmdArgs);
905         } catch (IOException e) {
906             e.printStackTrace(log.getOutStream());
907             throw new Failure("Caught exception while launching local debuggee VM process:\n\t"
908                             + e);
909         }
910 
911         return makeLocalDebugee(process);
912     }
913 
914     /**
915      * Launch remote debuggee process with specified command line arguments
916      * and make initial <code>Debugee</code> mirror.
917      */
startRemoteDebugee(String[] cmdArgs)918     protected RemoteLaunchedDebugee startRemoteDebugee(String[] cmdArgs) {
919         try {
920             launchRemoteProcess(cmdArgs);
921         } catch (IOException e) {
922             e.printStackTrace(log.getOutStream());
923             throw new Failure("Caught exception while launching remote debuggee VM process:\n\t"
924                             + e);
925         }
926 
927         RemoteLaunchedDebugee debugee = new RemoteLaunchedDebugee(this);
928 
929         Finalizer finalizer = new Finalizer(debugee);
930         finalizer.activate();
931 
932         return debugee;
933     }
934 
935     /**
936      * Launch manual debuggee process with specified command line arguments
937      * and make initial <code>Debugee</code> mirror.
938      */
startManualDebugee(String cmd)939     protected ManualLaunchedDebugee startManualDebugee(String cmd) {
940         ManualLaunchedDebugee debugee = new ManualLaunchedDebugee(this);
941         debugee.launchDebugee(cmd);
942 
943         Finalizer finalizer = new Finalizer(debugee);
944         finalizer.activate();
945 
946         return debugee;
947     }
948 
readVMStartExceptionOutput(VMStartException e, PrintStream log)949     public static String readVMStartExceptionOutput(VMStartException e, PrintStream log) {
950         StringBuffer msg = new StringBuffer();
951         try (InputStream is = e.process().getInputStream()) {
952             msg.append("\tstdout: ").append(new String(readAllBytes(is))).append('\n');
953         } catch (IOException e1) {
954             log.println("Could not read normal output from launched process:" + e1);
955         }
956         try (InputStream is = e.process().getErrorStream()) {
957             msg.append("\tstderr: ").append(new String(readAllBytes(is)));
958         } catch (IOException e1) {
959             log.println("Could not read error output from launched process:" + e1);
960         }
961         return msg.toString();
962     }
963 
964     /**
965      * Copied from the JDK 9 implementation in InputStream.java
966      */
readAllBytes(InputStream is)967     private static byte[] readAllBytes(InputStream is) throws IOException {
968         final int DEFAULT_BUFFER_SIZE = 8192;
969         final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
970 
971         byte[] buf = new byte[DEFAULT_BUFFER_SIZE];
972         int capacity = buf.length;
973         int nread = 0;
974         int n;
975         for (;;) {
976             // read to EOF which may read more or less than initial buffer size
977             while ((n = is.read(buf, nread, capacity - nread)) > 0)
978                 nread += n;
979 
980             // if the last call to read returned -1, then we're done
981             if (n < 0)
982                 break;
983 
984             // need to allocate a larger buffer
985             if (capacity <= MAX_BUFFER_SIZE - capacity) {
986                 capacity = capacity << 1;
987             } else {
988                 if (capacity == MAX_BUFFER_SIZE)
989                     throw new OutOfMemoryError("Required array size too large");
990                 capacity = MAX_BUFFER_SIZE;
991             }
992             buf = Arrays.copyOf(buf, capacity);
993         }
994         return (capacity == nread) ? buf : Arrays.copyOf(buf, nread);
995     }
996 
997 }
998 
999 
1000 /**
1001  * Mirror of locally launched debugee.
1002  */
1003 final class LocalLaunchedDebugee extends Debugee {
1004 
1005     /** Enwrap the locally started VM process. */
LocalLaunchedDebugee(Process process, Binder binder)1006     public LocalLaunchedDebugee (Process process, Binder binder) {
1007         super(binder);
1008         this.process = process;
1009         checkTermination = true;
1010     }
1011 
1012     // ---------------------------------------------- //
1013 
1014     /** Return exit status of the debugee VM. */
getStatus()1015     public int getStatus () {
1016         return process.exitValue();
1017     }
1018 
1019     /** Check whether the debugee VM has been terminated. */
terminated()1020     public boolean terminated () {
1021         if (process == null)
1022             return true;
1023 
1024         try {
1025             int value = process.exitValue();
1026             return true;
1027         } catch (IllegalThreadStateException e) {
1028             return false;
1029         }
1030     }
1031 
1032     // ---------------------------------------------- //
1033 
1034     /** Kill the debugee VM. */
killDebugee()1035     protected void killDebugee () {
1036         super.killDebugee();
1037         if (!terminated()) {
1038             log.display("Killing debugee VM process");
1039             process.destroy();
1040         }
1041     }
1042 
1043     /** Wait until the debugee VM shutdown or crash. */
waitForDebugee()1044     protected int waitForDebugee () throws InterruptedException {
1045         int code = process.waitFor();
1046         return code;
1047     }
1048 
1049     /** Get a pipe to write to the debugee's stdin stream. */
getInPipe()1050     protected OutputStream getInPipe () {
1051         return process.getOutputStream();
1052     }
1053 
1054     /** Get a pipe to read the debugee's stdout stream. */
getOutPipe()1055     protected InputStream getOutPipe () {
1056         return process.getInputStream();
1057     }
1058 
1059     /** Get a pipe to read the debugee's stderr stream. */
getErrPipe()1060     protected InputStream getErrPipe () {
1061         return process.getErrorStream();
1062     }
1063 }
1064 
1065 
1066 /**
1067  * Mirror of remotely launched debugee.
1068  */
1069 final class RemoteLaunchedDebugee extends Debugee {
1070 
1071     /** Enwrap the remotely started VM process. */
RemoteLaunchedDebugee(Binder binder)1072     public RemoteLaunchedDebugee (Binder binder) {
1073         super(binder);
1074     }
1075 
1076     // ---------------------------------------------- //
1077 
1078     /** Return exit status of the debugee VM. */
getStatus()1079     public int getStatus () {
1080         return binder.getRemoteProcessStatus();
1081     }
1082 
1083     /** Check whether the debugee VM has been terminated. */
terminated()1084     public boolean terminated () {
1085         return binder.isRemoteProcessTerminated();
1086     }
1087 
1088     // ---------------------------------------------- //
1089 
1090     /** Kill the debugee VM. */
killDebugee()1091     protected void killDebugee () {
1092         super.killDebugee();
1093         if (!terminated()) {
1094             binder.killRemoteProcess();
1095         }
1096     }
1097 
1098     /** Wait until the debugee VM shutdown or crash. */
waitForDebugee()1099     protected int waitForDebugee () {
1100         return binder.waitForRemoteProcess();
1101     }
1102 
1103     /** Get a pipe to write to the debugee's stdin stream. */
getInPipe()1104     protected OutputStream getInPipe () {
1105         return null;
1106     }
1107 
1108     /** Get a pipe to read the debugee's stdout stream. */
getOutPipe()1109     protected InputStream getOutPipe () {
1110         return null;
1111     }
1112 
1113     /** Get a pipe to read the debugee's stderr stream. */
getErrPipe()1114     protected InputStream getErrPipe () {
1115         return null;
1116     }
1117 
redirectStdout(OutputStream out)1118     public void redirectStdout(OutputStream out) {
1119     }
1120 
redirectStdout(Log log, String prefix)1121     public void redirectStdout(Log log, String prefix) {
1122     }
1123 
redirectStderr(OutputStream out)1124     public void redirectStderr(OutputStream out) {
1125     }
1126 
redirectStderr(Log log, String prefix)1127     public void redirectStderr(Log log, String prefix) {
1128     }
1129 }
1130 
1131 
1132 /**
1133  * Mirror of manually launched debugee.
1134  */
1135 final class ManualLaunchedDebugee extends Debugee {
1136     /** Enwrap the manually started VM process. */
ManualLaunchedDebugee(Binder binder)1137     public ManualLaunchedDebugee (Binder binder) {
1138         super(binder);
1139         makeInputReader();
1140     }
1141 
1142     // ---------------------------------------------- //
1143 
1144     private int exitCode = 0;
1145     private boolean finished = false;
1146     private static BufferedReader bin = null;
1147 
launchDebugee(String commandLine)1148     public void launchDebugee(String commandLine) {
1149         makeInputReader();
1150 
1151         putMessage("Launch target VM using such command line:\n"
1152                     + commandLine);
1153         String answer = askQuestion("Has the VM successfully started? (yes/no)", "yes");
1154         for ( ; ; ) {
1155             if (answer.equals("yes"))
1156                 break;
1157             if (answer.equals("no"))
1158                 throw new Failure ("Unable to manually launch debugee VM");
1159             answer = askQuestion("Wrong answer. Please type yes or no", "yes");
1160         }
1161     }
1162 
makeInputReader()1163     private static void makeInputReader() {
1164         if (bin == null) {
1165             bin = new BufferedReader(new InputStreamReader(System.in));
1166         }
1167     }
1168 
destroyInputReader()1169     private static void destroyInputReader() {
1170         if (bin != null) {
1171             try {
1172                 bin.close();
1173             } catch (IOException e) {
1174 //                e.printStackTrace(log.getOutStream());
1175                 throw new Failure("Caught exception while closing input stream:\n\t" + e);
1176             }
1177             bin = null;
1178         }
1179     }
1180 
putMessage(String msg)1181     private static void putMessage(String msg) {
1182         System.out.println("\n>>> " + msg);
1183     }
1184 
askQuestion(String question, String defaultAnswer)1185     private static String askQuestion(String question, String defaultAnswer) {
1186         try {
1187             System.out.print("\n>>> " + question);
1188             System.out.print(" [" + defaultAnswer + "] ");
1189             System.out.flush();
1190             String answer = bin.readLine();
1191             if (answer.equals(""))
1192                 return defaultAnswer;
1193             return answer;
1194         } catch (IOException e) {
1195 //            e.printStackTrace(log.getOutStream());
1196             throw new Failure("Caught exception while reading answer:\n\t" + e);
1197         }
1198     }
1199 
1200     /** Return exit status of the debugee VM. */
getStatus()1201     public int getStatus () {
1202         if (! finished) {
1203             throw new Failure("Unable to get status of debugee VM: process still alive");
1204         }
1205         return exitCode;
1206     }
1207 
1208     /** Check whether the debugee VM has been terminated. */
terminated()1209     public boolean terminated () {
1210         return finished;
1211     }
1212 
1213     // ---------------------------------------------- //
1214 
1215     /** Kill the debugee VM. */
killDebugee()1216     protected void killDebugee () {
1217         super.killDebugee();
1218         if (!terminated()) {
1219             putMessage("Kill launched VM");
1220             String answer = askQuestion("Has the VM successfully terminated? (yes/no)", "yes");
1221             for ( ; ; ) {
1222                 if (answer.equals("yes")) {
1223                     finished = true;
1224                     break;
1225                 }
1226                 if (answer.equals("no"))
1227                     throw new Failure ("Unable to manually kill debugee VM");
1228                 answer = askQuestion("Wrong answer. Please type yes or no", "yes");
1229             }
1230         }
1231     }
1232 
1233     /** Wait until the debugee VM shutdown or crash. */
waitForDebugee()1234     protected int waitForDebugee () {
1235         putMessage("Wait for launched VM to exit.");
1236         String answer = askQuestion("What is VM exit code?", "95");
1237         for ( ; ; ) {
1238             try {
1239                 exitCode = Integer.parseInt(answer);
1240                 break;
1241             } catch (NumberFormatException e) {
1242                 answer = askQuestion("Wrong answer. Please type integer value", "95");
1243             }
1244         }
1245         finished = true;
1246         return exitCode;
1247     }
1248 
1249     /** Get a pipe to write to the debugee's stdin stream. */
getInPipe()1250     protected OutputStream getInPipe () {
1251         return null;
1252     }
1253 
1254     /** Get a pipe to read the debugee's stdout stream. */
getOutPipe()1255     protected InputStream getOutPipe () {
1256         return null;
1257     }
1258 
1259     /** Get a pipe to read the debugee's stderr stream. */
getErrPipe()1260     protected InputStream getErrPipe () {
1261         return null;
1262     }
1263 
redirectStdout(OutputStream out)1264     public void redirectStdout(OutputStream out) {
1265     }
1266 
redirectStdout(Log log, String prefix)1267     public void redirectStdout(Log log, String prefix) {
1268     }
1269 
redirectStderr(OutputStream out)1270     public void redirectStderr(OutputStream out) {
1271     }
1272 
redirectStderr(Log log, String prefix)1273     public void redirectStderr(Log log, String prefix) {
1274     }
1275 
close()1276     public void close() {
1277         destroyInputReader();
1278         super.close();
1279     }
1280 }
1281