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