/*
* Copyright (c) 1999, 2008, 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.
*/
import com.sun.jdi.*;
import com.sun.jdi.connect.*;
import com.sun.jdi.request.EventRequestManager;
import java.util.*;
import java.io.*;
/**
* Manages a VM conection for the JDI test framework.
*/
class VMConnection {
private VirtualMachine vm;
private Process process = null;
private int outputCompleteCount = 0;
private final Connector connector;
private final Map connectorArgs;
private final int traceFlags;
/**
* Return a String containing VM Options to pass to the debugee
* or an empty string if there are none.
* These are read from the first non-comment line
* in file @debuggeeVMOptions in the test.classes dir
*/
static public String getDebuggeeVMOptions() {
String retVal = "";
// When we run under jtreg, test.classes contains the pathname of
// the dir in which the .class files will be placed.
BufferedReader reader;
String testClasses = System.getProperty("test.classes");
if (testClasses == null) {
return retVal;
}
retVal += "-classpath " + testClasses + " ";
File myFile = new File(testClasses, "@debuggeeVMOptions");
if (!myFile.canRead()) {
// Not there - look in parent (in case we are in a subdir)
myFile = new File(testClasses);
String parentDir = myFile.getParent();
if (parentDir != null) {
myFile = new File(parentDir, "@debuggeeVMOptions");
if (!myFile.canRead()) {
return retVal;
}
}
}
String wholePath = myFile.getPath();
try {
reader = new BufferedReader(new FileReader(myFile));
} catch (FileNotFoundException ee) {
System.out.println("-- Error 2 trying to access file " +
wholePath + ": " + ee);
return retVal;
}
String line;
while (true) {
try {
line = reader.readLine();
} catch (IOException ee) {
System.out.println("-- Error reading options from file " +
wholePath + ": " + ee);
break;
}
if (line == null) {
System.out.println("-- No debuggee VM options found in file " +
wholePath);
break;
}
line = line.trim();
if (line.length() != 0 && !line.startsWith("#")) {
System.out.println("-- Added debuggeeVM options from file " +
wholePath + ": " + line);
retVal += line;
break;
}
// Else, read he next line.
}
try {
reader.close();
} catch (IOException ee) {
}
return retVal;
}
static public String[] insertDebuggeeVMOptions(String[] cmdLine) {
String opts = getDebuggeeVMOptions();
if (opts.equals("")) {
return cmdLine;
}
// Insert the options at position 1. Blanks in args are not allowed!
String[] v1 = opts.split(" +");
String[] retVal = new String[cmdLine.length + v1.length];
retVal[0] = cmdLine[0];
System.arraycopy(v1, 0, retVal, 1, v1.length);
System.arraycopy(cmdLine, 1, retVal, v1.length + 1, cmdLine.length - 1);
return retVal;
}
private Connector findConnector(String name) {
List connectors = Bootstrap.virtualMachineManager().allConnectors();
Iterator iter = connectors.iterator();
while (iter.hasNext()) {
Connector connector = (Connector)iter.next();
if (connector.name().equals(name)) {
return connector;
}
}
return null;
}
private Map parseConnectorArgs(Connector connector, String argString) {
StringTokenizer tokenizer = new StringTokenizer(argString, ",");
Map arguments = connector.defaultArguments();
while (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken();
int index = token.indexOf('=');
if (index == -1) {
throw new IllegalArgumentException("Illegal connector argument: " +
token);
}
String name = token.substring(0, index);
String value = token.substring(index + 1);
Connector.Argument argument = (Connector.Argument)arguments.get(name);
if (argument == null) {
throw new IllegalArgumentException("Argument " + name +
"is not defined for connector: " +
connector.name());
}
argument.setValue(value);
}
return arguments;
}
VMConnection(String connectSpec, int traceFlags) {
String nameString;
String argString;
int index = connectSpec.indexOf(':');
if (index == -1) {
nameString = connectSpec;
argString = "";
} else {
nameString = connectSpec.substring(0, index);
argString = connectSpec.substring(index + 1);
}
connector = findConnector(nameString);
if (connector == null) {
throw new IllegalArgumentException("No connector named: " +
nameString);
}
connectorArgs = parseConnectorArgs(connector, argString);
this.traceFlags = traceFlags;
}
synchronized VirtualMachine open() {
if (connector instanceof LaunchingConnector) {
vm = launchTarget();
} else if (connector instanceof AttachingConnector) {
vm = attachTarget();
} else if (connector instanceof ListeningConnector) {
vm = listenTarget();
} else {
throw new InternalError("Invalid connect type");
}
vm.setDebugTraceMode(traceFlags);
System.out.println("JVM version:" + vm.version());
System.out.println("JDI version: " + Bootstrap.virtualMachineManager().majorInterfaceVersion() +
"." + Bootstrap.virtualMachineManager().minorInterfaceVersion());
System.out.println("JVM description: " + vm.description());
return vm;
}
boolean setConnectorArg(String name, String value) {
/*
* Too late if the connection already made
*/
if (vm != null) {
return false;
}
Connector.Argument argument = (Connector.Argument)connectorArgs.get(name);
if (argument == null) {
return false;
}
argument.setValue(value);
return true;
}
String connectorArg(String name) {
Connector.Argument argument = (Connector.Argument)connectorArgs.get(name);
if (argument == null) {
return "";
}
return argument.value();
}
public synchronized VirtualMachine vm() {
if (vm == null) {
throw new InternalError("VM not connected");
} else {
return vm;
}
}
boolean isOpen() {
return (vm != null);
}
boolean isLaunch() {
return (connector instanceof LaunchingConnector);
}
Connector connector() {
return connector;
}
boolean isListen() {
return (connector instanceof ListeningConnector);
}
boolean isAttach() {
return (connector instanceof AttachingConnector);
}
private synchronized void notifyOutputComplete() {
outputCompleteCount++;
notifyAll();
}
private synchronized void waitOutputComplete() {
// Wait for stderr and stdout
if (process != null) {
while (outputCompleteCount < 2) {
try {wait();} catch (InterruptedException e) {}
}
}
}
public void disposeVM() {
try {
if (vm != null) {
vm.dispose();
vm = null;
}
} finally {
if (process != null) {
process.destroy();
process = null;
}
waitOutputComplete();
}
}
private void dumpStream(InputStream stream) throws IOException {
PrintStream outStream = System.out;
BufferedReader in =
new BufferedReader(new InputStreamReader(stream));
String line;
while(true){
try{
line = in.readLine();
if( line == null ){
break;
}
outStream.println(line);
}
catch(IOException ieo){
/**
* IOException with "Bad file number..." can happen
* when the debuggee process is destroyed. Ignore such exception.
*
*/
String s = ieo.getMessage();
if( s.startsWith("Bad file number") ){
break;
}
throw ieo;
}
catch(NullPointerException npe){
throw new IOException("Bug 4728096 in Java io may cause in.readLine() to throw a NULL pointer exception");
}
}
}
/**
* Create a Thread that will retrieve and display any output.
* Needs to be high priority, else debugger may exit before
* it can be displayed.
*/
private void displayRemoteOutput(final InputStream stream) {
Thread thr = new Thread("output reader") {
public void run() {
try {
dumpStream(stream);
} catch (IOException ex) {
System.err.println("IOException reading output of child java interpreter:"
+ ex.getMessage());
} finally {
notifyOutputComplete();
}
}
};
thr.setPriority(Thread.MAX_PRIORITY-1);
thr.start();
}
private void dumpFailedLaunchInfo(Process process) {
try {
dumpStream(process.getErrorStream());
dumpStream(process.getInputStream());
} catch (IOException e) {
System.err.println("Unable to display process output: " +
e.getMessage());
}
}
/* launch child target vm */
private VirtualMachine launchTarget() {
LaunchingConnector launcher = (LaunchingConnector)connector;
try {
VirtualMachine vm = launcher.launch(connectorArgs);
process = vm.process();
displayRemoteOutput(process.getErrorStream());
displayRemoteOutput(process.getInputStream());
return vm;
} catch (IOException ioe) {
ioe.printStackTrace();
System.err.println("\n Unable to launch target VM.");
} catch (IllegalConnectorArgumentsException icae) {
icae.printStackTrace();
System.err.println("\n Internal debugger error.");
} catch (VMStartException vmse) {
System.err.println(vmse.getMessage() + "\n");
dumpFailedLaunchInfo(vmse.process());
System.err.println("\n Target VM failed to initialize.");
}
return null; // Shuts up the compiler
}
/* attach to running target vm */
private VirtualMachine attachTarget() {
AttachingConnector attacher = (AttachingConnector)connector;
try {
return attacher.attach(connectorArgs);
} catch (IOException ioe) {
ioe.printStackTrace();
System.err.println("\n Unable to attach to target VM.");
} catch (IllegalConnectorArgumentsException icae) {
icae.printStackTrace();
System.err.println("\n Internal debugger error.");
}
return null; // Shuts up the compiler
}
/* listen for connection from target vm */
private VirtualMachine listenTarget() {
ListeningConnector listener = (ListeningConnector)connector;
try {
String retAddress = listener.startListening(connectorArgs);
System.out.println("Listening at address: " + retAddress);
vm = listener.accept(connectorArgs);
listener.stopListening(connectorArgs);
return vm;
} catch (IOException ioe) {
ioe.printStackTrace();
System.err.println("\n Unable to attach to target VM.");
} catch (IllegalConnectorArgumentsException icae) {
icae.printStackTrace();
System.err.println("\n Internal debugger error.");
}
return null; // Shuts up the compiler
}
}