blob: cdafaa4b8f2f2406e9f785bf08a14fb116c5b6f4 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2003-2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26
27package sun.instrument;
28
29import java.lang.reflect.Method;
30import java.lang.reflect.AccessibleObject;
31
32import java.lang.instrument.ClassFileTransformer;
33import java.lang.instrument.ClassDefinition;
34import java.lang.instrument.Instrumentation;
35
36import java.security.AccessController;
37import java.security.PrivilegedAction;
38import java.security.ProtectionDomain;
39
40import java.util.jar.JarFile;
41
42/*
43 * Copyright 2003 Wily Technology, Inc.
44 */
45
46/**
47 * The Java side of the JPLIS implementation. Works in concert with a native JVMTI agent
48 * to implement the JPLIS API set. Provides both the Java API implementation of
49 * the Instrumentation interface and utility Java routines to support the native code.
50 * Keeps a pointer to the native data structure in a scalar field to allow native
51 * processing behind native methods.
52 */
53public class InstrumentationImpl implements Instrumentation {
54 private final TransformerManager mTransformerManager;
55 private TransformerManager mRetransfomableTransformerManager;
56 // needs to store a native pointer, so use 64 bits
57 private final long mNativeAgent;
58 private final boolean mEnvironmentSupportsRedefineClasses;
59 private volatile boolean mEnvironmentSupportsRetransformClassesKnown;
60 private volatile boolean mEnvironmentSupportsRetransformClasses;
61 private final boolean mEnvironmentSupportsNativeMethodPrefix;
62
63 private
64 InstrumentationImpl(long nativeAgent,
65 boolean environmentSupportsRedefineClasses,
66 boolean environmentSupportsNativeMethodPrefix) {
67 mTransformerManager = new TransformerManager(false);
68 mRetransfomableTransformerManager = null;
69 mNativeAgent = nativeAgent;
70 mEnvironmentSupportsRedefineClasses = environmentSupportsRedefineClasses;
71 mEnvironmentSupportsRetransformClassesKnown = false; // false = need to ask
72 mEnvironmentSupportsRetransformClasses = false; // don't know yet
73 mEnvironmentSupportsNativeMethodPrefix = environmentSupportsNativeMethodPrefix;
74 }
75
76 public void
77 addTransformer(ClassFileTransformer transformer) {
78 addTransformer(transformer, false);
79 }
80
81 public synchronized void
82 addTransformer(ClassFileTransformer transformer, boolean canRetransform) {
83 if (transformer == null) {
84 throw new NullPointerException("null passed as 'transformer' in addTransformer");
85 }
86 if (canRetransform) {
87 if (!isRetransformClassesSupported()) {
88 throw new UnsupportedOperationException(
89 "adding retransformable transformers is not supported in this environment");
90 }
91 if (mRetransfomableTransformerManager == null) {
92 mRetransfomableTransformerManager = new TransformerManager(true);
93 }
94 mRetransfomableTransformerManager.addTransformer(transformer);
95 if (mRetransfomableTransformerManager.getTransformerCount() == 1) {
96 setHasRetransformableTransformers(mNativeAgent, true);
97 }
98 } else {
99 mTransformerManager.addTransformer(transformer);
100 }
101 }
102
103 public synchronized boolean
104 removeTransformer(ClassFileTransformer transformer) {
105 if (transformer == null) {
106 throw new NullPointerException("null passed as 'transformer' in removeTransformer");
107 }
108 TransformerManager mgr = findTransformerManager(transformer);
109 if (mgr != null) {
110 mgr.removeTransformer(transformer);
111 if (mgr.isRetransformable() && mgr.getTransformerCount() == 0) {
112 setHasRetransformableTransformers(mNativeAgent, false);
113 }
114 return true;
115 }
116 return false;
117 }
118
119 public boolean
120 isModifiableClass(Class<?> theClass) {
121 if (theClass == null) {
122 throw new NullPointerException(
123 "null passed as 'theClass' in isModifiableClass");
124 }
125 return isModifiableClass0(mNativeAgent, theClass);
126 }
127
128 public boolean
129 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
139 retransformClasses(Class<?>[] classes) {
140 if (!isRetransformClassesSupported()) {
141 throw new UnsupportedOperationException(
142 "retransformClasses is not supported in this environment");
143 }
144 retransformClasses0(mNativeAgent, classes);
145 }
146
147 public boolean
148 isRedefineClassesSupported() {
149 return mEnvironmentSupportsRedefineClasses;
150 }
151
152 public void
153 redefineClasses(ClassDefinition[] definitions)
154 throws ClassNotFoundException {
155 if (!isRedefineClassesSupported()) {
156 throw new UnsupportedOperationException("redefineClasses is not supported in this environment");
157 }
158 if (definitions == null) {
159 throw new NullPointerException("null passed as 'definitions' in redefineClasses");
160 }
161 for (int i = 0; i < definitions.length; ++i) {
162 if (definitions[i] == null) {
163 throw new NullPointerException("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[]
174 getAllLoadedClasses() {
175 return getAllLoadedClasses0(mNativeAgent);
176 }
177
178 public Class[]
179 getInitiatedClasses(ClassLoader loader) {
180 return getInitiatedClasses0(mNativeAgent, loader);
181 }
182
183 public long
184 getObjectSize(Object objectToSize) {
185 if (objectToSize == null) {
186 throw new NullPointerException("null passed as 'objectToSize' in getObjectSize");
187 }
188 return getObjectSize0(mNativeAgent, objectToSize);
189 }
190
191 public void
192 appendToBootstrapClassLoaderSearch(JarFile jarfile) {
193 appendToClassLoaderSearch0(mNativeAgent, jarfile.getName(), true);
194 }
195
196 public void
197 appendToSystemClassLoaderSearch(JarFile jarfile) {
198 appendToClassLoaderSearch0(mNativeAgent, jarfile.getName(), false);
199 }
200
201 public boolean
202 isNativeMethodPrefixSupported() {
203 return mEnvironmentSupportsNativeMethodPrefix;
204 }
205
206 public synchronized void
207 setNativeMethodPrefix(ClassFileTransformer transformer, String prefix) {
208 if (!isNativeMethodPrefixSupported()) {
209 throw new UnsupportedOperationException(
210 "setNativeMethodPrefix is not supported in this environment");
211 }
212 if (transformer == null) {
213 throw new NullPointerException(
214 "null passed as 'transformer' in setNativeMethodPrefix");
215 }
216 TransformerManager mgr = findTransformerManager(transformer);
217 if (mgr == null) {
218 throw new IllegalArgumentException(
219 "transformer not registered in setNativeMethodPrefix");
220 }
221 mgr.setNativeMethodPrefix(transformer, prefix);
222 String[] prefixes = mgr.getNativeMethodPrefixes();
223 setNativeMethodPrefixes(mNativeAgent, prefixes, mgr.isRetransformable());
224 }
225
226 private TransformerManager
227 findTransformerManager(ClassFileTransformer transformer) {
228 if (mTransformerManager.includesTransformer(transformer)) {
229 return mTransformerManager;
230 }
231 if (mRetransfomableTransformerManager != null &&
232 mRetransfomableTransformerManager.includesTransformer(transformer)) {
233 return mRetransfomableTransformerManager;
234 }
235 return null;
236 }
237
238
239 /*
240 * Natives
241 */
242 private native boolean
243 isModifiableClass0(long nativeAgent, Class<?> theClass);
244
245 private native boolean
246 isRetransformClassesSupported0(long nativeAgent);
247
248 private native void
249 setHasRetransformableTransformers(long nativeAgent, boolean has);
250
251 private native void
252 retransformClasses0(long nativeAgent, Class<?>[] classes);
253
254 private native void
255 redefineClasses0(long nativeAgent, ClassDefinition[] definitions)
256 throws ClassNotFoundException;
257
258 private native Class[]
259 getAllLoadedClasses0(long nativeAgent);
260
261 private native Class[]
262 getInitiatedClasses0(long nativeAgent, ClassLoader loader);
263
264 private native long
265 getObjectSize0(long nativeAgent, Object objectToSize);
266
267 private native void
268 appendToClassLoaderSearch0(long nativeAgent, String jarfile, boolean bootLoader);
269
270 private native void
271 setNativeMethodPrefixes(long nativeAgent, String[] prefixes, boolean isRetransformable);
272
273 static {
274 System.loadLibrary("instrument");
275 }
276
277 /*
278 * Internals
279 */
280
281
282 // Enable or disable Java programming language access checks on a
283 // reflected object (for example, a method)
284 private static void setAccessible(final AccessibleObject ao, final boolean accessible) {
285 AccessController.doPrivileged(new PrivilegedAction<Object>() {
286 public Object run() {
287 ao.setAccessible(accessible);
288 return null;
289 }});
290 }
291
292 // Attempt to load and start an agent
293 private void
294 loadClassAndStartAgent( String classname,
295 String methodname,
296 String optionsString)
297 throws Throwable {
298
299 ClassLoader mainAppLoader = ClassLoader.getSystemClassLoader();
300 Class<?> javaAgentClass = mainAppLoader.loadClass(classname);
301
302 Method m = null;
303 NoSuchMethodException firstExc = null;
304 boolean twoArgAgent = false;
305
306 // The agent class has a premain or agentmain method that has 1 or 2
307 // arguments. We first check for a signature of (String, Instrumentation),
308 // and if not found we check for (String). If neither is found then we
309 // throw the NoSuchMethodException from the first attempt so that the
310 // exception text indicates the lookup failed for the 2-arg method
311 // (same as JDK5.0).
312
313 try {
314 m = javaAgentClass.getMethod( methodname,
315 new Class[] {
316 String.class,
317 java.lang.instrument.Instrumentation.class
318 }
319 );
320 twoArgAgent = true;
321 } catch (NoSuchMethodException x) {
322 // remember the NoSuchMethodException
323 firstExc = x;
324 }
325
326 // check for the 1-arg method
327 if (m == null) {
328 try {
329 m = javaAgentClass.getMethod(methodname, new Class[] { String.class });
330 } catch (NoSuchMethodException x) {
331 // Neither method exists so we throw the first NoSuchMethodException
332 // as per 5.0
333 throw firstExc;
334 }
335 }
336
337 // the premain method should not be required to be public,
338 // make it accessible so we can call it
339 setAccessible(m, true);
340
341 // invoke the 1 or 2-arg method
342 if (twoArgAgent) {
343 m.invoke(null, new Object[] { optionsString, this });
344 } else {
345 m.invoke(null, new Object[] { optionsString });
346 }
347
348 // don't let others access a non-public premain method
349 setAccessible(m, false);
350 }
351
352 // WARNING: the native code knows the name & signature of this method
353 private void
354 loadClassAndCallPremain( String classname,
355 String optionsString)
356 throws Throwable {
357
358 loadClassAndStartAgent( classname, "premain", optionsString );
359 }
360
361
362 // WARNING: the native code knows the name & signature of this method
363 private void
364 loadClassAndCallAgentmain( String classname,
365 String optionsString)
366 throws Throwable {
367
368 loadClassAndStartAgent( classname, "agentmain", optionsString );
369 }
370
371 // WARNING: the native code knows the name & signature of this method
372 private byte[]
373 transform( ClassLoader loader,
374 String classname,
375 Class classBeingRedefined,
376 ProtectionDomain protectionDomain,
377 byte[] classfileBuffer,
378 boolean isRetransformer) {
379 TransformerManager mgr = isRetransformer?
380 mRetransfomableTransformerManager :
381 mTransformerManager;
382 if (mgr == null) {
383 return null; // no manager, no transform
384 } else {
385 return mgr.transform( loader,
386 classname,
387 classBeingRedefined,
388 protectionDomain,
389 classfileBuffer);
390 }
391 }
392}