1 /* 2 * Copyright (c) 2001, 2020, 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 24 package nsk.share.jdi; 25 26 import jdk.test.lib.Platform; 27 import nsk.share.*; 28 import nsk.share.jpda.*; 29 30 import com.sun.jdi.*; 31 import com.sun.jdi.connect.*; 32 33 import com.sun.jdi.connect.Connector.Argument; 34 import java.io.*; 35 import java.net.*; 36 import java.util.*; 37 38 /** 39 * This class provides debugger with connection to debugee VM 40 * using JDI connectors. 41 *<p> 42 * This class provides abilities to launch and bind to debugee VM 43 * as described for base <code>DebugeeBinder</code> class, 44 * using JDI connectors and <code>com.sun.VirtualMachine</code> mirror. 45 * <p> 46 * When <code>Binder</code> is asked to bind to debugee by invoking 47 * <code>bindToBebugee()</code> method it uses 48 * <code>com.sun.jdi.Connector</code> object corresponding to 49 * value of command line options <code>-connector</code> and 50 * <code>-transport</code> to launch and connect to debugee VM. 51 * After debugee is launched and connection is established 52 * <code>Binder</code> uses <code>com.sun.jdi.VirtualMachine</code> 53 * object to construct <code>Debugee</code> object, that 54 * provides abilities to interact with debugee VM. 55 * 56 * @see Debugee 57 * @see DebugeeBinder 58 */ 59 public class Binder extends DebugeeBinder { 60 61 /** 62 * Default message prefix for <code>Binder</code> object. 63 */ 64 public static final String LOG_PREFIX = "binder> "; 65 66 /** 67 * Get version string. 68 */ getVersion()69 public static String getVersion () { 70 return "@(#)Binder.java 1.14 03/10/08"; 71 } 72 73 // -------------------------------------------------- // 74 75 /** 76 * Handler of command line arguments. 77 */ 78 private ArgumentHandler argumentHandler = null; 79 80 /** 81 * Return <code>argumentHandler</code> of this binder. 82 */ getArgumentHandler()83 public ArgumentHandler getArgumentHandler() { 84 return argumentHandler; 85 } 86 87 // -------------------------------------------------- // 88 89 /** 90 * Make <code>Binder</code> object and pass raw command line arguments. 91 * 92 * @deprecated Use newer 93 * <code>Binder(ArgumentHandler,Log)</code> 94 * constructor. 95 */ Binder(String args[])96 public Binder (String args[]) { 97 this(args, new Log(System.err)); 98 } 99 100 /** 101 * Make <code>Binder</code> object for raw command line arguments 102 * and specified <code>log</code> object. 103 * 104 * @deprecated Use newer 105 * <code>Binder(ArgumentHandler,Log)</code> 106 * constructor. 107 */ Binder(String args[], Log log)108 public Binder (String args[], Log log) { 109 this(new ArgumentHandler(args), log); 110 } 111 112 /** 113 * Make <code>Binder</code> object for specified command line arguments 114 * and <code>log</code> object. 115 */ Binder(ArgumentHandler argumentHandler, Log log)116 public Binder (ArgumentHandler argumentHandler, Log log) { 117 super(argumentHandler, log); 118 this.argumentHandler = argumentHandler; 119 } 120 121 // -------------------------------------------------- // 122 123 /** 124 * Make initial <code>Debugee</code> object for local debuggee process 125 * started with launching connector. 126 */ makeLocalDebugee(Process process)127 public Debugee makeLocalDebugee(Process process) { 128 LocalLaunchedDebugee debugee = new LocalLaunchedDebugee(process, this); 129 130 Finalizer finalizer = new Finalizer(debugee); 131 finalizer.activate(); 132 133 return debugee; 134 } 135 136 /** 137 * Launch local debuggee process with specified command line 138 * and make initial <code>Debugee</code> object. 139 */ startLocalDebugee(String cmd)140 public Debugee startLocalDebugee(String cmd) { 141 Process process = null; 142 143 try { 144 process = launchProcess(cmd); 145 } catch (IOException e) { 146 e.printStackTrace(log.getOutStream()); 147 throw new Failure("Caught exception while launching local debuggee VM process:\n\t" 148 + e); 149 } 150 151 return makeLocalDebugee(process); 152 } 153 154 /** 155 * Make debuggee wrapper for already launched debuggee VM. 156 * After enwraping debugee's output is redirected to Binder's log, 157 * VMStartEvent is received and debuggee is initialized. 158 */ enwrapDebugee(VirtualMachine vm, Process proc)159 public Debugee enwrapDebugee(VirtualMachine vm, Process proc) { 160 Debugee debugee = makeLocalDebugee(proc); 161 162 display("Redirecting VM output"); 163 debugee.redirectOutput(log); 164 debugee.setupVM(vm); 165 166 long timeout = argumentHandler.getWaitTime() * 60 * 1000; // milliseconds 167 168 display("Waiting for VM initialized"); 169 debugee.waitForVMInit(timeout); 170 171 return debugee; 172 } 173 174 /** 175 * Launch debugee VM and establish connection to it without waiting for VMStartEvent. 176 * After launching debugee's output is redirected to Binder's log, 177 * but VMStartEvent is not received and so debuggee is not fully initialized. 178 * 179 * @see #bindToDebugee(String) 180 */ bindToDebugeeNoWait(String classToExecute)181 public Debugee bindToDebugeeNoWait(String classToExecute) { 182 183 VirtualMachineManager vmm = Bootstrap.virtualMachineManager(); 184 display("VirtualMachineManager: version " 185 + vmm.majorInterfaceVersion() + "." 186 + vmm.minorInterfaceVersion()); 187 188 Debugee debugee = null; 189 190 String classPath = null; 191 // classPath = System.getProperty("java.class.path"); 192 193 prepareForPipeConnection(argumentHandler); 194 195 if (argumentHandler.isLaunchedLocally()) { 196 197 if (argumentHandler.isDefaultConnector()) { 198 debugee = localDefaultLaunchDebugee(vmm, classToExecute, classPath); 199 } else if (argumentHandler.isRawLaunchingConnector()) { 200 debugee = localRawLaunchDebugee(vmm, classToExecute, classPath); 201 } else if (argumentHandler.isLaunchingConnector()) { 202 debugee = localLaunchDebugee(vmm, classToExecute, classPath); 203 } else if (argumentHandler.isAttachingConnector()) { 204 debugee = localLaunchAndAttachDebugee(vmm, classToExecute, classPath); 205 } else if (argumentHandler.isListeningConnector()) { 206 debugee = localLaunchAndListenDebugee(vmm, classToExecute, classPath); 207 } else { 208 throw new TestBug("Unexpected connector type for local debugee launch mode" 209 + argumentHandler.getConnectorType()); 210 } 211 212 } else if (argumentHandler.isLaunchedRemotely()) { 213 214 connectToBindServer(classToExecute); 215 216 if (argumentHandler.isAttachingConnector()) { 217 debugee = remoteLaunchAndAttachDebugee(vmm, classToExecute, classPath); 218 } else if (argumentHandler.isListeningConnector()) { 219 debugee = remoteLaunchAndListenDebugee(vmm, classToExecute, classPath); 220 } else { 221 throw new TestBug("Unexpected connector type for remote debugee launch mode" 222 + argumentHandler.getConnectorType()); 223 } 224 225 } else if (argumentHandler.isLaunchedManually()) { 226 227 if (argumentHandler.isAttachingConnector()) { 228 debugee = manualLaunchAndAttachDebugee(vmm, classToExecute, classPath); 229 } else if (argumentHandler.isListeningConnector()) { 230 debugee = manualLaunchAndListenDebugee(vmm, classToExecute, classPath); 231 } else { 232 throw new TestBug("Unexpected connector type for manual debugee launch mode" 233 + argumentHandler.getConnectorType()); 234 } 235 236 } else { 237 throw new Failure("Unexpected debugee launching mode: " + argumentHandler.getLaunchMode()); 238 } 239 240 return debugee; 241 } 242 243 /** 244 * Launch debugee VM and establish JDI connection. 245 * After launching debugee's output is redirected to Binder's log, 246 * VMStart event is received and debuggee is initialized. 247 * 248 * @see #bindToDebugeeNoWait(String) 249 */ bindToDebugee(String classToExecute)250 public Debugee bindToDebugee(String classToExecute) { 251 Debugee debugee = bindToDebugeeNoWait(classToExecute); 252 253 if(argumentHandler.getOptions().getProperty("traceAll") != null) 254 debugee.VM().setDebugTraceMode(VirtualMachine.TRACE_ALL); 255 256 long timeout = argumentHandler.getWaitTime() * 60 * 1000; // milliseconds 257 258 display("Waiting for VM initialized"); 259 debugee.waitForVMInit(timeout); 260 261 return debugee; 262 } 263 264 // -------------------------------------------------- // 265 266 /** 267 * Launch debugee locally via the default LaunchingConnector. 268 */ localDefaultLaunchDebugee(VirtualMachineManager vmm, String classToExecute, String classPath)269 private Debugee localDefaultLaunchDebugee (VirtualMachineManager vmm, 270 String classToExecute, 271 String classPath) { 272 display("Finding connector: " + "default" ); 273 LaunchingConnector connector = vmm.defaultConnector(); 274 Map<String,? extends Argument> arguments = setupLaunchingConnector(connector, classToExecute, classPath); 275 276 VirtualMachine vm; 277 try { 278 display("Launching debugee"); 279 vm = connector.launch(arguments); 280 } catch (IllegalConnectorArgumentsException e) { 281 e.printStackTrace(log.getOutStream()); 282 throw new TestBug("Wrong connector arguments used to launch debuggee VM:\n\t" + e); 283 } catch (VMStartException e) { 284 e.printStackTrace(log.getOutStream()); 285 String msg = readVMStartExceptionOutput(e, log.getOutStream()); 286 throw new Failure("Caught exception while starting debugee VM:\n\t" + e + "\n" + msg); 287 } catch (IOException e) { 288 e.printStackTrace(log.getOutStream()); 289 throw new Failure("Caught exception while launching debugee VM:\n\t" + e); 290 }; 291 292 Process process = vm.process(); 293 Debugee debugee = makeLocalDebugee(process); 294 debugee.redirectOutput(log); 295 debugee.setupVM(vm); 296 297 return debugee; 298 } 299 300 301 /** 302 * Launch debugee locally via the default LaunchingConnector. 303 */ localLaunchDebugee(VirtualMachineManager vmm, String classToExecute, String classPath)304 private Debugee localLaunchDebugee (VirtualMachineManager vmm, 305 String classToExecute, 306 String classPath) { 307 308 display("Finding connector: " + argumentHandler.getConnectorName() ); 309 LaunchingConnector connector = 310 (LaunchingConnector) findConnector(argumentHandler.getConnectorName(), 311 vmm.launchingConnectors()); 312 Map<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> arguments = setupLaunchingConnector(connector, classToExecute, classPath); 313 314 VirtualMachine vm; 315 try { 316 display("Launching debugee"); 317 vm = connector.launch(arguments); 318 } catch (IllegalConnectorArgumentsException e) { 319 e.printStackTrace(log.getOutStream()); 320 throw new TestBug("Wrong connector arguments used to launch debuggee VM:\n\t" + e); 321 } catch (VMStartException e) { 322 e.printStackTrace(log.getOutStream()); 323 String msg = readVMStartExceptionOutput(e, log.getOutStream()); 324 throw new Failure("Caught exception while starting debugee VM:\n\t" + e + "\nProcess output:\n\t" + msg); 325 } catch (IOException e) { 326 e.printStackTrace(log.getOutStream()); 327 throw new Failure("Caught exception while launching debugee VM:\n\t" + e); 328 }; 329 330 Process process = vm.process(); 331 Debugee debugee = makeLocalDebugee(process); 332 debugee.redirectOutput(log); 333 debugee.setupVM(vm); 334 335 return debugee; 336 } 337 338 /** 339 * Launch debugee locally via the RawLaunchingConnector. 340 */ localRawLaunchDebugee(VirtualMachineManager vmm, String classToExecute, String classPath)341 private Debugee localRawLaunchDebugee (VirtualMachineManager vmm, 342 String classToExecute, 343 String classPath) { 344 display("Finding connector: " + argumentHandler.getConnectorName() ); 345 LaunchingConnector connector = 346 (LaunchingConnector) findConnector(argumentHandler.getConnectorName(), 347 vmm.launchingConnectors()); 348 Map<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> arguments = setupRawLaunchingConnector(connector, classToExecute, classPath); 349 350 VirtualMachine vm; 351 try { 352 display("Launching debugee"); 353 vm = connector.launch(arguments); 354 } catch (IllegalConnectorArgumentsException e) { 355 e.printStackTrace(log.getOutStream()); 356 throw new TestBug("Wrong connector arguments used to launch debuggee VM:\n\t" + e); 357 } catch (VMStartException e) { 358 e.printStackTrace(log.getOutStream()); 359 String msg = readVMStartExceptionOutput(e, log.getOutStream()); 360 throw new Failure("Caught exception while starting debugee VM:\n\t" + e + "\nProcess output:\n\t" + msg); 361 } catch (IOException e) { 362 e.printStackTrace(log.getOutStream()); 363 throw new Failure("Caught exception while launching debugee VM:\n\t" + e); 364 }; 365 366 Process process = vm.process(); 367 Debugee debugee = makeLocalDebugee(process); 368 debugee.redirectOutput(log); 369 debugee.setupVM(vm); 370 371 return debugee; 372 } 373 374 /** 375 * Launch debugee VM locally as a local process and connect to it using 376 * <code>AttachingConnector</code>. 377 */ localLaunchAndAttachDebugee(VirtualMachineManager vmm, String classToExecute, String classPath)378 private Debugee localLaunchAndAttachDebugee (VirtualMachineManager vmm, 379 String classToExecute, 380 String classPath) { 381 display("FindingConnector: " + argumentHandler.getConnectorName() ); 382 AttachingConnector connector = 383 (AttachingConnector) findConnector(argumentHandler.getConnectorName(), 384 vmm.attachingConnectors()); 385 Map<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> arguments = setupAttachingConnector(connector, classToExecute, classPath); 386 387 String address = makeTransportAddress(); 388 String[] cmdLineArgs = makeCommandLineArgs(classToExecute, address); 389 String javaCmdLine = makeCommandLineString(classToExecute, address, "\""); 390 391 display("Starting java process:\n\t" + javaCmdLine); 392 Debugee debugee = startLocalDebugee(cmdLineArgs); 393 debugee.redirectOutput(log); 394 395 display("Attaching to debugee"); 396 VirtualMachine vm = null; 397 IOException ioe = null; 398 for (int i = 0; i < CONNECT_TRIES; i++) { 399 try { 400 vm = connector.attach(arguments); 401 display("Debugee attached"); 402 debugee.setupVM(vm); 403 return debugee; 404 } catch (IOException e) { 405 display("Attempt #" + i + " to connect to debugee VM failed:\n\t" + e); 406 ioe = e; 407 if (debugee.terminated()) { 408 throw new Failure("Unable to connect to debuggee VM: VM process is terminated"); 409 } 410 try { 411 Thread.currentThread().sleep(CONNECT_TRY_DELAY); 412 } catch (InterruptedException ie) { 413 ie.printStackTrace(log.getOutStream()); 414 throw new Failure("Thread interrupted while pausing connection attempts:\n\t" 415 + ie); 416 } 417 } catch (IllegalConnectorArgumentsException e) { 418 e.printStackTrace(log.getOutStream()); 419 throw new TestBug("Wrong connector arguments used to attach to debuggee VM:\n\t" + e); 420 } 421 } 422 throw new Failure("Unable to connect to debugee VM after " + CONNECT_TRIES 423 + " tries:\n\t" + ioe); 424 } 425 426 /** 427 * Launch debugee VM locally as a local process and connect to it using 428 * <code>ListeningConnector</code>. 429 */ localLaunchAndListenDebugee(VirtualMachineManager vmm, String classToExecute, String classPath)430 private Debugee localLaunchAndListenDebugee (VirtualMachineManager vmm, 431 String classToExecute, 432 String classPath) { 433 display("Finding connector: " + argumentHandler.getConnectorName() ); 434 ListeningConnector connector = 435 (ListeningConnector) findConnector(argumentHandler.getConnectorName(), 436 vmm.listeningConnectors()); 437 Map<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> arguments = setupListeningConnector(connector, classToExecute, classPath); 438 439 String address = null; 440 try { 441 display("Listening for connection from debugee"); 442 address = connector.startListening(arguments); 443 } catch (IllegalConnectorArgumentsException e) { 444 e.printStackTrace(log.getOutStream()); 445 throw new TestBug("Wrong connector arguments used to listen debuggee VM:\n\t" + e); 446 } catch (IOException e) { 447 e.printStackTrace(log.getOutStream()); 448 throw new Failure("Caught exception while starting listening debugee VM:\n\t" + e); 449 }; 450 451 String[] cmdLineArgs = makeCommandLineArgs(classToExecute, address); 452 String javaCmdLine = makeCommandLineString(classToExecute, address, "\""); 453 454 display("Starting java process:\n\t" + javaCmdLine); 455 Debugee debugee = startLocalDebugee(cmdLineArgs); 456 debugee.redirectOutput(log); 457 458 display("Waiting for connection from debugee"); 459 VirtualMachine vm = null; 460 IOException ioe = null; 461 for (int i = 0; i < CONNECT_TRIES; i++) { 462 try { 463 vm = connector.accept(arguments); 464 connector.stopListening(arguments); 465 display("Debugee attached"); 466 debugee.setupVM(vm); 467 return debugee; 468 } catch (IOException e) { 469 display("Attempt #" + i + " to listen debugee VM failed:\n\t" + e); 470 ioe = e; 471 if (debugee.terminated()) { 472 throw new Failure("Unable to connect to debuggee VM: VM process is terminated"); 473 } 474 try { 475 Thread.currentThread().sleep(CONNECT_TRY_DELAY); 476 } catch (InterruptedException ie) { 477 ie.printStackTrace(log.getOutStream()); 478 throw new Failure("Thread interrupted while pausing connection attempts:\n\t" 479 + ie); 480 } 481 } catch (IllegalConnectorArgumentsException e) { 482 e.printStackTrace(log.getOutStream()); 483 throw new TestBug("Wrong connector arguments used to listen debuggee VM:\n\t" + e); 484 } 485 } 486 throw new Failure("Unable to connect to debugee VM after " + CONNECT_TRIES 487 + " tries:\n\t" + ioe); 488 } 489 490 // -------------------------------------------------- // 491 492 /** 493 * Launch debugee VM remotely via <code>BindServer</code> and connect to it using 494 * <code>AttachingConnector</code>. 495 */ remoteLaunchAndAttachDebugee(VirtualMachineManager vmm, String classToExecute, String classPath)496 private Debugee remoteLaunchAndAttachDebugee (VirtualMachineManager vmm, 497 String classToExecute, 498 String classPath) { 499 display("Finding connector: " + argumentHandler.getConnectorName() ); 500 AttachingConnector connector = 501 (AttachingConnector) findConnector(argumentHandler.getConnectorName(), 502 vmm.attachingConnectors()); 503 504 Map<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> arguments = setupAttachingConnector(connector, classToExecute, classPath); 505 506 String address = makeTransportAddress(); 507 String[] cmdLineArgs = makeCommandLineArgs(classToExecute, address); 508 String javaCmdLine = makeCommandLineString(classToExecute, address, "\""); 509 510 display("Starting remote java process:\n\t" + javaCmdLine); 511 Debugee debugee = startRemoteDebugee(cmdLineArgs); 512 513 display("Attaching to debugee"); 514 VirtualMachine vm; 515 IOException ioe = null; 516 for (int i = 0; i < CONNECT_TRIES; i++) { 517 try { 518 vm = connector.attach(arguments); 519 display("Debugee attached"); 520 debugee.setupVM(vm); 521 return debugee; 522 } catch (IOException e) { 523 display("Attempt #" + i + " to connect to debugee VM failed:\n\t" + e); 524 ioe = e; 525 if (debugee.terminated()) { 526 throw new Failure("Unable to connect to debuggee VM: VM process is terminated"); 527 } 528 try { 529 Thread.currentThread().sleep(CONNECT_TRY_DELAY); 530 } catch (InterruptedException ie) { 531 ie.printStackTrace(log.getOutStream()); 532 throw new Failure("Thread interrupted while pausing connection attempts:\n\t" 533 + ie); 534 } 535 } catch (IllegalConnectorArgumentsException e) { 536 e.printStackTrace(log.getOutStream()); 537 throw new TestBug("Wrong connector arguments used to attach to debuggee VM:\n\t" + e); 538 } 539 } 540 throw new Failure("Unable to connect to debugee VM after " + CONNECT_TRIES 541 + " tries:\n\t" + ioe); 542 } 543 544 /** 545 * Launch debugee VM remotely via <code>BindServer</code> and connect to it using 546 * <code>ListeningConnector</code>. 547 */ remoteLaunchAndListenDebugee(VirtualMachineManager vmm, String classToExecute, String classPath)548 private Debugee remoteLaunchAndListenDebugee (VirtualMachineManager vmm, 549 String classToExecute, 550 String classPath) { 551 display("Finding connector: " + argumentHandler.getConnectorName() ); 552 ListeningConnector connector = 553 (ListeningConnector) findConnector(argumentHandler.getConnectorName(), 554 vmm.listeningConnectors()); 555 Map<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> arguments = setupListeningConnector(connector, classToExecute, classPath); 556 557 String address = null; 558 try { 559 display("Listening for connection from debugee"); 560 address = connector.startListening(arguments); 561 } catch (IllegalConnectorArgumentsException e) { 562 e.printStackTrace(log.getOutStream()); 563 throw new TestBug("Wrong connector arguments used to listen debuggee VM:\n\t" + e); 564 } catch (IOException e) { 565 e.printStackTrace(log.getOutStream()); 566 throw new Failure("Caught exception while starting listening debugee VM:\n\t" + e); 567 }; 568 569 String[] cmdLineArgs = makeCommandLineArgs(classToExecute, address); 570 String javaCmdLine = makeCommandLineString(classToExecute, address, "\""); 571 572 display("Starting remote java process:\n\t" + javaCmdLine); 573 Debugee debugee = startRemoteDebugee(cmdLineArgs); 574 575 display("Waiting for connection from debugee"); 576 VirtualMachine vm; 577 IOException ioe = null; 578 for (int i = 0; i < CONNECT_TRIES; i++) { 579 try { 580 vm = connector.accept(arguments); 581 connector.stopListening(arguments); 582 display("Debugee attached"); 583 debugee.setupVM(vm); 584 return debugee; 585 } catch (IOException e) { 586 display("Attempt #" + i + " to listen debugee VM failed:\n\t" + e); 587 ioe = e; 588 if (debugee.terminated()) { 589 throw new Failure("Unable to connect to debuggee VM: VM process is terminated"); 590 } 591 try { 592 Thread.currentThread().sleep(CONNECT_TRY_DELAY); 593 } catch (InterruptedException ie) { 594 ie.printStackTrace(log.getOutStream()); 595 throw new Failure("Thread interrupted while pausing connection attempts:\n\t" 596 + ie); 597 } 598 } catch (IllegalConnectorArgumentsException e) { 599 e.printStackTrace(log.getOutStream()); 600 throw new TestBug("Wrong connector arguments used to listen debuggee VM:\n\t" + e); 601 } 602 } 603 throw new Failure("Unable to connect to debugee VM after " + CONNECT_TRIES 604 + " tries:\n\t" + ioe); 605 } 606 607 // -------------------------------------------------- // 608 609 /** 610 * Prompt to manually launch debugee VM and connect to it using 611 * <code>AttachingConnector</code>. 612 */ manualLaunchAndAttachDebugee(VirtualMachineManager vmm, String classToExecute, String classPath)613 private Debugee manualLaunchAndAttachDebugee (VirtualMachineManager vmm, 614 String classToExecute, 615 String classPath) { 616 display("Finding connector: " + argumentHandler.getConnectorName() ); 617 AttachingConnector connector = 618 (AttachingConnector) findConnector(argumentHandler.getConnectorName(), 619 vmm.attachingConnectors()); 620 Map<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> arguments = setupAttachingConnector(connector, classToExecute, classPath); 621 622 String address = makeTransportAddress(); 623 String javaCmdLine = makeCommandLineString(classToExecute, address, "\""); 624 625 display("Starting manual java process:\n\t" + javaCmdLine); 626 ManualLaunchedDebugee debugee = startManualDebugee(javaCmdLine); 627 628 VirtualMachine vm; 629 try { 630 display("Attaching to debugee"); 631 vm = connector.attach(arguments); 632 } catch (IllegalConnectorArgumentsException e) { 633 e.printStackTrace(log.getOutStream()); 634 throw new TestBug("Wrong connector arguments used to attach to debuggee VM:\n\t" + e); 635 } catch (IOException e) { 636 e.printStackTrace(log.getOutStream()); 637 throw new Failure("Caught exception while attaching to debugee VM:\n\t" + e); 638 }; 639 display("Debugee attached"); 640 641 debugee.setupVM(vm); 642 return debugee; 643 } 644 645 /** 646 * Prompt to manually launch debugee VM and connect to it using 647 * <code>ListeningConnector</code>. 648 */ manualLaunchAndListenDebugee(VirtualMachineManager vmm, String classToExecute, String classPath)649 private Debugee manualLaunchAndListenDebugee (VirtualMachineManager vmm, 650 String classToExecute, 651 String classPath) { 652 display("Finding connector: " + argumentHandler.getConnectorName() ); 653 ListeningConnector connector = 654 (ListeningConnector) findConnector(argumentHandler.getConnectorName(), 655 vmm.listeningConnectors()); 656 Map<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> arguments = setupListeningConnector(connector, classToExecute, classPath); 657 658 VirtualMachine vm; 659 try { 660 display("Listening for connection from debugee"); 661 String address = connector.startListening(arguments); 662 String javaCmdLine = makeCommandLineString(classToExecute, address, "\""); 663 display("Starting manual java process:\n\t" + javaCmdLine); 664 ManualLaunchedDebugee debugee = startManualDebugee(javaCmdLine); 665 display("Waiting for connection from debugee"); 666 vm = connector.accept(arguments); 667 display("Debugee attached"); 668 connector.stopListening(arguments); 669 debugee.setupVM(vm); 670 return debugee; 671 } catch (IllegalConnectorArgumentsException e) { 672 e.printStackTrace(log.getOutStream()); 673 throw new TestBug("Wrong connector arguments used to listen debuggee VM:\n\t" + e); 674 } catch (IOException e) { 675 e.printStackTrace(log.getOutStream()); 676 throw new Failure("Caught exception while listening to debugee VM:\n\t" + e); 677 } 678 } 679 680 // -------------------------------------------------- // 681 682 /** 683 * Make proper arguments for LaunchingConnector. 684 */ setupLaunchingConnector(LaunchingConnector connector, String classToExecute, String classPath)685 private Map<String,? extends Argument> setupLaunchingConnector(LaunchingConnector connector, 686 String classToExecute, 687 String classPath) { 688 display("LaunchingConnector:"); 689 display(" name: " + connector.name()); 690 display(" description: " + connector.description()); 691 display(" transport: " + connector.transport()); 692 693 Hashtable<String,? extends Argument> arguments = new Hashtable<String,Argument>(connector.defaultArguments()); 694 695 Connector.Argument arg; 696 697 arg = (Connector.StringArgument) arguments.get("quote"); 698 String quote = "\0"; 699 arg.setValue(quote); 700 701 String[] rawArgs = argumentHandler.getRawArguments(); 702 if (Platform.isWindows()) { 703 // " has to be escaped on windows 704 rawArgs = Arrays.stream(rawArgs) 705 .map(s -> s.replace("\"", "\\\"")) 706 .toArray(String[]::new); 707 } 708 709 String cmdline = classToExecute + " " + ArgumentHandler.joinArguments(rawArgs, quote); 710 711 arg = (Connector.StringArgument) arguments.get("main"); 712 arg.setValue(cmdline); 713 714 if (! argumentHandler.willDebugeeSuspended()) { 715 Connector.BooleanArgument barg = (Connector.BooleanArgument) arguments.get("suspend"); 716 barg.setValue(true); 717 } 718 719 /* 720 if (! argumentHandler.isJVMDIStrictMode()) { 721 arg = (Connector.StringArgument) arguments.get("options"); 722 arg.setValue("strict=y"); 723 } 724 */ 725 726 if (! argumentHandler.isDefaultDebugeeJavaHome()) { 727 arg = (Connector.StringArgument) arguments.get("home"); 728 arg.setValue(argumentHandler.getDebugeeJavaHome()); 729 } 730 731 if (! argumentHandler.isDefaultLaunchExecName()) { 732 arg = (Connector.StringArgument) arguments.get("vmexec"); 733 arg.setValue(argumentHandler.getLaunchExecName()); 734 } 735 736 String vmArgs = ""; 737 738 String vmUserArgs = argumentHandler.getLaunchOptions(); 739 740 if (vmUserArgs != null) { 741 vmArgs = vmUserArgs; 742 } 743 744 /* 745 if (classPath != null) { 746 vmArgs += " -classpath " + quote + classPath + quote; 747 } 748 */ 749 750 if (vmArgs.length() > 0) { 751 arg = (Connector.StringArgument) arguments.get("options"); 752 arg.setValue(vmArgs); 753 } 754 755 display("Connector arguments:"); 756 Iterator iterator = arguments.values().iterator(); 757 while (iterator.hasNext()) { 758 display(" " + iterator.next()); 759 } 760 return arguments; 761 } 762 763 /** 764 * Make proper arguments for RawLaunchingConnector. 765 */ setupRawLaunchingConnector(LaunchingConnector connector, String classToExecute, String classPath)766 private Map<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> setupRawLaunchingConnector(LaunchingConnector connector, 767 String classToExecute, 768 String classPath) { 769 display("RawLaunchingConnector:"); 770 display(" name: " + connector.name()); 771 display(" description: " + connector.description()); 772 display(" transport: " + connector.transport()); 773 774 Hashtable<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> arguments = new Hashtable<java.lang.String, com.sun.jdi.connect.Connector.Argument>(connector.defaultArguments()); 775 776 String connectorAddress; 777 String vmAddress; 778 779 if (argumentHandler.isSocketTransport()) { 780 connectorAddress = argumentHandler.getTransportPort(); 781 vmAddress = argumentHandler.getDebugeeHost() 782 + ":" + argumentHandler.getTransportPort(); 783 } else if (argumentHandler.isShmemTransport() ) { 784 connectorAddress = argumentHandler.getTransportSharedName(); 785 vmAddress=connectorAddress; 786 } else { 787 throw new TestBug("Undefined transport type for AttachingConnector"); 788 } 789 790 Connector.Argument arg; 791 792 arg = (Connector.StringArgument) arguments.get("quote"); 793 String quote = arg.value(); 794 795 String javaCmdLine = makeCommandLineString(classToExecute, quote); 796 797 arg = (Connector.StringArgument) arguments.get("command"); 798 arg.setValue(javaCmdLine); 799 800 arg = (Connector.StringArgument) arguments.get("address"); 801 arg.setValue(connectorAddress); 802 803 display("Connector arguments:"); 804 Iterator iterator = arguments.values().iterator(); 805 while (iterator.hasNext()) { 806 display(" " + iterator.next()); 807 } 808 return arguments; 809 } 810 811 /** 812 * Make proper arguments for AttachingConnector. 813 */ setupAttachingConnector(AttachingConnector connector, String classToExecute, String classPath)814 private Map<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> setupAttachingConnector(AttachingConnector connector, 815 String classToExecute, 816 String classPath) { 817 display("AttachingConnector:"); 818 display(" name: " + connector.name()); 819 display(" description: " + connector.description()); 820 display(" transport: " + connector.transport()); 821 822 Hashtable<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> arguments = new Hashtable<java.lang.String,com.sun.jdi.connect.Connector.Argument>(connector.defaultArguments()); 823 824 Connector.Argument arg; 825 if (argumentHandler.isSocketTransport()) { 826 arg = (Connector.StringArgument) arguments.get("hostname"); 827 arg.setValue(argumentHandler.getDebugeeHost()); 828 Connector.IntegerArgument iarg = (Connector.IntegerArgument) arguments.get("port"); 829 iarg.setValue(argumentHandler.getTransportPortNumber()); 830 } else { 831 arg = (Connector.StringArgument) arguments.get("name"); 832 arg.setValue(argumentHandler.getTransportSharedName()); 833 } 834 835 display("Connector arguments:"); 836 Iterator iterator = arguments.values().iterator(); 837 while (iterator.hasNext()) { 838 display(" " + iterator.next()); 839 } 840 return arguments; 841 } 842 843 /** 844 * Make proper arguments for ListeningConnector. 845 */ setupListeningConnector(ListeningConnector connector, String classToExecute, String classPath)846 private Map<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> setupListeningConnector(ListeningConnector connector, 847 String classToExecute, 848 String classPath) { 849 display("ListeningConnector:"); 850 display(" name: " + connector.name()); 851 display(" description: " + connector.description()); 852 display(" transport: " + connector.transport()); 853 854 Hashtable<java.lang.String,? extends com.sun.jdi.connect.Connector.Argument> arguments = new Hashtable<java.lang.String,com.sun.jdi.connect.Connector.Argument>(connector.defaultArguments()); 855 856 Connector.Argument arg; 857 if (argumentHandler.isSocketTransport()) { 858 if (!argumentHandler.isTransportAddressDynamic()) { 859 int port = argumentHandler.getTransportPortNumber(); 860 Connector.IntegerArgument iarg = (Connector.IntegerArgument) arguments.get("port"); 861 iarg.setValue(port); 862 } 863 } else { 864 String sharedName = argumentHandler.getTransportSharedName(); 865 arg = (Connector.StringArgument) arguments.get("name"); 866 arg.setValue(sharedName); 867 } 868 869 display("Connector arguments:"); 870 Iterator iterator = arguments.values().iterator(); 871 while (iterator.hasNext()) { 872 display(" " + iterator.next()); 873 } 874 return arguments; 875 } 876 877 // -------------------------------------------------- // 878 879 /** 880 * Find connector by name from given connectors list. 881 */ findConnector(String connectorName, List connectors)882 private Connector findConnector(String connectorName, List connectors) { 883 Iterator iter = connectors.iterator(); 884 885 while (iter.hasNext()) { 886 Connector connector = (Connector) iter.next(); 887 if (connector.name().equals(connectorName)) { 888 return connector; 889 } 890 } 891 throw new Failure("JDI connector not found: " + connectorName); 892 } 893 894 // -------------------------------------------------- // 895 896 /** 897 * Launch local debuggee process with specified command line arguments 898 * and make initial <code>Debugee</code> mirror. 899 */ startLocalDebugee(String[] cmdArgs)900 protected Debugee startLocalDebugee(String[] cmdArgs) { 901 Process process = null; 902 903 try { 904 process = launchProcess(cmdArgs); 905 } catch (IOException e) { 906 e.printStackTrace(log.getOutStream()); 907 throw new Failure("Caught exception while launching local debuggee VM process:\n\t" 908 + e); 909 } 910 911 return makeLocalDebugee(process); 912 } 913 914 /** 915 * Launch remote debuggee process with specified command line arguments 916 * and make initial <code>Debugee</code> mirror. 917 */ startRemoteDebugee(String[] cmdArgs)918 protected RemoteLaunchedDebugee startRemoteDebugee(String[] cmdArgs) { 919 try { 920 launchRemoteProcess(cmdArgs); 921 } catch (IOException e) { 922 e.printStackTrace(log.getOutStream()); 923 throw new Failure("Caught exception while launching remote debuggee VM process:\n\t" 924 + e); 925 } 926 927 RemoteLaunchedDebugee debugee = new RemoteLaunchedDebugee(this); 928 929 Finalizer finalizer = new Finalizer(debugee); 930 finalizer.activate(); 931 932 return debugee; 933 } 934 935 /** 936 * Launch manual debuggee process with specified command line arguments 937 * and make initial <code>Debugee</code> mirror. 938 */ startManualDebugee(String cmd)939 protected ManualLaunchedDebugee startManualDebugee(String cmd) { 940 ManualLaunchedDebugee debugee = new ManualLaunchedDebugee(this); 941 debugee.launchDebugee(cmd); 942 943 Finalizer finalizer = new Finalizer(debugee); 944 finalizer.activate(); 945 946 return debugee; 947 } 948 readVMStartExceptionOutput(VMStartException e, PrintStream log)949 public static String readVMStartExceptionOutput(VMStartException e, PrintStream log) { 950 StringBuffer msg = new StringBuffer(); 951 try (InputStream is = e.process().getInputStream()) { 952 msg.append("\tstdout: ").append(new String(readAllBytes(is))).append('\n'); 953 } catch (IOException e1) { 954 log.println("Could not read normal output from launched process:" + e1); 955 } 956 try (InputStream is = e.process().getErrorStream()) { 957 msg.append("\tstderr: ").append(new String(readAllBytes(is))); 958 } catch (IOException e1) { 959 log.println("Could not read error output from launched process:" + e1); 960 } 961 return msg.toString(); 962 } 963 964 /** 965 * Copied from the JDK 9 implementation in InputStream.java 966 */ readAllBytes(InputStream is)967 private static byte[] readAllBytes(InputStream is) throws IOException { 968 final int DEFAULT_BUFFER_SIZE = 8192; 969 final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8; 970 971 byte[] buf = new byte[DEFAULT_BUFFER_SIZE]; 972 int capacity = buf.length; 973 int nread = 0; 974 int n; 975 for (;;) { 976 // read to EOF which may read more or less than initial buffer size 977 while ((n = is.read(buf, nread, capacity - nread)) > 0) 978 nread += n; 979 980 // if the last call to read returned -1, then we're done 981 if (n < 0) 982 break; 983 984 // need to allocate a larger buffer 985 if (capacity <= MAX_BUFFER_SIZE - capacity) { 986 capacity = capacity << 1; 987 } else { 988 if (capacity == MAX_BUFFER_SIZE) 989 throw new OutOfMemoryError("Required array size too large"); 990 capacity = MAX_BUFFER_SIZE; 991 } 992 buf = Arrays.copyOf(buf, capacity); 993 } 994 return (capacity == nread) ? buf : Arrays.copyOf(buf, nread); 995 } 996 997 } 998 999 1000 /** 1001 * Mirror of locally launched debugee. 1002 */ 1003 final class LocalLaunchedDebugee extends Debugee { 1004 1005 /** Enwrap the locally started VM process. */ LocalLaunchedDebugee(Process process, Binder binder)1006 public LocalLaunchedDebugee (Process process, Binder binder) { 1007 super(binder); 1008 this.process = process; 1009 checkTermination = true; 1010 } 1011 1012 // ---------------------------------------------- // 1013 1014 /** Return exit status of the debugee VM. */ getStatus()1015 public int getStatus () { 1016 return process.exitValue(); 1017 } 1018 1019 /** Check whether the debugee VM has been terminated. */ terminated()1020 public boolean terminated () { 1021 if (process == null) 1022 return true; 1023 1024 try { 1025 int value = process.exitValue(); 1026 return true; 1027 } catch (IllegalThreadStateException e) { 1028 return false; 1029 } 1030 } 1031 1032 // ---------------------------------------------- // 1033 1034 /** Kill the debugee VM. */ killDebugee()1035 protected void killDebugee () { 1036 super.killDebugee(); 1037 if (!terminated()) { 1038 log.display("Killing debugee VM process"); 1039 process.destroy(); 1040 } 1041 } 1042 1043 /** Wait until the debugee VM shutdown or crash. */ waitForDebugee()1044 protected int waitForDebugee () throws InterruptedException { 1045 int code = process.waitFor(); 1046 return code; 1047 } 1048 1049 /** Get a pipe to write to the debugee's stdin stream. */ getInPipe()1050 protected OutputStream getInPipe () { 1051 return process.getOutputStream(); 1052 } 1053 1054 /** Get a pipe to read the debugee's stdout stream. */ getOutPipe()1055 protected InputStream getOutPipe () { 1056 return process.getInputStream(); 1057 } 1058 1059 /** Get a pipe to read the debugee's stderr stream. */ getErrPipe()1060 protected InputStream getErrPipe () { 1061 return process.getErrorStream(); 1062 } 1063 } 1064 1065 1066 /** 1067 * Mirror of remotely launched debugee. 1068 */ 1069 final class RemoteLaunchedDebugee extends Debugee { 1070 1071 /** Enwrap the remotely started VM process. */ RemoteLaunchedDebugee(Binder binder)1072 public RemoteLaunchedDebugee (Binder binder) { 1073 super(binder); 1074 } 1075 1076 // ---------------------------------------------- // 1077 1078 /** Return exit status of the debugee VM. */ getStatus()1079 public int getStatus () { 1080 return binder.getRemoteProcessStatus(); 1081 } 1082 1083 /** Check whether the debugee VM has been terminated. */ terminated()1084 public boolean terminated () { 1085 return binder.isRemoteProcessTerminated(); 1086 } 1087 1088 // ---------------------------------------------- // 1089 1090 /** Kill the debugee VM. */ killDebugee()1091 protected void killDebugee () { 1092 super.killDebugee(); 1093 if (!terminated()) { 1094 binder.killRemoteProcess(); 1095 } 1096 } 1097 1098 /** Wait until the debugee VM shutdown or crash. */ waitForDebugee()1099 protected int waitForDebugee () { 1100 return binder.waitForRemoteProcess(); 1101 } 1102 1103 /** Get a pipe to write to the debugee's stdin stream. */ getInPipe()1104 protected OutputStream getInPipe () { 1105 return null; 1106 } 1107 1108 /** Get a pipe to read the debugee's stdout stream. */ getOutPipe()1109 protected InputStream getOutPipe () { 1110 return null; 1111 } 1112 1113 /** Get a pipe to read the debugee's stderr stream. */ getErrPipe()1114 protected InputStream getErrPipe () { 1115 return null; 1116 } 1117 redirectStdout(OutputStream out)1118 public void redirectStdout(OutputStream out) { 1119 } 1120 redirectStdout(Log log, String prefix)1121 public void redirectStdout(Log log, String prefix) { 1122 } 1123 redirectStderr(OutputStream out)1124 public void redirectStderr(OutputStream out) { 1125 } 1126 redirectStderr(Log log, String prefix)1127 public void redirectStderr(Log log, String prefix) { 1128 } 1129 } 1130 1131 1132 /** 1133 * Mirror of manually launched debugee. 1134 */ 1135 final class ManualLaunchedDebugee extends Debugee { 1136 /** Enwrap the manually started VM process. */ ManualLaunchedDebugee(Binder binder)1137 public ManualLaunchedDebugee (Binder binder) { 1138 super(binder); 1139 makeInputReader(); 1140 } 1141 1142 // ---------------------------------------------- // 1143 1144 private int exitCode = 0; 1145 private boolean finished = false; 1146 private static BufferedReader bin = null; 1147 launchDebugee(String commandLine)1148 public void launchDebugee(String commandLine) { 1149 makeInputReader(); 1150 1151 putMessage("Launch target VM using such command line:\n" 1152 + commandLine); 1153 String answer = askQuestion("Has the VM successfully started? (yes/no)", "yes"); 1154 for ( ; ; ) { 1155 if (answer.equals("yes")) 1156 break; 1157 if (answer.equals("no")) 1158 throw new Failure ("Unable to manually launch debugee VM"); 1159 answer = askQuestion("Wrong answer. Please type yes or no", "yes"); 1160 } 1161 } 1162 makeInputReader()1163 private static void makeInputReader() { 1164 if (bin == null) { 1165 bin = new BufferedReader(new InputStreamReader(System.in)); 1166 } 1167 } 1168 destroyInputReader()1169 private static void destroyInputReader() { 1170 if (bin != null) { 1171 try { 1172 bin.close(); 1173 } catch (IOException e) { 1174 // e.printStackTrace(log.getOutStream()); 1175 throw new Failure("Caught exception while closing input stream:\n\t" + e); 1176 } 1177 bin = null; 1178 } 1179 } 1180 putMessage(String msg)1181 private static void putMessage(String msg) { 1182 System.out.println("\n>>> " + msg); 1183 } 1184 askQuestion(String question, String defaultAnswer)1185 private static String askQuestion(String question, String defaultAnswer) { 1186 try { 1187 System.out.print("\n>>> " + question); 1188 System.out.print(" [" + defaultAnswer + "] "); 1189 System.out.flush(); 1190 String answer = bin.readLine(); 1191 if (answer.equals("")) 1192 return defaultAnswer; 1193 return answer; 1194 } catch (IOException e) { 1195 // e.printStackTrace(log.getOutStream()); 1196 throw new Failure("Caught exception while reading answer:\n\t" + e); 1197 } 1198 } 1199 1200 /** Return exit status of the debugee VM. */ getStatus()1201 public int getStatus () { 1202 if (! finished) { 1203 throw new Failure("Unable to get status of debugee VM: process still alive"); 1204 } 1205 return exitCode; 1206 } 1207 1208 /** Check whether the debugee VM has been terminated. */ terminated()1209 public boolean terminated () { 1210 return finished; 1211 } 1212 1213 // ---------------------------------------------- // 1214 1215 /** Kill the debugee VM. */ killDebugee()1216 protected void killDebugee () { 1217 super.killDebugee(); 1218 if (!terminated()) { 1219 putMessage("Kill launched VM"); 1220 String answer = askQuestion("Has the VM successfully terminated? (yes/no)", "yes"); 1221 for ( ; ; ) { 1222 if (answer.equals("yes")) { 1223 finished = true; 1224 break; 1225 } 1226 if (answer.equals("no")) 1227 throw new Failure ("Unable to manually kill debugee VM"); 1228 answer = askQuestion("Wrong answer. Please type yes or no", "yes"); 1229 } 1230 } 1231 } 1232 1233 /** Wait until the debugee VM shutdown or crash. */ waitForDebugee()1234 protected int waitForDebugee () { 1235 putMessage("Wait for launched VM to exit."); 1236 String answer = askQuestion("What is VM exit code?", "95"); 1237 for ( ; ; ) { 1238 try { 1239 exitCode = Integer.parseInt(answer); 1240 break; 1241 } catch (NumberFormatException e) { 1242 answer = askQuestion("Wrong answer. Please type integer value", "95"); 1243 } 1244 } 1245 finished = true; 1246 return exitCode; 1247 } 1248 1249 /** Get a pipe to write to the debugee's stdin stream. */ getInPipe()1250 protected OutputStream getInPipe () { 1251 return null; 1252 } 1253 1254 /** Get a pipe to read the debugee's stdout stream. */ getOutPipe()1255 protected InputStream getOutPipe () { 1256 return null; 1257 } 1258 1259 /** Get a pipe to read the debugee's stderr stream. */ getErrPipe()1260 protected InputStream getErrPipe () { 1261 return null; 1262 } 1263 redirectStdout(OutputStream out)1264 public void redirectStdout(OutputStream out) { 1265 } 1266 redirectStdout(Log log, String prefix)1267 public void redirectStdout(Log log, String prefix) { 1268 } 1269 redirectStderr(OutputStream out)1270 public void redirectStderr(OutputStream out) { 1271 } 1272 redirectStderr(Log log, String prefix)1273 public void redirectStderr(Log log, String prefix) { 1274 } 1275 close()1276 public void close() { 1277 destroyInputReader(); 1278 super.close(); 1279 } 1280 } 1281