2e0bda002d
Reviewed-by: sspitsyn, mseledtsov
454 lines
14 KiB
Java
454 lines
14 KiB
Java
/*
|
|
* 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();
|
|
}
|
|
}
|