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