001: /*
002: * Copyright 2003-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.instrument;
027:
028: import java.lang.reflect.Method;
029: import java.lang.reflect.AccessibleObject;
030:
031: import java.lang.instrument.ClassFileTransformer;
032: import java.lang.instrument.ClassDefinition;
033: import java.lang.instrument.Instrumentation;
034:
035: import java.security.AccessController;
036: import java.security.PrivilegedAction;
037: import java.security.ProtectionDomain;
038:
039: import java.util.jar.JarFile;
040:
041: /*
042: * Copyright 2003 Wily Technology, Inc.
043: */
044:
045: /**
046: * The Java side of the JPLIS implementation. Works in concert with a native JVMTI agent
047: * to implement the JPLIS API set. Provides both the Java API implementation of
048: * the Instrumentation interface and utility Java routines to support the native code.
049: * Keeps a pointer to the native data structure in a scalar field to allow native
050: * processing behind native methods.
051: */
052: public class InstrumentationImpl implements Instrumentation {
053: private final TransformerManager mTransformerManager;
054: private TransformerManager mRetransfomableTransformerManager;
055: // needs to store a native pointer, so use 64 bits
056: private final long mNativeAgent;
057: private final boolean mEnvironmentSupportsRedefineClasses;
058: private volatile boolean mEnvironmentSupportsRetransformClassesKnown;
059: private volatile boolean mEnvironmentSupportsRetransformClasses;
060: private final boolean mEnvironmentSupportsNativeMethodPrefix;
061:
062: private InstrumentationImpl(long nativeAgent,
063: boolean environmentSupportsRedefineClasses,
064: boolean environmentSupportsNativeMethodPrefix) {
065: mTransformerManager = new TransformerManager(false);
066: mRetransfomableTransformerManager = null;
067: mNativeAgent = nativeAgent;
068: mEnvironmentSupportsRedefineClasses = environmentSupportsRedefineClasses;
069: mEnvironmentSupportsRetransformClassesKnown = false; // false = need to ask
070: mEnvironmentSupportsRetransformClasses = false; // don't know yet
071: mEnvironmentSupportsNativeMethodPrefix = environmentSupportsNativeMethodPrefix;
072: }
073:
074: public void addTransformer(ClassFileTransformer transformer) {
075: addTransformer(transformer, false);
076: }
077:
078: public synchronized void addTransformer(
079: ClassFileTransformer transformer, boolean canRetransform) {
080: if (transformer == null) {
081: throw new NullPointerException(
082: "null passed as 'transformer' in addTransformer");
083: }
084: if (canRetransform) {
085: if (!isRetransformClassesSupported()) {
086: throw new UnsupportedOperationException(
087: "adding retransformable transformers is not supported in this environment");
088: }
089: if (mRetransfomableTransformerManager == null) {
090: mRetransfomableTransformerManager = new TransformerManager(
091: true);
092: }
093: mRetransfomableTransformerManager
094: .addTransformer(transformer);
095: if (mRetransfomableTransformerManager.getTransformerCount() == 1) {
096: setHasRetransformableTransformers(mNativeAgent, true);
097: }
098: } else {
099: mTransformerManager.addTransformer(transformer);
100: }
101: }
102:
103: public synchronized boolean removeTransformer(
104: ClassFileTransformer transformer) {
105: if (transformer == null) {
106: throw new NullPointerException(
107: "null passed as 'transformer' in removeTransformer");
108: }
109: TransformerManager mgr = findTransformerManager(transformer);
110: if (mgr != null) {
111: mgr.removeTransformer(transformer);
112: if (mgr.isRetransformable()
113: && mgr.getTransformerCount() == 0) {
114: setHasRetransformableTransformers(mNativeAgent, false);
115: }
116: return true;
117: }
118: return false;
119: }
120:
121: public boolean isModifiableClass(Class<?> theClass) {
122: if (theClass == null) {
123: throw new NullPointerException(
124: "null passed as 'theClass' in isModifiableClass");
125: }
126: return isModifiableClass0(mNativeAgent, theClass);
127: }
128:
129: public boolean isRetransformClassesSupported() {
130: // ask lazily since there is some overhead
131: if (!mEnvironmentSupportsRetransformClassesKnown) {
132: mEnvironmentSupportsRetransformClasses = isRetransformClassesSupported0(mNativeAgent);
133: mEnvironmentSupportsRetransformClassesKnown = true;
134: }
135: return mEnvironmentSupportsRetransformClasses;
136: }
137:
138: public void retransformClasses(Class<?>[] classes) {
139: if (!isRetransformClassesSupported()) {
140: throw new UnsupportedOperationException(
141: "retransformClasses is not supported in this environment");
142: }
143: retransformClasses0(mNativeAgent, classes);
144: }
145:
146: public boolean isRedefineClassesSupported() {
147: return mEnvironmentSupportsRedefineClasses;
148: }
149:
150: public void redefineClasses(ClassDefinition[] definitions)
151: throws ClassNotFoundException {
152: if (!isRedefineClassesSupported()) {
153: throw new UnsupportedOperationException(
154: "redefineClasses is not supported in this environment");
155: }
156: if (definitions == null) {
157: throw new NullPointerException(
158: "null passed as 'definitions' in redefineClasses");
159: }
160: for (int i = 0; i < definitions.length; ++i) {
161: if (definitions[i] == null) {
162: throw new NullPointerException(
163: "element of 'definitions' is null in redefineClasses");
164: }
165: }
166: if (definitions.length == 0) {
167: return; // short-circuit if there are no changes requested
168: }
169:
170: redefineClasses0(mNativeAgent, definitions);
171: }
172:
173: public Class[] getAllLoadedClasses() {
174: return getAllLoadedClasses0(mNativeAgent);
175: }
176:
177: public Class[] getInitiatedClasses(ClassLoader loader) {
178: return getInitiatedClasses0(mNativeAgent, loader);
179: }
180:
181: public long getObjectSize(Object objectToSize) {
182: if (objectToSize == null) {
183: throw new NullPointerException(
184: "null passed as 'objectToSize' in getObjectSize");
185: }
186: return getObjectSize0(mNativeAgent, objectToSize);
187: }
188:
189: public void appendToBootstrapClassLoaderSearch(JarFile jarfile) {
190: appendToClassLoaderSearch0(mNativeAgent, jarfile.getName(),
191: true);
192: }
193:
194: public void appendToSystemClassLoaderSearch(JarFile jarfile) {
195: appendToClassLoaderSearch0(mNativeAgent, jarfile.getName(),
196: false);
197: }
198:
199: public boolean isNativeMethodPrefixSupported() {
200: return mEnvironmentSupportsNativeMethodPrefix;
201: }
202:
203: public synchronized void setNativeMethodPrefix(
204: ClassFileTransformer transformer, String prefix) {
205: if (!isNativeMethodPrefixSupported()) {
206: throw new UnsupportedOperationException(
207: "setNativeMethodPrefix is not supported in this environment");
208: }
209: if (transformer == null) {
210: throw new NullPointerException(
211: "null passed as 'transformer' in setNativeMethodPrefix");
212: }
213: TransformerManager mgr = findTransformerManager(transformer);
214: if (mgr == null) {
215: throw new IllegalArgumentException(
216: "transformer not registered in setNativeMethodPrefix");
217: }
218: mgr.setNativeMethodPrefix(transformer, prefix);
219: String[] prefixes = mgr.getNativeMethodPrefixes();
220: setNativeMethodPrefixes(mNativeAgent, prefixes, mgr
221: .isRetransformable());
222: }
223:
224: private TransformerManager findTransformerManager(
225: ClassFileTransformer transformer) {
226: if (mTransformerManager.includesTransformer(transformer)) {
227: return mTransformerManager;
228: }
229: if (mRetransfomableTransformerManager != null
230: && mRetransfomableTransformerManager
231: .includesTransformer(transformer)) {
232: return mRetransfomableTransformerManager;
233: }
234: return null;
235: }
236:
237: /*
238: * Natives
239: */
240: private native boolean isModifiableClass0(long nativeAgent,
241: Class<?> theClass);
242:
243: private native boolean isRetransformClassesSupported0(
244: long nativeAgent);
245:
246: private native void setHasRetransformableTransformers(
247: long nativeAgent, boolean has);
248:
249: private native void retransformClasses0(long nativeAgent,
250: Class<?>[] classes);
251:
252: private native void redefineClasses0(long nativeAgent,
253: ClassDefinition[] definitions)
254: throws ClassNotFoundException;
255:
256: private native Class[] getAllLoadedClasses0(long nativeAgent);
257:
258: private native Class[] getInitiatedClasses0(long nativeAgent,
259: ClassLoader loader);
260:
261: private native long getObjectSize0(long nativeAgent,
262: Object objectToSize);
263:
264: private native void appendToClassLoaderSearch0(long nativeAgent,
265: String jarfile, boolean bootLoader);
266:
267: private native void setNativeMethodPrefixes(long nativeAgent,
268: String[] prefixes, boolean isRetransformable);
269:
270: static {
271: System.loadLibrary("instrument");
272: }
273:
274: /*
275: * Internals
276: */
277:
278: // Enable or disable Java programming language access checks on a
279: // reflected object (for example, a method)
280: private static void setAccessible(final AccessibleObject ao,
281: final boolean accessible) {
282: AccessController.doPrivileged(new PrivilegedAction<Object>() {
283: public Object run() {
284: ao.setAccessible(accessible);
285: return null;
286: }
287: });
288: }
289:
290: // Attempt to load and start an agent
291: private void loadClassAndStartAgent(String classname,
292: String methodname, String optionsString) throws Throwable {
293:
294: ClassLoader mainAppLoader = ClassLoader.getSystemClassLoader();
295: Class<?> javaAgentClass = mainAppLoader.loadClass(classname);
296:
297: Method m = null;
298: NoSuchMethodException firstExc = null;
299: boolean twoArgAgent = false;
300:
301: // The agent class has a premain or agentmain method that has 1 or 2
302: // arguments. We first check for a signature of (String, Instrumentation),
303: // and if not found we check for (String). If neither is found then we
304: // throw the NoSuchMethodException from the first attempt so that the
305: // exception text indicates the lookup failed for the 2-arg method
306: // (same as JDK5.0).
307:
308: try {
309: m = javaAgentClass.getMethod(methodname, new Class[] {
310: String.class,
311: java.lang.instrument.Instrumentation.class });
312: twoArgAgent = true;
313: } catch (NoSuchMethodException x) {
314: // remember the NoSuchMethodException
315: firstExc = x;
316: }
317:
318: // check for the 1-arg method
319: if (m == null) {
320: try {
321: m = javaAgentClass.getMethod(methodname,
322: new Class[] { String.class });
323: } catch (NoSuchMethodException x) {
324: // Neither method exists so we throw the first NoSuchMethodException
325: // as per 5.0
326: throw firstExc;
327: }
328: }
329:
330: // the premain method should not be required to be public,
331: // make it accessible so we can call it
332: setAccessible(m, true);
333:
334: // invoke the 1 or 2-arg method
335: if (twoArgAgent) {
336: m.invoke(null, new Object[] { optionsString, this });
337: } else {
338: m.invoke(null, new Object[] { optionsString });
339: }
340:
341: // don't let others access a non-public premain method
342: setAccessible(m, false);
343: }
344:
345: // WARNING: the native code knows the name & signature of this method
346: private void loadClassAndCallPremain(String classname,
347: String optionsString) throws Throwable {
348:
349: loadClassAndStartAgent(classname, "premain", optionsString);
350: }
351:
352: // WARNING: the native code knows the name & signature of this method
353: private void loadClassAndCallAgentmain(String classname,
354: String optionsString) throws Throwable {
355:
356: loadClassAndStartAgent(classname, "agentmain", optionsString);
357: }
358:
359: // WARNING: the native code knows the name & signature of this method
360: private byte[] transform(ClassLoader loader, String classname,
361: Class classBeingRedefined,
362: ProtectionDomain protectionDomain, byte[] classfileBuffer,
363: boolean isRetransformer) {
364: TransformerManager mgr = isRetransformer ? mRetransfomableTransformerManager
365: : mTransformerManager;
366: if (mgr == null) {
367: return null; // no manager, no transform
368: } else {
369: return mgr.transform(loader, classname,
370: classBeingRedefined, protectionDomain,
371: classfileBuffer);
372: }
373: }
374: }
|