1 /* 2 * Copyright (c) 2001, 2018, 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.jdwp; 25 26 import nsk.share.*; 27 import nsk.share.jpda.*; 28 29 import java.io.*; 30 31 /** 32 * This class provides debugger with connection to debugee VM 33 * using JDWP protocol. 34 * <p> 35 * This class provides abilities to launch and bind to debugee VM 36 * as described for base <code>DebugeeBinder</code> class, 37 * using raw JDWP protocol. 38 * <p> 39 * When <code>Binder</code> is asked to bind to debugee by invoking 40 * <code>bindToBebugee()</code> method it launches process 41 * with debugee VM and makes connection to it using JDWP transport 42 * corresponding to value of command line options <code>-connector</code> 43 * and <code>-transport</code>. 44 * After debugee is launched and connection is established 45 * <code>Binder</code> constructs <code>Debugee</code> object, 46 * that provides abilities to interact with debugee VM. 47 * 48 * @see Debugee 49 * @see DebugeeBinder 50 */ 51 final public class Binder extends DebugeeBinder { 52 53 /** 54 * Default message prefix for <code>Binder</code> object. 55 */ 56 public static final String LOG_PREFIX = "binder> "; 57 58 /** 59 * Get version string. 60 */ getVersion()61 public static String getVersion () { 62 return "@(#)Binder.java %I% %E%"; 63 } 64 65 // -------------------------------------------------- // 66 67 /** 68 * Handler of command line arguments. 69 */ 70 private ArgumentHandler argumentHandler = null; 71 72 /** 73 * Return <code>argumentHandler</code> of this binder. 74 */ getArgumentHandler()75 public ArgumentHandler getArgumentHandler() { 76 return argumentHandler; 77 } 78 79 // -------------------------------------------------- // 80 81 /** 82 * Make new <code>Binder</code> object with specified 83 * <code>argumentHandler</code> and <code>log</code>. 84 */ Binder(ArgumentHandler argumentHandler, Log log)85 public Binder (ArgumentHandler argumentHandler, Log log) { 86 super(argumentHandler, log); 87 this.argumentHandler = argumentHandler; 88 } 89 90 // -------------------------------------------------- // 91 92 /** 93 * Start debugee VM and establish JDWP connection to it. 94 */ bindToDebugee(String classToExecute)95 public Debugee bindToDebugee (String classToExecute) { 96 97 Debugee debugee = null; 98 99 prepareForPipeConnection(argumentHandler); 100 101 if (argumentHandler.isLaunchedRemotely()) { 102 connectToBindServer(classToExecute); 103 debugee = launchDebugee(classToExecute); 104 } else { 105 debugee = launchDebugee(classToExecute); 106 debugee.redirectOutput(log); 107 } 108 109 Finalizer finalizer = new Finalizer(debugee); 110 finalizer.activate(); 111 112 Transport transport = debugee.connect(); 113 114 return debugee; 115 } 116 117 /** 118 * Launch debugee VM for specified class. 119 */ launchDebugee(String classToExecute)120 public Debugee launchDebugee (String classToExecute) { 121 122 try { 123 124 if (argumentHandler.isLaunchedLocally()) { 125 LocalLaunchedDebugee debugee = new LocalLaunchedDebugee(this); 126 String address = debugee.prepareTransport(argumentHandler); 127 if (address == null) 128 address = makeTransportAddress(); 129 String[] argsArray = makeCommandLineArgs(classToExecute, address); 130 debugee.launch(argsArray); 131 return debugee; 132 } 133 134 if (argumentHandler.isLaunchedRemotely()) { 135 RemoteLaunchedDebugee debugee = new RemoteLaunchedDebugee(this); 136 String address = debugee.prepareTransport(argumentHandler); 137 if (address == null) 138 address = makeTransportAddress(); 139 String[] argsArray = makeCommandLineArgs(classToExecute, address); 140 debugee.launch(argsArray); 141 return debugee; 142 } 143 144 if (argumentHandler.isLaunchedManually()) { 145 ManualLaunchedDebugee debugee = new ManualLaunchedDebugee(this); 146 String address = debugee.prepareTransport(argumentHandler); 147 if (address == null) 148 address = makeTransportAddress(); 149 String cmdLine = makeCommandLineString(classToExecute, address, "\""); 150 debugee.launch(cmdLine); 151 return debugee; 152 } 153 154 throw new TestBug("Unexpected launching mode: " 155 + argumentHandler.getLaunchMode()); 156 } catch (IOException e) { 157 e.printStackTrace(log.getOutStream()); 158 throw new Failure("Caught exception while launching debugee:\n\t" + e); 159 } 160 } 161 162 } 163 164 /** 165 * Mirror of locally launched debugee. 166 */ 167 final class LocalLaunchedDebugee extends Debugee { 168 169 /** Enwrap the existing <code>VM</code> mirror. */ LocalLaunchedDebugee(Binder binder)170 public LocalLaunchedDebugee (Binder binder) { 171 super(binder); 172 checkTermination = true; 173 } 174 175 // ---------------------------------------------- // 176 launch(String[] args)177 public void launch(String[] args) throws IOException { 178 String cmdLine = ArgumentHandler.joinArguments(args, "\""); 179 display("Starting java process:\n" + cmdLine); 180 process = binder.launchProcess(args); 181 } 182 183 /** Return exit status of the debugee VM. */ getStatus()184 public int getStatus () { 185 return process.exitValue(); 186 } 187 188 /** Check whether the debugee VM has been terminated. */ terminated()189 public boolean terminated () { 190 if (process == null) 191 return true; 192 193 try { 194 int value = process.exitValue(); 195 return true; 196 } catch (IllegalThreadStateException e) { 197 return false; 198 } 199 } 200 201 // ---------------------------------------------- // 202 203 /** Kill the debugee VM. */ killDebugee()204 protected void killDebugee () { 205 super.killDebugee(); 206 if (!terminated()) { 207 log.display("Killing debugee VM process"); 208 process.destroy(); 209 } 210 } 211 212 /** Wait until the debugee VM shutdown or crash. */ waitForDebugee()213 protected int waitForDebugee () throws InterruptedException { 214 return process.waitFor(); 215 } 216 217 /** Get a pipe to write to the debugee's stdin stream. */ getInPipe()218 protected OutputStream getInPipe () { 219 return process.getOutputStream(); 220 } 221 222 /** Get a pipe to read the debugee's stdout stream. */ getOutPipe()223 protected InputStream getOutPipe () { 224 return process.getInputStream(); 225 } 226 227 /** Get a pipe to read the debugee's stderr stream. */ getErrPipe()228 protected InputStream getErrPipe () { 229 return process.getErrorStream(); 230 } 231 } 232 233 234 /** 235 * Mirror of remotely launched debugee. 236 */ 237 final class RemoteLaunchedDebugee extends Debugee { 238 239 /** Enwrap the existing <code>VM</code> mirror. */ RemoteLaunchedDebugee(Binder binder)240 public RemoteLaunchedDebugee (Binder binder) { 241 super(binder); 242 } 243 244 // ---------------------------------------------- // 245 launch(String[] args)246 public void launch(String[] args) throws IOException { 247 String cmdLine = ArgumentHandler.joinArguments(args, "\""); 248 display("Starting remote java process:\n" + cmdLine); 249 binder.launchRemoteProcess(args); 250 } 251 252 /** Return exit status of the debugee VM. */ getStatus()253 public int getStatus () { 254 return binder.getRemoteProcessStatus(); 255 } 256 257 /** Check whether the debugee VM has been terminated. */ terminated()258 public boolean terminated () { 259 return binder.isRemoteProcessTerminated(); 260 } 261 262 // ---------------------------------------------- // 263 264 /** Kill the debugee VM. */ killDebugee()265 protected void killDebugee () { 266 super.killDebugee(); 267 if (!terminated()) { 268 log.display("Killing debugee VM process"); 269 binder.killRemoteProcess(); 270 } 271 } 272 273 /** Wait until the debugee VM shutdown or crash. */ waitForDebugee()274 protected int waitForDebugee () { 275 return binder.waitForRemoteProcess(); 276 } 277 278 /** Get a pipe to write to the debugee's stdin stream. */ getInPipe()279 protected OutputStream getInPipe () { 280 return null; 281 } 282 283 /** Get a pipe to read the debugee's stdout stream. */ getOutPipe()284 protected InputStream getOutPipe () { 285 return null; 286 } 287 288 /** Get a pipe to read the debugee's stderr stream. */ getErrPipe()289 protected InputStream getErrPipe () { 290 return null; 291 } 292 redirectStdout(OutputStream out)293 public void redirectStdout(OutputStream out) { 294 } 295 redirectStdout(Log log, String prefix)296 public void redirectStdout(Log log, String prefix) { 297 } 298 redirectStderr(OutputStream out)299 public void redirectStderr(OutputStream out) { 300 } 301 redirectStderr(Log log, String prefix)302 public void redirectStderr(Log log, String prefix) { 303 } 304 } 305 306 307 /** 308 * Mirror of manually launched debugee. 309 */ 310 final class ManualLaunchedDebugee extends Debugee { 311 312 private int exitCode = 0; 313 private boolean finished = false; 314 private static BufferedReader bin = new BufferedReader(new InputStreamReader(System.in)); 315 316 /** Enwrap the existing <code>VM</code> mirror. */ ManualLaunchedDebugee(Binder binder)317 public ManualLaunchedDebugee (Binder binder) { 318 super(binder); 319 } 320 321 // ---------------------------------------------- // 322 launch(String commandLine)323 public void launch(String commandLine) throws IOException { 324 putMessage("Launch target VM using such command line:\n" 325 + commandLine); 326 String answer = askQuestion("Has the VM successfully started? (yes/no)", "yes"); 327 for ( ; ; ) { 328 if (answer.equals("yes")) 329 break; 330 if (answer.equals("no")) 331 throw new Failure ("Unable to manually launch debugee VM"); 332 answer = askQuestion("Wrong answer. Please type yes or no", "yes"); 333 } 334 } 335 putMessage(String msg)336 private void putMessage(String msg) { 337 System.out.println("\n>>> " + msg); 338 } 339 askQuestion(String question, String defaultAnswer)340 private String askQuestion(String question, String defaultAnswer) { 341 try { 342 System.out.print("\n>>> " + question); 343 System.out.print(" [" + defaultAnswer + "] "); 344 System.out.flush(); 345 String answer = bin.readLine(); 346 if (answer.equals("")) 347 return defaultAnswer; 348 return answer; 349 } catch (IOException e) { 350 e.printStackTrace(log.getOutStream()); 351 throw new Failure("Caught exception while reading answer:\n\t" + e); 352 } 353 } 354 355 /** Return exit status of the debugee VM. */ getStatus()356 public int getStatus () { 357 if (! terminated()) { 358 throw new Failure("Unable to get status of debugee VM: process still alive"); 359 } 360 return exitCode; 361 } 362 363 /** Check whether the debugee VM has been terminated. */ terminated()364 public boolean terminated () { 365 if(! finished) { 366 String answer = askQuestion("Has the VM exited?", "no"); 367 for ( ; ; ) { 368 if (answer.equals("no")) 369 return false; 370 if (answer.equals("yes")) { 371 finished = true; 372 waitForDebugee(); 373 break; 374 } 375 answer = askQuestion("Wrong answer. Please type yes or no", "yes"); 376 } 377 } 378 return finished; 379 } 380 381 // ---------------------------------------------- // 382 383 /** Kill the debugee VM. */ killDebugee()384 protected void killDebugee () { 385 super.killDebugee(); 386 if (!terminated()) { 387 putMessage("Kill launched VM"); 388 String answer = askQuestion("Has the VM successfully terminated? (yes/no)", "yes"); 389 for ( ; ; ) { 390 if (answer.equals("yes")) { 391 finished = true; 392 break; 393 } 394 if (answer.equals("no")) 395 throw new Failure ("Unable to manually kill debugee VM"); 396 answer = askQuestion("Wrong answer. Please type yes or no", "yes"); 397 } 398 } 399 } 400 401 /** Wait until the debugee VM shutdown or crash. */ waitForDebugee()402 protected int waitForDebugee () { 403 putMessage("Wait for launched VM to exit."); 404 String answer = askQuestion("What is VM exit code?", "95"); 405 for ( ; ; ) { 406 try { 407 exitCode = Integer.parseInt(answer); 408 break; 409 } catch (NumberFormatException e) { 410 answer = askQuestion("Wrong answer. Please type integer value", "95"); 411 } 412 } 413 finished = true; 414 return exitCode; 415 } 416 417 /** Get a pipe to write to the debugee's stdin stream. */ getInPipe()418 protected OutputStream getInPipe () { 419 return null; 420 } 421 422 /** Get a pipe to read the debugee's stdout stream. */ getOutPipe()423 protected InputStream getOutPipe () { 424 return null; 425 } 426 427 /** Get a pipe to read the debugee's stderr stream. */ getErrPipe()428 protected InputStream getErrPipe () { 429 return null; 430 } 431 redirectStdout(OutputStream out)432 public void redirectStdout(OutputStream out) { 433 } 434 redirectStdout(Log log, String prefix)435 public void redirectStdout(Log log, String prefix) { 436 } 437 redirectStderr(OutputStream out)438 public void redirectStderr(OutputStream out) { 439 } 440 redirectStderr(Log log, String prefix)441 public void redirectStderr(Log log, String prefix) { 442 } 443 close()444 public void close() { 445 try { 446 bin.close(); 447 } catch (IOException e) { 448 log.display("WARNING: Caught IOException while closing InputStream"); 449 } 450 bin = null; 451 super.close(); 452 } 453 } 454