001: /*
002: * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.tools.attach;
027:
028: import com.sun.tools.attach.VirtualMachine;
029: import com.sun.tools.attach.AgentLoadException;
030: import com.sun.tools.attach.AgentInitializationException;
031: import com.sun.tools.attach.spi.AttachProvider;
032:
033: import java.io.InputStream;
034: import java.io.IOException;
035: import java.util.Properties;
036: import java.util.Map;
037:
038: /*
039: * The HotSpot implementation of com.sun.tools.attach.VirtualMachine.
040: */
041:
042: public abstract class HotSpotVirtualMachine extends VirtualMachine {
043:
044: HotSpotVirtualMachine(AttachProvider provider, String id) {
045: super (provider, id);
046: }
047:
048: /*
049: * Load agent library
050: * If isAbsolute is true then the agent library is the absolute path
051: * to the library and thus will not be expanded in the target VM.
052: * if isAbsolute is false then the agent library is just a library
053: * name and it will be expended in the target VM.
054: */
055: private void loadAgentLibrary(String agentLibrary,
056: boolean isAbsolute, String options)
057: throws AgentLoadException, AgentInitializationException,
058: IOException {
059: InputStream in = execute("load", agentLibrary,
060: isAbsolute ? "true" : "false", options);
061: try {
062: int result = readInt(in);
063: if (result != 0) {
064: throw new AgentInitializationException(
065: "Agent_OnAttach failed", result);
066: }
067: } finally {
068: in.close();
069:
070: }
071: }
072:
073: /*
074: * Load agent library - library name will be expanded in target VM
075: */
076: public void loadAgentLibrary(String agentLibrary, String options)
077: throws AgentLoadException, AgentInitializationException,
078: IOException {
079: loadAgentLibrary(agentLibrary, false, options);
080: }
081:
082: /*
083: * Load agent - absolute path of library provided to target VM
084: */
085: public void loadAgentPath(String agentLibrary, String options)
086: throws AgentLoadException, AgentInitializationException,
087: IOException {
088: loadAgentLibrary(agentLibrary, true, options);
089: }
090:
091: /*
092: * Load JPLIS agent which will load the agent JAR file and invoke
093: * the agentmain method.
094: */
095: public void loadAgent(String agent, String options)
096: throws AgentLoadException, AgentInitializationException,
097: IOException {
098: String args = agent;
099: if (options != null) {
100: args = args + "=" + options;
101: }
102: try {
103: loadAgentLibrary("instrument", args);
104: } catch (AgentLoadException x) {
105: throw new InternalError(
106: "instrument library is missing in target VM");
107: } catch (AgentInitializationException x) {
108: /*
109: * Translate interesting errors into the right exception and
110: * message (FIXME: create a better interface to the instrument
111: * implementation so this isn't necessary)
112: */
113: int rc = x.returnValue();
114: switch (rc) {
115: case JNI_ENOMEM:
116: throw new AgentLoadException("Insuffient memory");
117: case ATTACH_ERROR_BADJAR:
118: throw new AgentLoadException(
119: "Agent JAR not found or no Agent-Class attribute");
120: case ATTACH_ERROR_NOTONCP:
121: throw new AgentLoadException(
122: "Unable to add JAR file to system class path");
123: case ATTACH_ERROR_STARTFAIL:
124: throw new AgentInitializationException(
125: "Agent JAR loaded but agent failed to initialize");
126: default:
127: throw new AgentLoadException(
128: "Failed to load agent - unknown reason: " + rc);
129: }
130: }
131: }
132:
133: /*
134: * The possible errors returned by JPLIS's agentmain
135: */
136: private static final int JNI_ENOMEM = -4;
137: private static final int ATTACH_ERROR_BADJAR = 100;
138: private static final int ATTACH_ERROR_NOTONCP = 101;
139: private static final int ATTACH_ERROR_STARTFAIL = 102;
140:
141: /*
142: * Send "properties" command to target VM
143: */
144: public Properties getSystemProperties() throws IOException {
145: InputStream in = null;
146: Properties props = new Properties();
147: try {
148: in = executeCommand("properties");
149: props.load(in);
150: } finally {
151: if (in != null)
152: in.close();
153: }
154: return props;
155: }
156:
157: public Properties getAgentProperties() throws IOException {
158: InputStream in = null;
159: Properties props = new Properties();
160: try {
161: in = executeCommand("agentProperties");
162: props.load(in);
163: } finally {
164: if (in != null)
165: in.close();
166: }
167: return props;
168: }
169:
170: // --- HotSpot specific methods ---
171:
172: // same as SIGQUIT
173: public void localDataDump() throws IOException {
174: executeCommand("datadump").close();
175: }
176:
177: // Remote ctrl-break. The output of the ctrl-break actions can
178: // be read from the input stream.
179: public InputStream remoteDataDump(Object... args)
180: throws IOException {
181: return executeCommand("threaddump", args);
182: }
183:
184: // Remote heap dump. The output (error message) can be read from the
185: // returned input stream.
186: public InputStream dumpHeap(Object... args) throws IOException {
187: return executeCommand("dumpheap", args);
188: }
189:
190: // Heap histogram (heap inspection in HotSpot)
191: public InputStream heapHisto(Object... args) throws IOException {
192: return executeCommand("inspectheap", args);
193: }
194:
195: // set JVM command line flag
196: public InputStream setFlag(String name, String value)
197: throws IOException {
198: return executeCommand("setflag", name, value);
199: }
200:
201: // print command line flag
202: public InputStream printFlag(String name) throws IOException {
203: return executeCommand("printflag", name);
204: }
205:
206: // -- Supporting methods
207:
208: /*
209: * Execute the given command in the target VM - specific platform
210: * implementation must implement this.
211: */
212: abstract InputStream execute(String cmd, Object... args)
213: throws AgentLoadException, IOException;
214:
215: /*
216: * Convenience method for simple commands
217: */
218: private InputStream executeCommand(String cmd, Object... args)
219: throws IOException {
220: try {
221: return execute(cmd, args);
222: } catch (AgentLoadException x) {
223: throw new InternalError("Should not get here");
224: }
225: }
226:
227: /*
228: * Utility method to read an 'int' from the input stream. Ideally
229: * we should be using java.util.Scanner here but this implementation
230: * guarantees not to read ahead.
231: */
232: int readInt(InputStream in) throws IOException {
233: StringBuilder sb = new StringBuilder();
234:
235: // read to \n or EOF
236: int n;
237: byte buf[] = new byte[1];
238: do {
239: n = in.read(buf, 0, 1);
240: if (n > 0) {
241: char c = (char) buf[0];
242: if (c == '\n') {
243: break; // EOL found
244: } else {
245: sb.append(c);
246: }
247: }
248: } while (n > 0);
249:
250: if (sb.length() == 0) {
251: throw new IOException("Premature EOF");
252: }
253:
254: int value;
255: try {
256: value = Integer.parseInt(sb.toString());
257: } catch (NumberFormatException x) {
258: throw new IOException(
259: "Non-numeric value found - int expected");
260: }
261: return value;
262: }
263:
264: // -- attach timeout support
265:
266: private static long defaultAttachTimeout = 5000;
267: private volatile long attachTimeout;
268:
269: /*
270: * Return attach timeout based on the value of the sun.tools.attach.attachTimeout
271: * property, or the default timeout if the property is not set to a positive
272: * value.
273: */
274: long attachTimeout() {
275: if (attachTimeout == 0) {
276: synchronized (this ) {
277: if (attachTimeout == 0) {
278: try {
279: String s = (String) System
280: .getProperty("sun.tools.attach.attachTimeout");
281: attachTimeout = Long.parseLong(s);
282: } catch (SecurityException se) {
283: } catch (NumberFormatException ne) {
284: }
285: if (attachTimeout <= 0) {
286: attachTimeout = defaultAttachTimeout;
287: }
288: }
289: }
290: }
291: return attachTimeout;
292: }
293: }
|