blob: f370bf46b8a60c716a1c168cac3ea13c9a898b6d [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1999-2006 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
20 * CA 95054 USA or visit www.sun.com if you need additional information or
21 * have any questions.
22 */
23
24import com.sun.jdi.*;
25import com.sun.jdi.connect.*;
26import com.sun.jdi.request.EventRequestManager;
27
28import java.util.*;
29import java.io.*;
30
31
32/**
33 * Manages a VM conection for the JDI test framework.
34 */
35class VMConnection {
36 private VirtualMachine vm;
37 private Process process = null;
38 private int outputCompleteCount = 0;
39
40 private final Connector connector;
41 private final Map connectorArgs;
42 private final int traceFlags;
43
44 /**
45 * Return a String containing VM Options to pass to the debugee
46 * or an empty string if there are none.
47 * These are read from the first non-comment line
48 * in file @debuggeeVMOptions in the test.classes dir
49 */
50 static public String getDebuggeeVMOptions() {
51 String retVal = "";
52
53 // When we run under jtreg, test.classes contains the pathname of
54 // the dir in which the .class files will be placed.
55 BufferedReader reader;
56 String testClasses = System.getProperty("test.classes");
57 if (testClasses == null) {
58 return retVal;
59 }
60 File myFile = new File(testClasses, "@debuggeeVMOptions");
61
62 if (!myFile.canRead()) {
63 // Not there - look in parent (in case we are in a subdir)
64 myFile = new File(testClasses);
65 String parentDir = myFile.getParent();
66 if (parentDir != null) {
67 myFile = new File(parentDir, "@debuggeeVMOptions");
68 if (!myFile.canRead()) {
69 return retVal;
70 }
71 }
72 }
73 String wholePath = myFile.getPath();
74 try {
75 reader = new BufferedReader(new FileReader(myFile));
76 } catch (FileNotFoundException ee) {
77 System.out.println("-- Error 2 trying to access file " +
78 wholePath + ": " + ee);
79 return retVal;
80 }
81
82 String line;
83 while (true) {
84 try {
85 line = reader.readLine();
86 } catch (IOException ee) {
87 System.out.println("-- Error reading options from file " +
88 wholePath + ": " + ee);
89 break;
90 }
91 if (line == null) {
92 System.out.println("-- No debuggee VM options found in file " +
93 wholePath);
94 break;
95 }
96 line = line.trim();
97 if (line.length() != 0 && !line.startsWith("#")) {
98 System.out.println("-- Added debuggeeVM options from file " +
99 wholePath + ": " + line);
100 retVal = line;
101 break;
102 }
103 // Else, read he next line.
104 }
105 try {
106 reader.close();
107 } catch (IOException ee) {
108 }
109 return retVal;
110 }
111
112 static public String[] insertDebuggeeVMOptions(String[] cmdLine) {
113 String opts = getDebuggeeVMOptions();
114 if (opts.equals("")) {
115 return cmdLine;
116 }
117 // Insert the options at position 1. Blanks in args are not allowed!
118 String[] v1 = opts.split(" ");
119 String[] retVal = new String[cmdLine.length + v1.length];
120 retVal[0] = cmdLine[0];
121 System.arraycopy(v1, 0, retVal, 1, v1.length);
122 System.arraycopy(cmdLine, 1, retVal, v1.length + 1, cmdLine.length - 1);
123 return retVal;
124 }
125
126
127 private Connector findConnector(String name) {
128 List connectors = Bootstrap.virtualMachineManager().allConnectors();
129 Iterator iter = connectors.iterator();
130 while (iter.hasNext()) {
131 Connector connector = (Connector)iter.next();
132 if (connector.name().equals(name)) {
133 return connector;
134 }
135 }
136 return null;
137 }
138
139 private Map parseConnectorArgs(Connector connector, String argString) {
140 StringTokenizer tokenizer = new StringTokenizer(argString, ",");
141 Map arguments = connector.defaultArguments();
142
143 while (tokenizer.hasMoreTokens()) {
144 String token = tokenizer.nextToken();
145 int index = token.indexOf('=');
146 if (index == -1) {
147 throw new IllegalArgumentException("Illegal connector argument: " +
148 token);
149 }
150 String name = token.substring(0, index);
151 String value = token.substring(index + 1);
152 Connector.Argument argument = (Connector.Argument)arguments.get(name);
153 if (argument == null) {
154 throw new IllegalArgumentException("Argument " + name +
155 "is not defined for connector: " +
156 connector.name());
157 }
158 argument.setValue(value);
159 }
160 return arguments;
161 }
162
163 VMConnection(String connectSpec, int traceFlags) {
164 String nameString;
165 String argString;
166 int index = connectSpec.indexOf(':');
167 if (index == -1) {
168 nameString = connectSpec;
169 argString = "";
170 } else {
171 nameString = connectSpec.substring(0, index);
172 argString = connectSpec.substring(index + 1);
173 }
174
175 connector = findConnector(nameString);
176 if (connector == null) {
177 throw new IllegalArgumentException("No connector named: " +
178 nameString);
179 }
180
181 connectorArgs = parseConnectorArgs(connector, argString);
182 this.traceFlags = traceFlags;
183 }
184
185 synchronized VirtualMachine open() {
186 if (connector instanceof LaunchingConnector) {
187 vm = launchTarget();
188 } else if (connector instanceof AttachingConnector) {
189 vm = attachTarget();
190 } else if (connector instanceof ListeningConnector) {
191 vm = listenTarget();
192 } else {
193 throw new InternalError("Invalid connect type");
194 }
195 vm.setDebugTraceMode(traceFlags);
196 System.out.println("JVM version:" + vm.version());
197 System.out.println("JDI version: " + Bootstrap.virtualMachineManager().majorInterfaceVersion() +
198 "." + Bootstrap.virtualMachineManager().minorInterfaceVersion());
199 System.out.println("JVM description: " + vm.description());
200
201 return vm;
202 }
203
204 boolean setConnectorArg(String name, String value) {
205 /*
206 * Too late if the connection already made
207 */
208 if (vm != null) {
209 return false;
210 }
211
212 Connector.Argument argument = (Connector.Argument)connectorArgs.get(name);
213 if (argument == null) {
214 return false;
215 }
216 argument.setValue(value);
217 return true;
218 }
219
220 String connectorArg(String name) {
221 Connector.Argument argument = (Connector.Argument)connectorArgs.get(name);
222 if (argument == null) {
223 return "";
224 }
225 return argument.value();
226 }
227
228 public synchronized VirtualMachine vm() {
229 if (vm == null) {
230 throw new InternalError("VM not connected");
231 } else {
232 return vm;
233 }
234 }
235
236 boolean isOpen() {
237 return (vm != null);
238 }
239
240 boolean isLaunch() {
241 return (connector instanceof LaunchingConnector);
242 }
243
244 Connector connector() {
245 return connector;
246 }
247
248 boolean isListen() {
249 return (connector instanceof ListeningConnector);
250 }
251
252 boolean isAttach() {
253 return (connector instanceof AttachingConnector);
254 }
255
256 private synchronized void notifyOutputComplete() {
257 outputCompleteCount++;
258 notifyAll();
259 }
260
261 private synchronized void waitOutputComplete() {
262 // Wait for stderr and stdout
263 if (process != null) {
264 while (outputCompleteCount < 2) {
265 try {wait();} catch (InterruptedException e) {}
266 }
267 }
268 }
269
270 public void disposeVM() {
271 try {
272 if (vm != null) {
273 vm.dispose();
274 vm = null;
275 }
276 } finally {
277 if (process != null) {
278 process.destroy();
279 process = null;
280 }
281 waitOutputComplete();
282 }
283 }
284
285 private void dumpStream(InputStream stream) throws IOException {
286 PrintStream outStream = System.out;
287 BufferedReader in =
288 new BufferedReader(new InputStreamReader(stream));
289 String line;
290 while(true){
291 try{
292 line = in.readLine();
293 if( line == null ){
294 break;
295 }
296 outStream.println(line);
297 }
298 catch(IOException ieo){
299 /**
300 * IOException with "Bad file number..." can happen
301 * when the debuggee process is destroyed. Ignore such exception.
302 *
303 */
304 String s = ieo.getMessage();
305 if( s.startsWith("Bad file number") ){
306 break;
307 }
308 throw ieo;
309 }
310 catch(NullPointerException npe){
311 throw new IOException("Bug 4728096 in Java io may cause in.readLine() to throw a NULL pointer exception");
312 }
313 }
314 }
315
316 /**
317 * Create a Thread that will retrieve and display any output.
318 * Needs to be high priority, else debugger may exit before
319 * it can be displayed.
320 */
321 private void displayRemoteOutput(final InputStream stream) {
322 Thread thr = new Thread("output reader") {
323 public void run() {
324 try {
325 dumpStream(stream);
326 } catch (IOException ex) {
327 System.err.println("IOException reading output of child java interpreter:"
328 + ex.getMessage());
329 } finally {
330 notifyOutputComplete();
331 }
332 }
333 };
334 thr.setPriority(Thread.MAX_PRIORITY-1);
335 thr.start();
336 }
337
338 private void dumpFailedLaunchInfo(Process process) {
339 try {
340 dumpStream(process.getErrorStream());
341 dumpStream(process.getInputStream());
342 } catch (IOException e) {
343 System.err.println("Unable to display process output: " +
344 e.getMessage());
345 }
346 }
347
348 /* launch child target vm */
349 private VirtualMachine launchTarget() {
350 LaunchingConnector launcher = (LaunchingConnector)connector;
351 try {
352 VirtualMachine vm = launcher.launch(connectorArgs);
353 process = vm.process();
354 displayRemoteOutput(process.getErrorStream());
355 displayRemoteOutput(process.getInputStream());
356 return vm;
357 } catch (IOException ioe) {
358 ioe.printStackTrace();
359 System.err.println("\n Unable to launch target VM.");
360 } catch (IllegalConnectorArgumentsException icae) {
361 icae.printStackTrace();
362 System.err.println("\n Internal debugger error.");
363 } catch (VMStartException vmse) {
364 System.err.println(vmse.getMessage() + "\n");
365 dumpFailedLaunchInfo(vmse.process());
366 System.err.println("\n Target VM failed to initialize.");
367 }
368 return null; // Shuts up the compiler
369 }
370
371 /* attach to running target vm */
372 private VirtualMachine attachTarget() {
373 AttachingConnector attacher = (AttachingConnector)connector;
374 try {
375 return attacher.attach(connectorArgs);
376 } catch (IOException ioe) {
377 ioe.printStackTrace();
378 System.err.println("\n Unable to attach to target VM.");
379 } catch (IllegalConnectorArgumentsException icae) {
380 icae.printStackTrace();
381 System.err.println("\n Internal debugger error.");
382 }
383 return null; // Shuts up the compiler
384 }
385
386 /* listen for connection from target vm */
387 private VirtualMachine listenTarget() {
388 ListeningConnector listener = (ListeningConnector)connector;
389 try {
390 String retAddress = listener.startListening(connectorArgs);
391 System.out.println("Listening at address: " + retAddress);
392 vm = listener.accept(connectorArgs);
393 listener.stopListening(connectorArgs);
394 return vm;
395 } catch (IOException ioe) {
396 ioe.printStackTrace();
397 System.err.println("\n Unable to attach to target VM.");
398 } catch (IllegalConnectorArgumentsException icae) {
399 icae.printStackTrace();
400 System.err.println("\n Internal debugger error.");
401 }
402 return null; // Shuts up the compiler
403 }
404}