/* * 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. *

* This class provides abilities to launch and bind to debugee VM * as described for base DebugeeBinder class, * using raw JDWP protocol. *

* When Binder is asked to bind to debugee by invoking * bindToBebugee() method it launches process * with debugee VM and makes connection to it using JDWP transport * corresponding to value of command line options -connector * and -transport. * After debugee is launched and connection is established * Binder constructs Debugee object, * that provides abilities to interact with debugee VM. * * @see Debugee * @see DebugeeBinder */ final public class Binder extends DebugeeBinder { /** * Default message prefix for Binder 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 argumentHandler of this binder. */ public ArgumentHandler getArgumentHandler() { return argumentHandler; } // -------------------------------------------------- // /** * Make new Binder object with specified * argumentHandler and log. */ 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 VM 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 VM 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 VM 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(); } }