1 /*
2  * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  *
23  */
24 
25 import com.sun.jdi.*;
26 import com.sun.jdi.connect.*;
27 import com.sun.jdi.request.EventRequestManager;
28 
29 import java.util.*;
30 import java.io.*;
31 
32 
33 /**
34  * Manages a VM conection for the JDI test framework.
35  */
36 class VMConnection {
37     private VirtualMachine vm;
38     private Process process = null;
39     private int outputCompleteCount = 0;
40 
41     private final Connector connector;
42     private final Map connectorArgs;
43     private final int traceFlags;
44 
45     /**
46      * Return a String containing VM Options to pass to the debugee
47      * or an empty string if there are none.
48      * These are read from the first non-comment line
49      * in file test/com/sun/jdi/@debuggeeVMOptions.
50      */
getDebuggeeVMOptions()51     static public String getDebuggeeVMOptions() {
52 
53         // When we run under jtreg, test.src contains the pathname of
54         // the test/com/sun/jdi dir.
55         BufferedReader reader;
56         final String filename = "@debuggeeVMOptions";
57         String srcDir = System.getProperty("test.src");
58 
59         if (srcDir == null) {
60           srcDir = System.getProperty("user.dir");
61         }
62         srcDir = srcDir + File.separator;
63 
64         File myDir = new File(srcDir);
65 
66         File myFile = new File(myDir, filename);
67         if (!myFile.canRead()) {
68             try {
69                 // We have some subdirs of test/com/sun/jdi so in case we
70                 // are in one of them, look in our parent dir for the file.
71                 myFile = new File(myDir.getCanonicalFile().getParent(),
72                                   filename);
73                 if (!myFile.canRead()) {
74                     return "";
75                 }
76             } catch (IOException ee) {
77                 System.out.println("-- Error 1 trying to access file " +
78                                    myFile.getPath() + ": " + ee);
79                 return "";
80             }
81         }
82         String wholePath = myFile.getPath();
83         try {
84             reader = new BufferedReader(new FileReader(myFile));
85         } catch (FileNotFoundException ee) {
86             System.out.println("-- Error 2 trying to access file " +
87                                wholePath + ": " + ee);
88             return "";
89         }
90 
91         String line;
92         String retVal = "";
93         while (true) {
94             try {
95                 line = reader.readLine();
96             } catch (IOException ee) {
97                 System.out.println("-- Error reading options from file " +
98                                    wholePath + ": " + ee);
99                 break;
100             }
101             if (line == null) {
102                 System.out.println("-- No debuggee VM options found in file " +
103                                    wholePath);
104                 break;
105             }
106             line = line.trim();
107             if (line.length() != 0 && !line.startsWith("#")) {
108                 System.out.println("-- Added debuggeeVM options from file " +
109                                    wholePath + ": " + line);
110                 retVal = line;
111                 break;
112             }
113             // Else, read he next line.
114         }
115         try {
116             reader.close();
117         } catch (IOException ee) {
118         }
119         return retVal;
120     }
121 
findConnector(String name)122     private Connector findConnector(String name) {
123         List connectors = Bootstrap.virtualMachineManager().allConnectors();
124         Iterator iter = connectors.iterator();
125         while (iter.hasNext()) {
126             Connector connector = (Connector)iter.next();
127             if (connector.name().equals(name)) {
128                 return connector;
129             }
130         }
131         return null;
132     }
133 
parseConnectorArgs(Connector connector, String argString)134     private Map parseConnectorArgs(Connector connector, String argString) {
135         StringTokenizer tokenizer = new StringTokenizer(argString, ",");
136         Map arguments = connector.defaultArguments();
137 
138         while (tokenizer.hasMoreTokens()) {
139             String token = tokenizer.nextToken();
140             int index = token.indexOf('=');
141             if (index == -1) {
142                 throw new IllegalArgumentException("Illegal connector argument: " +
143                                                    token);
144             }
145             String name = token.substring(0, index);
146             String value = token.substring(index + 1);
147             Connector.Argument argument = (Connector.Argument)arguments.get(name);
148             if (argument == null) {
149                 throw new IllegalArgumentException("Argument " + name +
150                                                "is not defined for connector: " +
151                                                connector.name());
152             }
153             argument.setValue(value);
154         }
155         return arguments;
156     }
157 
VMConnection(String connectSpec, int traceFlags)158     VMConnection(String connectSpec, int traceFlags) {
159         String nameString;
160         String argString;
161         int index = connectSpec.indexOf(':');
162         if (index == -1) {
163             nameString = connectSpec;
164             argString = "";
165         } else {
166             nameString = connectSpec.substring(0, index);
167             argString = connectSpec.substring(index + 1);
168         }
169 
170         connector = findConnector(nameString);
171         if (connector == null) {
172             throw new IllegalArgumentException("No connector named: " +
173                                                nameString);
174         }
175 
176         connectorArgs = parseConnectorArgs(connector, argString);
177         this.traceFlags = traceFlags;
178     }
179 
open()180     synchronized VirtualMachine open() {
181         if (connector instanceof LaunchingConnector) {
182             vm = launchTarget();
183         } else if (connector instanceof AttachingConnector) {
184             vm = attachTarget();
185         } else if (connector instanceof ListeningConnector) {
186             vm = listenTarget();
187         } else {
188             throw new InternalError("Invalid connect type");
189         }
190         vm.setDebugTraceMode(traceFlags);
191         System.out.println("JVM version:" + vm.version());
192         System.out.println("JDI version: " + Bootstrap.virtualMachineManager().majorInterfaceVersion() +
193                            "." + Bootstrap.virtualMachineManager().minorInterfaceVersion());
194         System.out.println("JVM description: " + vm.description());
195 
196         return vm;
197     }
198 
setConnectorArg(String name, String value)199     boolean setConnectorArg(String name, String value) {
200         /*
201          * Too late if the connection already made
202          */
203         if (vm != null) {
204             return false;
205         }
206 
207         Connector.Argument argument = (Connector.Argument)connectorArgs.get(name);
208         if (argument == null) {
209             return false;
210         }
211         argument.setValue(value);
212         return true;
213     }
214 
connectorArg(String name)215     String connectorArg(String name) {
216         Connector.Argument argument = (Connector.Argument)connectorArgs.get(name);
217         if (argument == null) {
218             return "";
219         }
220         return argument.value();
221     }
222 
vm()223     public synchronized VirtualMachine vm() {
224         if (vm == null) {
225             throw new InternalError("VM not connected");
226         } else {
227             return vm;
228         }
229     }
230 
isOpen()231     boolean isOpen() {
232         return (vm != null);
233     }
234 
isLaunch()235     boolean isLaunch() {
236         return (connector instanceof LaunchingConnector);
237     }
238 
connector()239     Connector connector() {
240         return connector;
241     }
242 
isListen()243     boolean isListen() {
244         return (connector instanceof ListeningConnector);
245     }
246 
isAttach()247     boolean isAttach() {
248         return (connector instanceof AttachingConnector);
249     }
250 
notifyOutputComplete()251     private synchronized void notifyOutputComplete() {
252         outputCompleteCount++;
253         notifyAll();
254     }
255 
waitOutputComplete()256     private synchronized void waitOutputComplete() {
257         // Wait for stderr and stdout
258         if (process != null) {
259             while (outputCompleteCount < 2) {
260                 try {wait();} catch (InterruptedException e) {}
261             }
262         }
263     }
264 
disposeVM()265     public void disposeVM() {
266         try {
267             if (vm != null) {
268                 vm.dispose();
269                 vm = null;
270             }
271         } finally {
272             if (process != null) {
273                 process.destroy();
274                 process = null;
275             }
276             waitOutputComplete();
277         }
278     }
279 
dumpStream(InputStream stream)280     private void dumpStream(InputStream stream) throws IOException {
281         PrintStream outStream = System.out;
282         BufferedReader in =
283             new BufferedReader(new InputStreamReader(stream));
284         String line;
285         while ((line = in.readLine()) != null) {
286             outStream.println(line);
287         }
288     }
289 
290     /**
291      *  Create a Thread that will retrieve and display any output.
292      *  Needs to be high priority, else debugger may exit before
293      *  it can be displayed.
294      */
displayRemoteOutput(final InputStream stream)295     private void displayRemoteOutput(final InputStream stream) {
296         Thread thr = new Thread("output reader") {
297             public void run() {
298                 try {
299                     dumpStream(stream);
300                 } catch (IOException ex) {
301                     System.err.println("IOException reading output of child java interpreter:"
302                                        + ex.getMessage());
303                 } finally {
304                     notifyOutputComplete();
305                 }
306             }
307         };
308         thr.setPriority(Thread.MAX_PRIORITY-1);
309         thr.start();
310     }
311 
dumpFailedLaunchInfo(Process process)312     private void dumpFailedLaunchInfo(Process process) {
313         try {
314             dumpStream(process.getErrorStream());
315             dumpStream(process.getInputStream());
316         } catch (IOException e) {
317             System.err.println("Unable to display process output: " +
318                                e.getMessage());
319         }
320     }
321 
322     /* launch child target vm */
launchTarget()323     private VirtualMachine launchTarget() {
324         LaunchingConnector launcher = (LaunchingConnector)connector;
325         try {
326             VirtualMachine vm = launcher.launch(connectorArgs);
327             process = vm.process();
328             displayRemoteOutput(process.getErrorStream());
329             displayRemoteOutput(process.getInputStream());
330             return vm;
331         } catch (IOException ioe) {
332             ioe.printStackTrace();
333             System.err.println("\n Unable to launch target VM.");
334         } catch (IllegalConnectorArgumentsException icae) {
335             icae.printStackTrace();
336             System.err.println("\n Internal debugger error.");
337         } catch (VMStartException vmse) {
338             System.err.println(vmse.getMessage() + "\n");
339             dumpFailedLaunchInfo(vmse.process());
340             System.err.println("\n Target VM failed to initialize.");
341         }
342         return null; // Shuts up the compiler
343     }
344 
345     /* attach to running target vm */
attachTarget()346     private VirtualMachine attachTarget() {
347         AttachingConnector attacher = (AttachingConnector)connector;
348         try {
349             return attacher.attach(connectorArgs);
350         } catch (IOException ioe) {
351             ioe.printStackTrace();
352             System.err.println("\n Unable to attach to target VM.");
353         } catch (IllegalConnectorArgumentsException icae) {
354             icae.printStackTrace();
355             System.err.println("\n Internal debugger error.");
356         }
357         return null; // Shuts up the compiler
358     }
359 
360     /* listen for connection from target vm */
listenTarget()361     private VirtualMachine listenTarget() {
362         ListeningConnector listener = (ListeningConnector)connector;
363         try {
364             String retAddress = listener.startListening(connectorArgs);
365             System.out.println("Listening at address: " + retAddress);
366             vm = listener.accept(connectorArgs);
367             listener.stopListening(connectorArgs);
368             return vm;
369         } catch (IOException ioe) {
370             ioe.printStackTrace();
371             System.err.println("\n Unable to attach to target VM.");
372         } catch (IllegalConnectorArgumentsException icae) {
373             icae.printStackTrace();
374             System.err.println("\n Internal debugger error.");
375         }
376         return null; // Shuts up the compiler
377     }
378 }
379