1 /* 2 * Copyright (c) 2005, 2014, 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 sun.tools.attach; 27 28 import com.sun.tools.attach.VirtualMachine; 29 import com.sun.tools.attach.AgentLoadException; 30 import com.sun.tools.attach.AgentInitializationException; 31 import com.sun.tools.attach.spi.AttachProvider; 32 33 import java.io.BufferedReader; 34 import java.io.InputStream; 35 import java.io.IOException; 36 import java.io.InputStreamReader; 37 import java.util.Properties; 38 import java.util.stream.Collectors; 39 40 /* 41 * The HotSpot implementation of com.sun.tools.attach.VirtualMachine. 42 */ 43 44 public abstract class HotSpotVirtualMachine extends VirtualMachine { 45 46 HotSpotVirtualMachine(AttachProvider provider, String id) { 47 super(provider, id); 48 } 49 50 /* 51 * Load agent library 52 * If isAbsolute is true then the agent library is the absolute path 53 * to the library and thus will not be expanded in the target VM. 54 * if isAbsolute is false then the agent library is just a library 55 * name and it will be expended in the target VM. 56 */ 57 private void loadAgentLibrary(String agentLibrary, boolean isAbsolute, String options) 58 throws AgentLoadException, AgentInitializationException, IOException 59 { 60 InputStream in = execute("load", 61 agentLibrary, 62 isAbsolute ? "true" : "false", 63 options); 64 try { 65 int result = readInt(in); 66 if (result != 0) { 67 throw new AgentInitializationException("Agent_OnAttach failed", result); 68 } 69 } finally { 70 in.close(); 71 72 } 73 } 74 75 /* 76 * Load agent library - library name will be expanded in target VM 77 */ 78 public void loadAgentLibrary(String agentLibrary, String options) 79 throws AgentLoadException, AgentInitializationException, IOException 80 { 81 loadAgentLibrary(agentLibrary, false, options); 82 } 83 84 /* 85 * Load agent - absolute path of library provided to target VM 86 */ 87 public void loadAgentPath(String agentLibrary, String options) 88 throws AgentLoadException, AgentInitializationException, IOException 89 { 90 loadAgentLibrary(agentLibrary, true, options); 91 } 92 93 /* 94 * Load JPLIS agent which will load the agent JAR file and invoke 95 * the agentmain method. 96 */ 97 public void loadAgent(String agent, String options) 98 throws AgentLoadException, AgentInitializationException, IOException 99 { 100 String args = agent; 101 if (options != null) { 102 args = args + "=" + options; 103 } 104 try { 105 loadAgentLibrary("instrument", args); 106 } catch (AgentLoadException x) { 107 throw new InternalError("instrument library is missing in target VM", x); 108 } catch (AgentInitializationException x) { 109 /* 110 * Translate interesting errors into the right exception and 111 * message (FIXME: create a better interface to the instrument 112 * implementation so this isn't necessary) 113 */ 114 int rc = x.returnValue(); 115 switch (rc) { 116 case JNI_ENOMEM: 117 throw new AgentLoadException("Insuffient memory"); 118 case ATTACH_ERROR_BADJAR: 119 throw new AgentLoadException("Agent JAR not found or no Agent-Class attribute"); 120 case ATTACH_ERROR_NOTONCP: 121 throw new AgentLoadException("Unable to add JAR file to system class path"); 122 case ATTACH_ERROR_STARTFAIL: 123 throw new AgentInitializationException("Agent JAR loaded but agent failed to initialize"); 124 default : 125 throw new AgentLoadException("Failed to load agent - unknown reason: " + rc); 126 } 127 } 128 } 129 130 /* 131 * The possible errors returned by JPLIS's agentmain 132 */ 133 private static final int JNI_ENOMEM = -4; 134 private static final int ATTACH_ERROR_BADJAR = 100; 135 private static final int ATTACH_ERROR_NOTONCP = 101; 136 private static final int ATTACH_ERROR_STARTFAIL = 102; 137 138 139 /* 140 * Send "properties" command to target VM 141 */ 142 public Properties getSystemProperties() throws IOException { 143 InputStream in = null; 144 Properties props = new Properties(); 145 try { 146 in = executeCommand("properties"); 147 props.load(in); 148 } finally { 149 if (in != null) in.close(); 150 } 151 return props; 152 } 153 154 public Properties getAgentProperties() throws IOException { 155 InputStream in = null; 156 Properties props = new Properties(); 157 try { 158 in = executeCommand("agentProperties"); 159 props.load(in); 160 } finally { 161 if (in != null) in.close(); 162 } 163 return props; 164 } 165 166 private static final String MANAGMENT_PREFIX = "com.sun.management."; 167 168 private static boolean checkedKeyName(Object key) { 169 if (!(key instanceof String)) { 170 throw new IllegalArgumentException("Invalid option (not a String): "+key); 171 } 172 if (!((String)key).startsWith(MANAGMENT_PREFIX)) { 173 throw new IllegalArgumentException("Invalid option: "+key); 174 } 175 return true; 176 } 177 178 private static String stripKeyName(Object key) { 179 return ((String)key).substring(MANAGMENT_PREFIX.length()); 180 } 181 182 @Override 183 public void startManagementAgent(Properties agentProperties) throws IOException { 184 if (agentProperties == null) { 185 throw new NullPointerException("agentProperties cannot be null"); 186 } 187 // Convert the arguments into arguments suitable for the Diagnostic Command: 188 // "ManagementAgent.start jmxremote.port=5555 jmxremote.authenticate=false" 189 String args = agentProperties.entrySet().stream() 190 .filter(entry -> checkedKeyName(entry.getKey())) 191 .map(entry -> stripKeyName(entry.getKey()) + "=" + escape(entry.getValue())) 192 .collect(Collectors.joining(" ")); 193 executeJCmd("ManagementAgent.start " + args).close(); 194 } 195 196 private String escape(Object arg) { 197 String value = arg.toString(); 198 if (value.contains(" ")) { 199 return "'" + value + "'"; 200 } 201 return value; 202 } 203 204 @Override 205 public String startLocalManagementAgent() throws IOException { 206 executeJCmd("ManagementAgent.start_local").close(); 207 return getAgentProperties().getProperty("com.sun.management.jmxremote.localConnectorAddress"); 208 } 209 210 // --- HotSpot specific methods --- 211 212 // same as SIGQUIT 213 public void localDataDump() throws IOException { 214 executeCommand("datadump").close(); 215 } 216 217 // Remote ctrl-break. The output of the ctrl-break actions can 218 // be read from the input stream. 219 public InputStream remoteDataDump(Object ... args) throws IOException { 220 return executeCommand("threaddump", args); 221 } 222 223 // Remote heap dump. The output (error message) can be read from the 224 // returned input stream. 225 public InputStream dumpHeap(Object ... args) throws IOException { 226 return executeCommand("dumpheap", args); 227 } 228 229 // Heap histogram (heap inspection in HotSpot) 230 public InputStream heapHisto(Object ... args) throws IOException { 231 return executeCommand("inspectheap", args); 232 } 233 234 // set JVM command line flag 235 public InputStream setFlag(String name, String value) throws IOException { 236 return executeCommand("setflag", name, value); 237 } 238 239 // print command line flag 240 public InputStream printFlag(String name) throws IOException { 241 return executeCommand("printflag", name); 242 } 243 244 public InputStream executeJCmd(String command) throws IOException { 245 return executeCommand("jcmd", command); 246 } 247 248 // -- Supporting methods 249 250 251 /* 252 * Execute the given command in the target VM - specific platform 253 * implementation must implement this. 254 */ 255 abstract InputStream execute(String cmd, Object ... args) 256 throws AgentLoadException, IOException; 257 258 /* 259 * Convenience method for simple commands 260 */ 261 public InputStream executeCommand(String cmd, Object ... args) throws IOException { 262 try { 263 return execute(cmd, args); 264 } catch (AgentLoadException x) { 265 throw new InternalError("Should not get here", x); 266 } 267 } 268 269 270 /* 271 * Utility method to read an 'int' from the input stream. Ideally 272 * we should be using java.util.Scanner here but this implementation 273 * guarantees not to read ahead. 274 */ 275 int readInt(InputStream in) throws IOException { 276 StringBuilder sb = new StringBuilder(); 277 278 // read to \n or EOF 279 int n; 280 byte buf[] = new byte[1]; 281 do { 282 n = in.read(buf, 0, 1); 283 if (n > 0) { 284 char c = (char)buf[0]; 285 if (c == '\n') { 286 break; // EOL found 287 } else { 288 sb.append(c); 289 } 290 } 291 } while (n > 0); 292 293 if (sb.length() == 0) { 294 throw new IOException("Premature EOF"); 295 } 296 297 int value; 298 try { 299 value = Integer.parseInt(sb.toString()); 300 } catch (NumberFormatException x) { 301 throw new IOException("Non-numeric value found - int expected"); 302 } 303 return value; 304 } 305 306 /* 307 * Utility method to read data into a String. 308 */ 309 String readErrorMessage(InputStream sis) throws IOException { 310 String s; 311 StringBuilder message = new StringBuilder(); 312 BufferedReader br = new BufferedReader(new InputStreamReader(sis)); 313 while ((s = br.readLine()) != null) { 314 message.append(s); 315 } 316 return message.toString(); 317 } 318 319 320 // -- attach timeout support 321 322 private static long defaultAttachTimeout = 5000; 323 private volatile long attachTimeout; 324 325 /* 326 * Return attach timeout based on the value of the sun.tools.attach.attachTimeout 327 * property, or the default timeout if the property is not set to a positive 328 * value. 329 */ 330 long attachTimeout() { 331 if (attachTimeout == 0) { 332 synchronized(this) { 333 if (attachTimeout == 0) { 334 try { 335 String s = 336 System.getProperty("sun.tools.attach.attachTimeout"); 337 attachTimeout = Long.parseLong(s); 338 } catch (SecurityException se) { 339 } catch (NumberFormatException ne) { 340 } 341 if (attachTimeout <= 0) { 342 attachTimeout = defaultAttachTimeout; 343 } 344 } 345 } 346 } 347 return attachTimeout; 348 } 349 }