VMConnection.java revision 411:c678b172284b
187628Sdwmalone/* 287289Sdwmalone * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. 31590Srgrimes * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 41590Srgrimes * 51590Srgrimes * This code is free software; you can redistribute it and/or modify it 6201386Sed * under the terms of the GNU General Public License version 2 only, as 7201386Sed * published by the Free Software Foundation. 81590Srgrimes * 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} 405