/* * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. */ package sun.tools.attach; import java.io.*; import java.util.*; import com.sun.tools.attach.*; import com.sun.tools.attach.spi.*; /** * The HotSpot implementation of com.sun.tools.attach.VirtualMachine. */ public abstract class HotSpotVirtualMachine extends VirtualMachine { HotSpotVirtualMachine(AttachProvider provider, String id) { super(provider, id); } /** * Load agent library. * If isAbsolute is true then the agent library is the absolute path to the library and thus * will not be expanded in the target VM. * If isAbsolute is false then the agent library is just a library name and it will be expended * in the target VM. */ private void loadAgentLibrary(String agentLibrary, boolean isAbsolute, String options) throws AgentLoadException, AgentInitializationException, IOException { InputStream in = execute("load", agentLibrary, isAbsolute ? "true" : "false", options); try { int result = readInt(in); if (result != 0) { throw new AgentInitializationException("Agent_OnAttach failed", result); } } finally { in.close(); } } /** * Load agent library - library name will be expanded in target VM. */ @Override public void loadAgentLibrary(String agentLibrary, String options) throws AgentLoadException, AgentInitializationException, IOException { loadAgentLibrary(agentLibrary, false, options); } /** * Load agent - absolute path of library provided to target VM. */ @Override public void loadAgentPath(String agentLibrary, String options) throws AgentLoadException, AgentInitializationException, IOException { loadAgentLibrary(agentLibrary, true, options); } /** * Load JPLIS agent which will load the agent JAR file and invoke the agentmain method. */ @Override public void loadAgent(String agent, String options) throws AgentLoadException, AgentInitializationException, IOException { String args = agent; if (options != null) { args = args + '=' + options; } try { loadAgentLibrary("instrument", args); } catch (AgentLoadException ignore) { throw new InternalError("instrument library is missing in target VM"); } catch (AgentInitializationException x) { /* * Translate interesting errors into the right exception and message * (FIXME: create a better interface to the instrument implementation so this isn't * necessary). */ int rc = x.returnValue(); switch (rc) { case JNI_ENOMEM: throw new AgentLoadException("Insufficient memory"); case ATTACH_ERROR_BADJAR: throw new AgentLoadException("Agent JAR not found or no Agent-Class attribute"); case ATTACH_ERROR_NOTONCP: throw new AgentLoadException("Unable to add JAR file to system class path"); case ATTACH_ERROR_STARTFAIL: throw new AgentInitializationException( "Agent JAR loaded but agent failed to initialize"); default: throw new AgentLoadException("Failed to load agent - unknown reason: " + rc); } } } /* * The possible errors returned by JPLIS's agentmain. */ private static final int JNI_ENOMEM = -4; private static final int ATTACH_ERROR_BADJAR = 100; private static final int ATTACH_ERROR_NOTONCP = 101; private static final int ATTACH_ERROR_STARTFAIL = 102; /** * Send "properties" command to target VM. */ @Override public Properties getSystemProperties() throws IOException { InputStream in = null; Properties props = new Properties(); try { in = executeCommand("properties"); props.load(in); } finally { if (in != null) { in.close(); } } return props; } @Override public Properties getAgentProperties() throws IOException { InputStream in = null; Properties props = new Properties(); try { in = executeCommand("agentProperties"); props.load(in); } finally { if (in != null) { in.close(); } } return props; } // --- HotSpot specific methods --- // same as SIGQUIT public void localDataDump() throws IOException { executeCommand("datadump").close(); } // Remote ctrl-break. The output of the ctrl-break actions can be read from the input stream. public InputStream remoteDataDump(Object... args) throws IOException { return executeCommand("threaddump", args); } // Remote heap dump. The output (error message) can be read from the returned input stream. public InputStream dumpHeap(Object... args) throws IOException { return executeCommand("dumpheap", args); } // Heap histogram (heap inspection in HotSpot). public InputStream heapHisto(Object... args) throws IOException { return executeCommand("inspectheap", args); } // Set JVM command line flag. public InputStream setFlag(String name, String value) throws IOException { return executeCommand("setflag", name, value); } // Print command line flag. public InputStream printFlag(String name) throws IOException { return executeCommand("printflag", name); } // -- Supporting methods /** * Execute the given command in the target VM - specific platform implementation must implement * this. */ abstract InputStream execute(String cmd, Object... args) throws AgentLoadException, IOException; /** * Convenience method for simple commands. */ private InputStream executeCommand(String cmd, Object... args) throws IOException { try { return execute(cmd, args); } catch (AgentLoadException ignore) { throw new InternalError("Should not get here"); } } /** * Utility method to read an 'int' from the input stream. * Ideally we should be using java.util.Scanner here but this implementation guarantees not to * read ahead. */ int readInt(InputStream in) throws IOException { StringBuilder sb = new StringBuilder(); // read to \n or EOF int n; byte[] buf = new byte[1]; do { n = in.read(buf, 0, 1); if (n > 0) { char c = (char) buf[0]; if (c == '\n') { break; // EOL found } else { sb.append(c); } } } while (n > 0); if (sb.length() == 0) { throw new IOException("Premature EOF"); } int value; try { value = Integer.parseInt(sb.toString()); } catch (NumberFormatException ignore) { throw new IOException("Non-numeric value found - int expected"); } return value; } // -- attach timeout support private static final long defaultAttachTimeout = 5000; private volatile long attachTimeout; /** * Return attach timeout based on the value of the sun.tools.attach.attachTimeout * property, or the default timeout if the property is not set to a positive value. */ long attachTimeout() { if (attachTimeout == 0) { synchronized (this) { if (attachTimeout == 0) { try { String s = System.getProperty("sun.tools.attach.attachTimeout"); attachTimeout = Long.parseLong(s); } catch (SecurityException ignore) {} catch (NumberFormatException ignore) {} if (attachTimeout <= 0) { attachTimeout = defaultAttachTimeout; } } } } return attachTimeout; } }