Showing the changes to java/pkg/com/sun/tools/example/debug/tty/VMConnection.java (1 of 18352 files). Show all
Unified · Split
| @@ -0,0 +1,376 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1998, 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. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package com.sun.tools.example.debug.tty; | ||
| 27 | |||
| 28 | import com.sun.jdi.*; | ||
| 29 | import com.sun.jdi.connect.*; | ||
| 30 | import com.sun.jdi.request.EventRequestManager; | ||
| 31 | import com.sun.jdi.request.ThreadStartRequest; | ||
| 32 | import com.sun.jdi.request.ThreadDeathRequest; | ||
| 33 | |||
| 34 | import java.util.*; | ||
| 35 | import java.util.regex.*; | ||
| 36 | import java.io.*; | ||
| 37 | |||
| 38 | class VMConnection { | ||
| 39 | |||
| 40 | private VirtualMachine vm; | ||
| 41 | private Process process = null; | ||
| 42 | private int outputCompleteCount = 0; | ||
| 43 | |||
| 44 | private final Connector connector; | ||
| 45 | private final Map<String, com.sun.jdi.connect.Connector.Argument> connectorArgs; | ||
| 46 | private final int traceFlags; | ||
| 47 | |||
| 48 | synchronized void notifyOutputComplete() { | ||
| 49 | outputCompleteCount++; | ||
| 50 | notifyAll(); | ||
| 51 | } | ||
| 52 | |||
| 53 | synchronized void waitOutputComplete() { | ||
| 54 | // Wait for stderr and stdout | ||
| 55 | if (process != null) { | ||
| 56 | while (outputCompleteCount < 2) { | ||
| 57 | try {wait();} catch (InterruptedException e) {} | ||
| 58 | } | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | private Connector findConnector(String name) { | ||
| 63 | for (Connector connector : | ||
| 64 | Bootstrap.virtualMachineManager().allConnectors()) { | ||
| 65 | if (connector.name().equals(name)) { | ||
| 66 | return connector; | ||
| 67 | } | ||
| 68 | } | ||
| 69 | return null; | ||
| 70 | } | ||
| 71 | |||
| 72 | private Map <String, com.sun.jdi.connect.Connector.Argument> parseConnectorArgs(Connector connector, String argString) { | ||
| 73 | Map<String, com.sun.jdi.connect.Connector.Argument> arguments = connector.defaultArguments(); | ||
| 74 | |||
| 75 | /* | ||
| 76 | * We are parsing strings of the form: | ||
| 77 | * name1=value1,[name2=value2,...] | ||
| 78 | * However, the value1...valuen substrings may contain | ||
| 79 | * embedded comma(s), so make provision for quoting inside | ||
| 80 | * the value substrings. (Bug ID 4285874) | ||
| 81 | */ | ||
| 82 | String regexPattern = | ||
| 83 | "(quote=[^,]+,)|" + // special case for quote=., | ||
| 84 | "(\\w+=)" + // name= | ||
| 85 | "(((\"[^\"]*\")|" + // ( "l , ue" | ||
| 86 | "('[^']*')|" + // 'l , ue' | ||
| 87 | "([^,'\"]+))+,)"; // v a l u e )+ , | ||
| 88 | Pattern p = Pattern.compile(regexPattern); | ||
| 89 | Matcher m = p.matcher(argString); | ||
| 90 | while (m.find()) { | ||
| 91 | int startPosition = m.start(); | ||
| 92 | int endPosition = m.end(); | ||
| 93 | if (startPosition > 0) { | ||
| 94 | /* | ||
| 95 | * It is an error if parsing skips over any part of argString. | ||
| 96 | */ | ||
| 97 | throw new IllegalArgumentException | ||
| 98 | (MessageOutput.format("Illegal connector argument", | ||
| 99 | argString)); | ||
| 100 | } | ||
| 101 | |||
| 102 | String token = argString.substring(startPosition, endPosition); | ||
| 103 | int index = token.indexOf('='); | ||
| 104 | String name = token.substring(0, index); | ||
| 105 | String value = token.substring(index + 1, | ||
| 106 | token.length() - 1); // Remove comma delimiter | ||
| 107 | |||
| 108 | Connector.Argument argument = arguments.get(name); | ||
| 109 | if (argument == null) { | ||
| 110 | throw new IllegalArgumentException | ||
| 111 | (MessageOutput.format("Argument is not defined for connector:", | ||
| 112 | new Object [] {name, connector.name()})); | ||
| 113 | } | ||
| 114 | argument.setValue(value); | ||
| 115 | |||
| 116 | argString = argString.substring(endPosition); // Remove what was just parsed... | ||
| 117 | m = p.matcher(argString); // and parse again on what is left. | ||
| 118 | } | ||
| 119 | if ((! argString.equals(",")) && (argString.length() > 0)) { | ||
| 120 | /* | ||
| 121 | * It is an error if any part of argString is left over, | ||
| 122 | * unless it was empty to begin with. | ||
| 123 | */ | ||
| 124 | throw new IllegalArgumentException | ||
| 125 | (MessageOutput.format("Illegal connector argument", argString)); | ||
| 126 | } | ||
| 127 | return arguments; | ||
| 128 | } | ||
| 129 | |||
| 130 | VMConnection(String connectSpec, int traceFlags) { | ||
| 131 | String nameString; | ||
| 132 | String argString; | ||
| 133 | int index = connectSpec.indexOf(':'); | ||
| 134 | if (index == -1) { | ||
| 135 | nameString = connectSpec; | ||
| 136 | argString = ""; | ||
| 137 | } else { | ||
| 138 | nameString = connectSpec.substring(0, index); | ||
| 139 | argString = connectSpec.substring(index + 1); | ||
| 140 | } | ||
| 141 | |||
| 142 | connector = findConnector(nameString); | ||
| 143 | if (connector == null) { | ||
| 144 | throw new IllegalArgumentException | ||
| 145 | (MessageOutput.format("No connector named:", nameString)); | ||
| 146 | } | ||
| 147 | |||
| 148 | connectorArgs = parseConnectorArgs(connector, argString); | ||
| 149 | this.traceFlags = traceFlags; | ||
| 150 | } | ||
| 151 | |||
| 152 | synchronized VirtualMachine open() { | ||
| 153 | if (connector instanceof LaunchingConnector) { | ||
| 154 | vm = launchTarget(); | ||
| 155 | } else if (connector instanceof AttachingConnector) { | ||
| 156 | vm = attachTarget(); | ||
| 157 | } else if (connector instanceof ListeningConnector) { | ||
| 158 | vm = listenTarget(); | ||
| 159 | } else { | ||
| 160 | throw new InternalError | ||
| 161 | (MessageOutput.format("Invalid connect type")); | ||
| 162 | } | ||
| 163 | vm.setDebugTraceMode(traceFlags); | ||
| 164 | if (vm.canBeModified()){ | ||
| 165 | setEventRequests(vm); | ||
| 166 | resolveEventRequests(); | ||
| 167 | } | ||
| 168 | /* | ||
| 169 | * Now that the vm connection is open, fetch the debugee | ||
| 170 | * classpath and set up a default sourcepath. | ||
| 171 | * (Unless user supplied a sourcepath on the command line) | ||
| 172 | * (Bug ID 4186582) | ||
| 173 | */ | ||
| 174 | if (Env.getSourcePath().length() == 0) { | ||
| 175 | if (vm instanceof PathSearchingVirtualMachine) { | ||
| 176 | PathSearchingVirtualMachine psvm = | ||
| 177 | (PathSearchingVirtualMachine) vm; | ||
| 178 | Env.setSourcePath(psvm.classPath()); | ||
| 179 | } else { | ||
| 180 | Env.setSourcePath("."); | ||
| 181 | } | ||
| 182 | } | ||
| 183 | |||
| 184 | return vm; | ||
| 185 | } | ||
| 186 | |||
| 187 | boolean setConnectorArg(String name, String value) { | ||
| 188 | /* | ||
| 189 | * Too late if the connection already made | ||
| 190 | */ | ||
| 191 | if (vm != null) { | ||
| 192 | return false; | ||
| 193 | } | ||
| 194 | |||
| 195 | Connector.Argument argument = connectorArgs.get(name); | ||
| 196 | if (argument == null) { | ||
| 197 | return false; | ||
| 198 | } | ||
| 199 | argument.setValue(value); | ||
| 200 | return true; | ||
| 201 | } | ||
| 202 | |||
| 203 | String connectorArg(String name) { | ||
| 204 | Connector.Argument argument = connectorArgs.get(name); | ||
| 205 | if (argument == null) { | ||
| 206 | return ""; | ||
| 207 | } | ||
| 208 | return argument.value(); | ||
| 209 | } | ||
| 210 | |||
| 211 | public synchronized VirtualMachine vm() { | ||
| 212 | if (vm == null) { | ||
| 213 | throw new VMNotConnectedException(); | ||
| 214 | } else { | ||
| 215 | return vm; | ||
| 216 | } | ||
| 217 | } | ||
| 218 | |||
| 219 | boolean isOpen() { | ||
| 220 | return (vm != null); | ||
| 221 | } | ||
| 222 | |||
| 223 | boolean isLaunch() { | ||
| 224 | return (connector instanceof LaunchingConnector); | ||
| 225 | } | ||
| 226 | |||
| 227 | public void disposeVM() { | ||
| 228 | try { | ||
| 229 | if (vm != null) { | ||
| 230 | vm.dispose(); | ||
| 231 | vm = null; | ||
| 232 | } | ||
| 233 | } finally { | ||
| 234 | if (process != null) { | ||
| 235 | process.destroy(); | ||
| 236 | process = null; | ||
| 237 | } | ||
| 238 | waitOutputComplete(); | ||
| 239 | } | ||
| 240 | } | ||
| 241 | |||
| 242 | private void setEventRequests(VirtualMachine vm) { | ||
| 243 | EventRequestManager erm = vm.eventRequestManager(); | ||
| 244 | |||
| 245 | // Normally, we want all uncaught exceptions. We request them | ||
| 246 | // via the same mechanism as Commands.commandCatchException() | ||
| 247 | // so the user can ignore them later if they are not | ||
| 248 | // interested. | ||
| 249 | // FIXME: this works but generates spurious messages on stdout | ||
| 250 | // during startup: | ||
| 251 | // Set uncaught java.lang.Throwable | ||
| 252 | // Set deferred uncaught java.lang.Throwable | ||
| 253 | Commands evaluator = new Commands(); | ||
| 254 | evaluator.commandCatchException | ||
| 255 | (new StringTokenizer("uncaught java.lang.Throwable")); | ||
| 256 | |||
| 257 | ThreadStartRequest tsr = erm.createThreadStartRequest(); | ||
| 258 | tsr.enable(); | ||
| 259 | ThreadDeathRequest tdr = erm.createThreadDeathRequest(); | ||
| 260 | tdr.enable(); | ||
| 261 | } | ||
| 262 | |||
| 263 | private void resolveEventRequests() { | ||
| 264 | Env.specList.resolveAll(); | ||
| 265 | } | ||
| 266 | |||
| 267 | private void dumpStream(InputStream stream) throws IOException { | ||
| 268 | BufferedReader in = | ||
| 269 | new BufferedReader(new InputStreamReader(stream)); | ||
| 270 | int i; | ||
| 271 | try { | ||
| 272 | while ((i = in.read()) != -1) { | ||
| 273 | MessageOutput.printDirect((char)i);// Special case: use | ||
| 274 | // printDirect() | ||
| 275 | } | ||
| 276 | } catch (IOException ex) { | ||
| 277 | String s = ex.getMessage(); | ||
| 278 | if (!s.startsWith("Bad file number")) { | ||
| 279 | throw ex; | ||
| 280 | } | ||
| 281 | // else we got a Bad file number IOException which just means | ||
| 282 | // that the debuggee has gone away. We'll just treat it the | ||
| 283 | // same as if we got an EOF. | ||
| 284 | } | ||
| 285 | } | ||
| 286 | |||
| 287 | /** | ||
| 288 | * Create a Thread that will retrieve and display any output. | ||
| 289 | * Needs to be high priority, else debugger may exit before | ||
| 290 | * it can be displayed. | ||
| 291 | */ | ||
| 292 | private void displayRemoteOutput(final InputStream stream) { | ||
| 293 | Thread thr = new Thread("output reader") { | ||
| 294 | @Override | ||
| 295 | public void run() { | ||
| 296 | try { | ||
| 297 | dumpStream(stream); | ||
| 298 | } catch (IOException ex) { | ||
| 299 | MessageOutput.fatalError("Failed reading output"); | ||
| 300 | } finally { | ||
| 301 | notifyOutputComplete(); | ||
| 302 | } | ||
| 303 | } | ||
| 304 | }; | ||
| 305 | thr.setPriority(Thread.MAX_PRIORITY-1); | ||
| 306 | thr.start(); | ||
| 307 | } | ||
| 308 | |||
| 309 | private void dumpFailedLaunchInfo(Process process) { | ||
| 310 | try { | ||
| 311 | dumpStream(process.getErrorStream()); | ||
| 312 | dumpStream(process.getInputStream()); | ||
| 313 | } catch (IOException e) { | ||
| 314 | MessageOutput.println("Unable to display process output:", | ||
| 315 | e.getMessage()); | ||
| 316 | } | ||
| 317 | } | ||
| 318 | |||
| 319 | /* launch child target vm */ | ||
| 320 | private VirtualMachine launchTarget() { | ||
| 321 | LaunchingConnector launcher = (LaunchingConnector)connector; | ||
| 322 | try { | ||
| 323 | VirtualMachine vm = launcher.launch(connectorArgs); | ||
| 324 | process = vm.process(); | ||
| 325 | displayRemoteOutput(process.getErrorStream()); | ||
| 326 | displayRemoteOutput(process.getInputStream()); | ||
| 327 | return vm; | ||
| 328 | } catch (IOException ioe) { | ||
| 329 | ioe.printStackTrace(); | ||
| 330 | MessageOutput.fatalError("Unable to launch target VM."); | ||
| 331 | } catch (IllegalConnectorArgumentsException icae) { | ||
| 332 | icae.printStackTrace(); | ||
| 333 | MessageOutput.fatalError("Internal debugger error."); | ||
| 334 | } catch (VMStartException vmse) { | ||
| 335 | MessageOutput.println("vmstartexception", vmse.getMessage()); | ||
| 336 | MessageOutput.println(); | ||
| 337 | dumpFailedLaunchInfo(vmse.process()); | ||
| 338 | MessageOutput.fatalError("Target VM failed to initialize."); | ||
| 339 | } | ||
| 340 | return null; // Shuts up the compiler | ||
| 341 | } | ||
| 342 | |||
| 343 | /* attach to running target vm */ | ||
| 344 | private VirtualMachine attachTarget() { | ||
| 345 | AttachingConnector attacher = (AttachingConnector)connector; | ||
| 346 | try { | ||
| 347 | return attacher.attach(connectorArgs); | ||
| 348 | } catch (IOException ioe) { | ||
| 349 | ioe.printStackTrace(); | ||
| 350 | MessageOutput.fatalError("Unable to attach to target VM."); | ||
| 351 | } catch (IllegalConnectorArgumentsException icae) { | ||
| 352 | icae.printStackTrace(); | ||
| 353 | MessageOutput.fatalError("Internal debugger error."); | ||
| 354 | } | ||
| 355 | return null; // Shuts up the compiler | ||
| 356 | } | ||
| 357 | |||
| 358 | /* listen for connection from target vm */ | ||
| 359 | private VirtualMachine listenTarget() { | ||
| 360 | ListeningConnector listener = (ListeningConnector)connector; | ||
| 361 | try { | ||
| 362 | String retAddress = listener.startListening(connectorArgs); | ||
| 363 | MessageOutput.println("Listening at address:", retAddress); | ||
| 364 | vm = listener.accept(connectorArgs); | ||
| 365 | listener.stopListening(connectorArgs); | ||
| 366 | return vm; | ||
| 367 | } catch (IOException ioe) { | ||
| 368 | ioe.printStackTrace(); | ||
| 369 | MessageOutput.fatalError("Unable to attach to target VM."); | ||
| 370 | } catch (IllegalConnectorArgumentsException icae) { | ||
| 371 | icae.printStackTrace(); | ||
| 372 | MessageOutput.fatalError("Internal debugger error."); | ||
| 373 | } | ||
| 374 | return null; // Shuts up the compiler | ||
| 375 | } | ||
| 376 | } | ||