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