1 /* 2 * Copyright (c) 1998, 2013, 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.jdi; 27 28 import com.sun.tools.jdi.*; 29 import com.sun.jdi.connect.*; 30 import com.sun.jdi.connect.spi.*; 31 import com.sun.jdi.VirtualMachine; 32 import java.util.Map; 33 import java.util.HashMap; 34 import java.util.Random; 35 import java.io.IOException; 36 import java.io.File; 37 38 public class SunCommandLineLauncher extends AbstractLauncher implements LaunchingConnector { 39 40 static private final String ARG_HOME = "home"; 41 static private final String ARG_OPTIONS = "options"; 42 static private final String ARG_MAIN = "main"; 43 static private final String ARG_INIT_SUSPEND = "suspend"; 44 static private final String ARG_QUOTE = "quote"; 45 static private final String ARG_VM_EXEC = "vmexec"; 46 47 TransportService transportService; 48 Transport transport; 49 boolean usingSharedMemory = false; 50 transportService()51 TransportService transportService() { 52 return transportService; 53 } 54 transport()55 public Transport transport() { 56 return transport; 57 } 58 SunCommandLineLauncher()59 public SunCommandLineLauncher() { 60 super(); 61 62 /** 63 * By default this connector uses either the shared memory 64 * transport or the socket transport 65 */ 66 try { 67 Class<?> c = Class.forName("com.sun.tools.jdi.SharedMemoryTransportService"); 68 transportService = (TransportService)c.newInstance(); 69 transport = new Transport() { 70 public String name() { 71 return "dt_shmem"; 72 } 73 }; 74 usingSharedMemory = true; 75 } catch (ClassNotFoundException x) { 76 } catch (UnsatisfiedLinkError x) { 77 } catch (InstantiationException x) { 78 } catch (IllegalAccessException x) { 79 }; 80 if (transportService == null) { 81 transportService = new SocketTransportService(); 82 transport = new Transport() { 83 public String name() { 84 return "dt_socket"; 85 } 86 }; 87 } 88 89 addStringArgument( 90 ARG_HOME, 91 getString("sun.home.label"), 92 getString("sun.home"), 93 System.getProperty("java.home"), 94 false); 95 addStringArgument( 96 ARG_OPTIONS, 97 getString("sun.options.label"), 98 getString("sun.options"), 99 "", 100 false); 101 addStringArgument( 102 ARG_MAIN, 103 getString("sun.main.label"), 104 getString("sun.main"), 105 "", 106 true); 107 108 addBooleanArgument( 109 ARG_INIT_SUSPEND, 110 getString("sun.init_suspend.label"), 111 getString("sun.init_suspend"), 112 true, 113 false); 114 115 addStringArgument( 116 ARG_QUOTE, 117 getString("sun.quote.label"), 118 getString("sun.quote"), 119 "\"", 120 true); 121 addStringArgument( 122 ARG_VM_EXEC, 123 getString("sun.vm_exec.label"), 124 getString("sun.vm_exec"), 125 "java", 126 true); 127 } 128 hasWhitespace(String string)129 static boolean hasWhitespace(String string) { 130 int length = string.length(); 131 for (int i = 0; i < length; i++) { 132 if (Character.isWhitespace(string.charAt(i))) { 133 return true; 134 } 135 } 136 return false; 137 } 138 139 public VirtualMachine launch(Map<String,? extends Connector.Argument> arguments)140 launch(Map<String,? extends Connector.Argument> arguments) 141 throws IOException, IllegalConnectorArgumentsException, 142 VMStartException 143 { 144 VirtualMachine vm; 145 146 String home = argument(ARG_HOME, arguments).value(); 147 String options = argument(ARG_OPTIONS, arguments).value(); 148 String mainClassAndArgs = argument(ARG_MAIN, arguments).value(); 149 boolean wait = ((BooleanArgumentImpl)argument(ARG_INIT_SUSPEND, 150 arguments)).booleanValue(); 151 String quote = argument(ARG_QUOTE, arguments).value(); 152 String exe = argument(ARG_VM_EXEC, arguments).value(); 153 String exePath = null; 154 155 if (quote.length() > 1) { 156 throw new IllegalConnectorArgumentsException("Invalid length", 157 ARG_QUOTE); 158 } 159 160 if ((options.indexOf("-Djava.compiler=") != -1) && 161 (options.toLowerCase().indexOf("-djava.compiler=none") == -1)) { 162 throw new IllegalConnectorArgumentsException("Cannot debug with a JIT compiler", 163 ARG_OPTIONS); 164 } 165 166 /* 167 * Start listening. 168 * If we're using the shared memory transport then we pick a 169 * random address rather than using the (fixed) default. 170 * Random() uses System.currentTimeMillis() as the seed 171 * which can be a problem on windows (many calls to 172 * currentTimeMillis can return the same value), so 173 * we do a few retries if we get an IOException (we 174 * assume the IOException is the filename is already in use.) 175 */ 176 TransportService.ListenKey listenKey; 177 if (usingSharedMemory) { 178 Random rr = new Random(); 179 int failCount = 0; 180 while(true) { 181 try { 182 String address = "javadebug" + 183 String.valueOf(rr.nextInt(100000)); 184 listenKey = transportService().startListening(address); 185 break; 186 } catch (IOException ioe) { 187 if (++failCount > 5) { 188 throw ioe; 189 } 190 } 191 } 192 } else { 193 listenKey = transportService().startListening(); 194 } 195 String address = listenKey.address(); 196 197 try { 198 if (home.length() > 0) { 199 exePath = home + File.separator + "bin" + File.separator + exe; 200 } else { 201 exePath = exe; 202 } 203 // Quote only if necessary in case the quote arg value is bogus 204 if (hasWhitespace(exePath)) { 205 exePath = quote + exePath + quote; 206 } 207 208 String xrun = "transport=" + transport().name() + 209 ",address=" + address + 210 ",suspend=" + (wait? 'y' : 'n'); 211 // Quote only if necessary in case the quote arg value is bogus 212 if (hasWhitespace(xrun)) { 213 xrun = quote + xrun + quote; 214 } 215 216 String command = exePath + ' ' + 217 options + ' ' + 218 "-Xdebug " + 219 "-Xrunjdwp:" + xrun + ' ' + 220 mainClassAndArgs; 221 222 // System.err.println("Command: \"" + command + '"'); 223 vm = launch(tokenizeCommand(command, quote.charAt(0)), address, listenKey, 224 transportService()); 225 } finally { 226 transportService().stopListening(listenKey); 227 } 228 229 return vm; 230 } 231 name()232 public String name() { 233 return "com.sun.jdi.CommandLineLaunch"; 234 } 235 description()236 public String description() { 237 return getString("sun.description"); 238 239 } 240 } 241