0N/A/*
2362N/A * Copyright (c) 1999, 2008, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
0N/A * published by the Free Software Foundation.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Aimport com.sun.jdi.*;
0N/Aimport com.sun.jdi.connect.*;
0N/Aimport com.sun.jdi.request.EventRequestManager;
0N/A
0N/Aimport java.util.*;
0N/Aimport java.io.*;
0N/A
0N/A
0N/A/**
0N/A * Manages a VM conection for the JDI test framework.
0N/A */
0N/Aclass VMConnection {
0N/A private VirtualMachine vm;
0N/A private Process process = null;
0N/A private int outputCompleteCount = 0;
0N/A
0N/A private final Connector connector;
0N/A private final Map connectorArgs;
0N/A private final int traceFlags;
0N/A
0N/A /**
0N/A * Return a String containing VM Options to pass to the debugee
0N/A * or an empty string if there are none.
0N/A * These are read from the first non-comment line
0N/A * in file @debuggeeVMOptions in the test.classes dir
0N/A */
0N/A static public String getDebuggeeVMOptions() {
0N/A String retVal = "";
0N/A
0N/A // When we run under jtreg, test.classes contains the pathname of
0N/A // the dir in which the .class files will be placed.
0N/A BufferedReader reader;
0N/A String testClasses = System.getProperty("test.classes");
0N/A if (testClasses == null) {
0N/A return retVal;
0N/A }
1940N/A retVal += "-classpath " + testClasses + " ";
0N/A File myFile = new File(testClasses, "@debuggeeVMOptions");
0N/A
0N/A if (!myFile.canRead()) {
0N/A // Not there - look in parent (in case we are in a subdir)
0N/A myFile = new File(testClasses);
0N/A String parentDir = myFile.getParent();
0N/A if (parentDir != null) {
0N/A myFile = new File(parentDir, "@debuggeeVMOptions");
0N/A if (!myFile.canRead()) {
0N/A return retVal;
0N/A }
0N/A }
0N/A }
0N/A String wholePath = myFile.getPath();
0N/A try {
0N/A reader = new BufferedReader(new FileReader(myFile));
0N/A } catch (FileNotFoundException ee) {
0N/A System.out.println("-- Error 2 trying to access file " +
0N/A wholePath + ": " + ee);
0N/A return retVal;
0N/A }
0N/A
0N/A String line;
0N/A while (true) {
0N/A try {
0N/A line = reader.readLine();
0N/A } catch (IOException ee) {
0N/A System.out.println("-- Error reading options from file " +
0N/A wholePath + ": " + ee);
0N/A break;
0N/A }
0N/A if (line == null) {
0N/A System.out.println("-- No debuggee VM options found in file " +
0N/A wholePath);
0N/A break;
0N/A }
0N/A line = line.trim();
0N/A if (line.length() != 0 && !line.startsWith("#")) {
0N/A System.out.println("-- Added debuggeeVM options from file " +
0N/A wholePath + ": " + line);
1940N/A retVal += line;
0N/A break;
0N/A }
0N/A // Else, read he next line.
0N/A }
0N/A try {
0N/A reader.close();
0N/A } catch (IOException ee) {
0N/A }
0N/A return retVal;
0N/A }
0N/A
0N/A static public String[] insertDebuggeeVMOptions(String[] cmdLine) {
0N/A String opts = getDebuggeeVMOptions();
0N/A if (opts.equals("")) {
0N/A return cmdLine;
0N/A }
0N/A // Insert the options at position 1. Blanks in args are not allowed!
411N/A String[] v1 = opts.split(" +");
0N/A String[] retVal = new String[cmdLine.length + v1.length];
0N/A retVal[0] = cmdLine[0];
0N/A System.arraycopy(v1, 0, retVal, 1, v1.length);
0N/A System.arraycopy(cmdLine, 1, retVal, v1.length + 1, cmdLine.length - 1);
0N/A return retVal;
0N/A }
0N/A
0N/A
0N/A private Connector findConnector(String name) {
0N/A List connectors = Bootstrap.virtualMachineManager().allConnectors();
0N/A Iterator iter = connectors.iterator();
0N/A while (iter.hasNext()) {
0N/A Connector connector = (Connector)iter.next();
0N/A if (connector.name().equals(name)) {
0N/A return connector;
0N/A }
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A private Map parseConnectorArgs(Connector connector, String argString) {
0N/A StringTokenizer tokenizer = new StringTokenizer(argString, ",");
0N/A Map arguments = connector.defaultArguments();
0N/A
0N/A while (tokenizer.hasMoreTokens()) {
0N/A String token = tokenizer.nextToken();
0N/A int index = token.indexOf('=');
0N/A if (index == -1) {
0N/A throw new IllegalArgumentException("Illegal connector argument: " +
0N/A token);
0N/A }
0N/A String name = token.substring(0, index);
0N/A String value = token.substring(index + 1);
0N/A Connector.Argument argument = (Connector.Argument)arguments.get(name);
0N/A if (argument == null) {
0N/A throw new IllegalArgumentException("Argument " + name +
0N/A "is not defined for connector: " +
0N/A connector.name());
0N/A }
0N/A argument.setValue(value);
0N/A }
0N/A return arguments;
0N/A }
0N/A
0N/A VMConnection(String connectSpec, int traceFlags) {
0N/A String nameString;
0N/A String argString;
0N/A int index = connectSpec.indexOf(':');
0N/A if (index == -1) {
0N/A nameString = connectSpec;
0N/A argString = "";
0N/A } else {
0N/A nameString = connectSpec.substring(0, index);
0N/A argString = connectSpec.substring(index + 1);
0N/A }
0N/A
0N/A connector = findConnector(nameString);
0N/A if (connector == null) {
0N/A throw new IllegalArgumentException("No connector named: " +
0N/A nameString);
0N/A }
0N/A
0N/A connectorArgs = parseConnectorArgs(connector, argString);
0N/A this.traceFlags = traceFlags;
0N/A }
0N/A
0N/A synchronized VirtualMachine open() {
0N/A if (connector instanceof LaunchingConnector) {
0N/A vm = launchTarget();
0N/A } else if (connector instanceof AttachingConnector) {
0N/A vm = attachTarget();
0N/A } else if (connector instanceof ListeningConnector) {
0N/A vm = listenTarget();
0N/A } else {
0N/A throw new InternalError("Invalid connect type");
0N/A }
0N/A vm.setDebugTraceMode(traceFlags);
0N/A System.out.println("JVM version:" + vm.version());
0N/A System.out.println("JDI version: " + Bootstrap.virtualMachineManager().majorInterfaceVersion() +
0N/A "." + Bootstrap.virtualMachineManager().minorInterfaceVersion());
0N/A System.out.println("JVM description: " + vm.description());
0N/A
0N/A return vm;
0N/A }
0N/A
0N/A boolean setConnectorArg(String name, String value) {
0N/A /*
0N/A * Too late if the connection already made
0N/A */
0N/A if (vm != null) {
0N/A return false;
0N/A }
0N/A
0N/A Connector.Argument argument = (Connector.Argument)connectorArgs.get(name);
0N/A if (argument == null) {
0N/A return false;
0N/A }
0N/A argument.setValue(value);
0N/A return true;
0N/A }
0N/A
0N/A String connectorArg(String name) {
0N/A Connector.Argument argument = (Connector.Argument)connectorArgs.get(name);
0N/A if (argument == null) {
0N/A return "";
0N/A }
0N/A return argument.value();
0N/A }
0N/A
0N/A public synchronized VirtualMachine vm() {
0N/A if (vm == null) {
0N/A throw new InternalError("VM not connected");
0N/A } else {
0N/A return vm;
0N/A }
0N/A }
0N/A
0N/A boolean isOpen() {
0N/A return (vm != null);
0N/A }
0N/A
0N/A boolean isLaunch() {
0N/A return (connector instanceof LaunchingConnector);
0N/A }
0N/A
0N/A Connector connector() {
0N/A return connector;
0N/A }
0N/A
0N/A boolean isListen() {
0N/A return (connector instanceof ListeningConnector);
0N/A }
0N/A
0N/A boolean isAttach() {
0N/A return (connector instanceof AttachingConnector);
0N/A }
0N/A
0N/A private synchronized void notifyOutputComplete() {
0N/A outputCompleteCount++;
0N/A notifyAll();
0N/A }
0N/A
0N/A private synchronized void waitOutputComplete() {
0N/A // Wait for stderr and stdout
0N/A if (process != null) {
0N/A while (outputCompleteCount < 2) {
0N/A try {wait();} catch (InterruptedException e) {}
0N/A }
0N/A }
0N/A }
0N/A
0N/A public void disposeVM() {
0N/A try {
0N/A if (vm != null) {
0N/A vm.dispose();
0N/A vm = null;
0N/A }
0N/A } finally {
0N/A if (process != null) {
0N/A process.destroy();
0N/A process = null;
0N/A }
0N/A waitOutputComplete();
0N/A }
0N/A }
0N/A
0N/A private void dumpStream(InputStream stream) throws IOException {
0N/A PrintStream outStream = System.out;
0N/A BufferedReader in =
0N/A new BufferedReader(new InputStreamReader(stream));
0N/A String line;
0N/A while(true){
0N/A try{
0N/A line = in.readLine();
0N/A if( line == null ){
0N/A break;
0N/A }
0N/A outStream.println(line);
0N/A }
0N/A catch(IOException ieo){
0N/A /**
0N/A * IOException with "Bad file number..." can happen
0N/A * when the debuggee process is destroyed. Ignore such exception.
0N/A *
0N/A */
0N/A String s = ieo.getMessage();
0N/A if( s.startsWith("Bad file number") ){
0N/A break;
0N/A }
0N/A throw ieo;
0N/A }
0N/A catch(NullPointerException npe){
0N/A throw new IOException("Bug 4728096 in Java io may cause in.readLine() to throw a NULL pointer exception");
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Create a Thread that will retrieve and display any output.
0N/A * Needs to be high priority, else debugger may exit before
0N/A * it can be displayed.
0N/A */
0N/A private void displayRemoteOutput(final InputStream stream) {
0N/A Thread thr = new Thread("output reader") {
0N/A public void run() {
0N/A try {
0N/A dumpStream(stream);
0N/A } catch (IOException ex) {
0N/A System.err.println("IOException reading output of child java interpreter:"
0N/A + ex.getMessage());
0N/A } finally {
0N/A notifyOutputComplete();
0N/A }
0N/A }
0N/A };
0N/A thr.setPriority(Thread.MAX_PRIORITY-1);
0N/A thr.start();
0N/A }
0N/A
0N/A private void dumpFailedLaunchInfo(Process process) {
0N/A try {
0N/A dumpStream(process.getErrorStream());
0N/A dumpStream(process.getInputStream());
0N/A } catch (IOException e) {
0N/A System.err.println("Unable to display process output: " +
0N/A e.getMessage());
0N/A }
0N/A }
0N/A
0N/A /* launch child target vm */
0N/A private VirtualMachine launchTarget() {
0N/A LaunchingConnector launcher = (LaunchingConnector)connector;
0N/A try {
0N/A VirtualMachine vm = launcher.launch(connectorArgs);
0N/A process = vm.process();
0N/A displayRemoteOutput(process.getErrorStream());
0N/A displayRemoteOutput(process.getInputStream());
0N/A return vm;
0N/A } catch (IOException ioe) {
0N/A ioe.printStackTrace();
0N/A System.err.println("\n Unable to launch target VM.");
0N/A } catch (IllegalConnectorArgumentsException icae) {
0N/A icae.printStackTrace();
0N/A System.err.println("\n Internal debugger error.");
0N/A } catch (VMStartException vmse) {
0N/A System.err.println(vmse.getMessage() + "\n");
0N/A dumpFailedLaunchInfo(vmse.process());
0N/A System.err.println("\n Target VM failed to initialize.");
0N/A }
0N/A return null; // Shuts up the compiler
0N/A }
0N/A
0N/A /* attach to running target vm */
0N/A private VirtualMachine attachTarget() {
0N/A AttachingConnector attacher = (AttachingConnector)connector;
0N/A try {
0N/A return attacher.attach(connectorArgs);
0N/A } catch (IOException ioe) {
0N/A ioe.printStackTrace();
0N/A System.err.println("\n Unable to attach to target VM.");
0N/A } catch (IllegalConnectorArgumentsException icae) {
0N/A icae.printStackTrace();
0N/A System.err.println("\n Internal debugger error.");
0N/A }
0N/A return null; // Shuts up the compiler
0N/A }
0N/A
0N/A /* listen for connection from target vm */
0N/A private VirtualMachine listenTarget() {
0N/A ListeningConnector listener = (ListeningConnector)connector;
0N/A try {
0N/A String retAddress = listener.startListening(connectorArgs);
0N/A System.out.println("Listening at address: " + retAddress);
0N/A vm = listener.accept(connectorArgs);
0N/A listener.stopListening(connectorArgs);
0N/A return vm;
0N/A } catch (IOException ioe) {
0N/A ioe.printStackTrace();
0N/A System.err.println("\n Unable to attach to target VM.");
0N/A } catch (IllegalConnectorArgumentsException icae) {
0N/A icae.printStackTrace();
0N/A System.err.println("\n Internal debugger error.");
0N/A }
0N/A return null; // Shuts up the compiler
0N/A }
0N/A}