blob: 2424a9a56edb96ba17cab0b5c0ec81fb69ba05bd [file] [log] [blame]
/*
* Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package nsk.share.jdwp;
import nsk.share.*;
import nsk.share.jpda.*;
import java.io.*;
/**
* This class provides debugger with connection to debugee VM
* using JDWP protocol.
* <p>
* This class provides abilities to launch and bind to debugee VM
* as described for base <code>DebugeeBinder</code> class,
* using raw JDWP protocol.
* <p>
* When <code>Binder</code> is asked to bind to debugee by invoking
* <code>bindToBebugee()</code> method it launches process
* with debugee VM and makes connection to it using JDWP transport
* corresponding to value of command line options <code>-connector</code>
* and <code>-transport</code>.
* After debugee is launched and connection is established
* <code>Binder</code> constructs <code>Debugee</code> object,
* that provides abilities to interact with debugee VM.
*
* @see Debugee
* @see DebugeeBinder
*/
final public class Binder extends DebugeeBinder {
/**
* Default message prefix for <code>Binder</code> object.
*/
public static final String LOG_PREFIX = "binder> ";
/**
* Get version string.
*/
public static String getVersion () {
return "@(#)Binder.java %I% %E%";
}
// -------------------------------------------------- //
/**
* Handler of command line arguments.
*/
private ArgumentHandler argumentHandler = null;
/**
* Return <code>argumentHandler</code> of this binder.
*/
public ArgumentHandler getArgumentHandler() {
return argumentHandler;
}
// -------------------------------------------------- //
/**
* Make new <code>Binder</code> object with specified
* <code>argumentHandler</code> and <code>log</code>.
*/
public Binder (ArgumentHandler argumentHandler, Log log) {
super(argumentHandler, log);
this.argumentHandler = argumentHandler;
}
// -------------------------------------------------- //
/**
* Start debugee VM and establish JDWP connection to it.
*/
public Debugee bindToDebugee (String classToExecute) {
Debugee debugee = null;
prepareForPipeConnection(argumentHandler);
if (argumentHandler.isLaunchedRemotely()) {
connectToBindServer(classToExecute);
debugee = launchDebugee(classToExecute);
} else {
debugee = launchDebugee(classToExecute);
debugee.redirectOutput(log);
}
Finalizer finalizer = new Finalizer(debugee);
finalizer.activate();
Transport transport = debugee.connect();
return debugee;
}
/**
* Launch debugee VM for specified class.
*/
public Debugee launchDebugee (String classToExecute) {
try {
if (argumentHandler.isLaunchedLocally()) {
LocalLaunchedDebugee debugee = new LocalLaunchedDebugee(this);
String address = debugee.prepareTransport(argumentHandler);
if (address == null)
address = makeTransportAddress();
String[] argsArray = makeCommandLineArgs(classToExecute, address);
debugee.launch(argsArray);
return debugee;
}
if (argumentHandler.isLaunchedRemotely()) {
RemoteLaunchedDebugee debugee = new RemoteLaunchedDebugee(this);
String address = debugee.prepareTransport(argumentHandler);
if (address == null)
address = makeTransportAddress();
String[] argsArray = makeCommandLineArgs(classToExecute, address);
debugee.launch(argsArray);
return debugee;
}
if (argumentHandler.isLaunchedManually()) {
ManualLaunchedDebugee debugee = new ManualLaunchedDebugee(this);
String address = debugee.prepareTransport(argumentHandler);
if (address == null)
address = makeTransportAddress();
String cmdLine = makeCommandLineString(classToExecute, address, "\"");
debugee.launch(cmdLine);
return debugee;
}
throw new TestBug("Unexpected launching mode: "
+ argumentHandler.getLaunchMode());
} catch (IOException e) {
e.printStackTrace(log.getOutStream());
throw new Failure("Caught exception while launching debugee:\n\t" + e);
}
}
}
/**
* Mirror of locally launched debugee.
*/
final class LocalLaunchedDebugee extends Debugee {
/** Enwrap the existing <code>VM</code> mirror. */
public LocalLaunchedDebugee (Binder binder) {
super(binder);
checkTermination = true;
}
// ---------------------------------------------- //
public void launch(String[] args) throws IOException {
String cmdLine = ArgumentHandler.joinArguments(args, "\"");
display("Starting java process:\n" + cmdLine);
process = binder.launchProcess(args);
}
/** Return exit status of the debugee VM. */
public int getStatus () {
return process.exitValue();
}
/** Check whether the debugee VM has been terminated. */
public boolean terminated () {
if (process == null)
return true;
try {
int value = process.exitValue();
return true;
} catch (IllegalThreadStateException e) {
return false;
}
}
// ---------------------------------------------- //
/** Kill the debugee VM. */
protected void killDebugee () {
super.killDebugee();
if (!terminated()) {
log.display("Killing debugee VM process");
process.destroy();
}
}
/** Wait until the debugee VM shutdown or crash. */
protected int waitForDebugee () throws InterruptedException {
return process.waitFor();
}
/** Get a pipe to write to the debugee's stdin stream. */
protected OutputStream getInPipe () {
return process.getOutputStream();
}
/** Get a pipe to read the debugee's stdout stream. */
protected InputStream getOutPipe () {
return process.getInputStream();
}
/** Get a pipe to read the debugee's stderr stream. */
protected InputStream getErrPipe () {
return process.getErrorStream();
}
}
/**
* Mirror of remotely launched debugee.
*/
final class RemoteLaunchedDebugee extends Debugee {
/** Enwrap the existing <code>VM</code> mirror. */
public RemoteLaunchedDebugee (Binder binder) {
super(binder);
}
// ---------------------------------------------- //
public void launch(String[] args) throws IOException {
String cmdLine = ArgumentHandler.joinArguments(args, "\"");
display("Starting remote java process:\n" + cmdLine);
binder.launchRemoteProcess(args);
}
/** Return exit status of the debugee VM. */
public int getStatus () {
return binder.getRemoteProcessStatus();
}
/** Check whether the debugee VM has been terminated. */
public boolean terminated () {
return binder.isRemoteProcessTerminated();
}
// ---------------------------------------------- //
/** Kill the debugee VM. */
protected void killDebugee () {
super.killDebugee();
if (!terminated()) {
log.display("Killing debugee VM process");
binder.killRemoteProcess();
}
}
/** Wait until the debugee VM shutdown or crash. */
protected int waitForDebugee () {
return binder.waitForRemoteProcess();
}
/** Get a pipe to write to the debugee's stdin stream. */
protected OutputStream getInPipe () {
return null;
}
/** Get a pipe to read the debugee's stdout stream. */
protected InputStream getOutPipe () {
return null;
}
/** Get a pipe to read the debugee's stderr stream. */
protected InputStream getErrPipe () {
return null;
}
public void redirectStdout(OutputStream out) {
}
public void redirectStdout(Log log, String prefix) {
}
public void redirectStderr(OutputStream out) {
}
public void redirectStderr(Log log, String prefix) {
}
}
/**
* Mirror of manually launched debugee.
*/
final class ManualLaunchedDebugee extends Debugee {
private int exitCode = 0;
private boolean finished = false;
private static BufferedReader bin = new BufferedReader(new InputStreamReader(System.in));
/** Enwrap the existing <code>VM</code> mirror. */
public ManualLaunchedDebugee (Binder binder) {
super(binder);
}
// ---------------------------------------------- //
public void launch(String commandLine) throws IOException {
putMessage("Launch target VM using such command line:\n"
+ commandLine);
String answer = askQuestion("Has the VM successfully started? (yes/no)", "yes");
for ( ; ; ) {
if (answer.equals("yes"))
break;
if (answer.equals("no"))
throw new Failure ("Unable to manually launch debugee VM");
answer = askQuestion("Wrong answer. Please type yes or no", "yes");
}
}
private void putMessage(String msg) {
System.out.println("\n>>> " + msg);
}
private String askQuestion(String question, String defaultAnswer) {
try {
System.out.print("\n>>> " + question);
System.out.print(" [" + defaultAnswer + "] ");
System.out.flush();
String answer = bin.readLine();
if (answer.equals(""))
return defaultAnswer;
return answer;
} catch (IOException e) {
e.printStackTrace(log.getOutStream());
throw new Failure("Caught exception while reading answer:\n\t" + e);
}
}
/** Return exit status of the debugee VM. */
public int getStatus () {
if (! terminated()) {
throw new Failure("Unable to get status of debugee VM: process still alive");
}
return exitCode;
}
/** Check whether the debugee VM has been terminated. */
public boolean terminated () {
if(! finished) {
String answer = askQuestion("Has the VM exited?", "no");
for ( ; ; ) {
if (answer.equals("no"))
return false;
if (answer.equals("yes")) {
finished = true;
waitForDebugee();
break;
}
answer = askQuestion("Wrong answer. Please type yes or no", "yes");
}
}
return finished;
}
// ---------------------------------------------- //
/** Kill the debugee VM. */
protected void killDebugee () {
super.killDebugee();
if (!terminated()) {
putMessage("Kill launched VM");
String answer = askQuestion("Has the VM successfully terminated? (yes/no)", "yes");
for ( ; ; ) {
if (answer.equals("yes")) {
finished = true;
break;
}
if (answer.equals("no"))
throw new Failure ("Unable to manually kill debugee VM");
answer = askQuestion("Wrong answer. Please type yes or no", "yes");
}
}
}
/** Wait until the debugee VM shutdown or crash. */
protected int waitForDebugee () {
putMessage("Wait for launched VM to exit.");
String answer = askQuestion("What is VM exit code?", "95");
for ( ; ; ) {
try {
exitCode = Integer.parseInt(answer);
break;
} catch (NumberFormatException e) {
answer = askQuestion("Wrong answer. Please type integer value", "95");
}
}
finished = true;
return exitCode;
}
/** Get a pipe to write to the debugee's stdin stream. */
protected OutputStream getInPipe () {
return null;
}
/** Get a pipe to read the debugee's stdout stream. */
protected InputStream getOutPipe () {
return null;
}
/** Get a pipe to read the debugee's stderr stream. */
protected InputStream getErrPipe () {
return null;
}
public void redirectStdout(OutputStream out) {
}
public void redirectStdout(Log log, String prefix) {
}
public void redirectStderr(OutputStream out) {
}
public void redirectStderr(Log log, String prefix) {
}
public void close() {
try {
bin.close();
} catch (IOException e) {
log.display("WARNING: Caught IOException while closing InputStream");
}
bin = null;
super.close();
}
}