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