1 /* 2 * Copyright (c) 2003, 2013, 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 27 package sun.instrument; 28 29 import java.lang.reflect.Method; 30 import java.lang.reflect.AccessibleObject; 31 32 import java.lang.instrument.ClassFileTransformer; 33 import java.lang.instrument.ClassDefinition; 34 import java.lang.instrument.Instrumentation; 35 36 import java.security.AccessController; 37 import java.security.PrivilegedAction; 38 import java.security.ProtectionDomain; 39 40 import 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 */ 53 public 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 InstrumentationImpl(long nativeAgent, boolean environmentSupportsRedefineClasses, boolean environmentSupportsNativeMethodPrefix)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 addTransformer(ClassFileTransformer transformer)77 addTransformer(ClassFileTransformer transformer) { 78 addTransformer(transformer, false); 79 } 80 81 public synchronized void addTransformer(ClassFileTransformer transformer, boolean canRetransform)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 removeTransformer(ClassFileTransformer transformer)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 isModifiableClass(Class<?> theClass)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 isRetransformClassesSupported()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 retransformClasses(Class<?>.... classes)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 isRedefineClassesSupported()148 isRedefineClassesSupported() { 149 return mEnvironmentSupportsRedefineClasses; 150 } 151 152 public void redefineClasses(ClassDefinition... definitions)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 @SuppressWarnings("rawtypes") 174 public Class[] getAllLoadedClasses()175 getAllLoadedClasses() { 176 return getAllLoadedClasses0(mNativeAgent); 177 } 178 179 @SuppressWarnings("rawtypes") 180 public Class[] getInitiatedClasses(ClassLoader loader)181 getInitiatedClasses(ClassLoader loader) { 182 return getInitiatedClasses0(mNativeAgent, loader); 183 } 184 185 public long getObjectSize(Object objectToSize)186 getObjectSize(Object objectToSize) { 187 if (objectToSize == null) { 188 throw new NullPointerException("null passed as 'objectToSize' in getObjectSize"); 189 } 190 return getObjectSize0(mNativeAgent, objectToSize); 191 } 192 193 public void appendToBootstrapClassLoaderSearch(JarFile jarfile)194 appendToBootstrapClassLoaderSearch(JarFile jarfile) { 195 appendToClassLoaderSearch0(mNativeAgent, jarfile.getName(), true); 196 } 197 198 public void appendToSystemClassLoaderSearch(JarFile jarfile)199 appendToSystemClassLoaderSearch(JarFile jarfile) { 200 appendToClassLoaderSearch0(mNativeAgent, jarfile.getName(), false); 201 } 202 203 public boolean isNativeMethodPrefixSupported()204 isNativeMethodPrefixSupported() { 205 return mEnvironmentSupportsNativeMethodPrefix; 206 } 207 208 public synchronized void setNativeMethodPrefix(ClassFileTransformer transformer, String prefix)209 setNativeMethodPrefix(ClassFileTransformer transformer, String prefix) { 210 if (!isNativeMethodPrefixSupported()) { 211 throw new UnsupportedOperationException( 212 "setNativeMethodPrefix is not supported in this environment"); 213 } 214 if (transformer == null) { 215 throw new NullPointerException( 216 "null passed as 'transformer' in setNativeMethodPrefix"); 217 } 218 TransformerManager mgr = findTransformerManager(transformer); 219 if (mgr == null) { 220 throw new IllegalArgumentException( 221 "transformer not registered in setNativeMethodPrefix"); 222 } 223 mgr.setNativeMethodPrefix(transformer, prefix); 224 String[] prefixes = mgr.getNativeMethodPrefixes(); 225 setNativeMethodPrefixes(mNativeAgent, prefixes, mgr.isRetransformable()); 226 } 227 228 private TransformerManager findTransformerManager(ClassFileTransformer transformer)229 findTransformerManager(ClassFileTransformer transformer) { 230 if (mTransformerManager.includesTransformer(transformer)) { 231 return mTransformerManager; 232 } 233 if (mRetransfomableTransformerManager != null && 234 mRetransfomableTransformerManager.includesTransformer(transformer)) { 235 return mRetransfomableTransformerManager; 236 } 237 return null; 238 } 239 240 241 /* 242 * Natives 243 */ 244 private native boolean isModifiableClass0(long nativeAgent, Class<?> theClass)245 isModifiableClass0(long nativeAgent, Class<?> theClass); 246 247 private native boolean isRetransformClassesSupported0(long nativeAgent)248 isRetransformClassesSupported0(long nativeAgent); 249 250 private native void setHasRetransformableTransformers(long nativeAgent, boolean has)251 setHasRetransformableTransformers(long nativeAgent, boolean has); 252 253 private native void retransformClasses0(long nativeAgent, Class<?>[] classes)254 retransformClasses0(long nativeAgent, Class<?>[] classes); 255 256 private native void redefineClasses0(long nativeAgent, ClassDefinition[] definitions)257 redefineClasses0(long nativeAgent, ClassDefinition[] definitions) 258 throws ClassNotFoundException; 259 260 @SuppressWarnings("rawtypes") 261 private native Class[] getAllLoadedClasses0(long nativeAgent)262 getAllLoadedClasses0(long nativeAgent); 263 264 @SuppressWarnings("rawtypes") 265 private native Class[] getInitiatedClasses0(long nativeAgent, ClassLoader loader)266 getInitiatedClasses0(long nativeAgent, ClassLoader loader); 267 268 private native long getObjectSize0(long nativeAgent, Object objectToSize)269 getObjectSize0(long nativeAgent, Object objectToSize); 270 271 private native void appendToClassLoaderSearch0(long nativeAgent, String jarfile, boolean bootLoader)272 appendToClassLoaderSearch0(long nativeAgent, String jarfile, boolean bootLoader); 273 274 private native void setNativeMethodPrefixes(long nativeAgent, String[] prefixes, boolean isRetransformable)275 setNativeMethodPrefixes(long nativeAgent, String[] prefixes, boolean isRetransformable); 276 277 static { 278 System.loadLibrary("instrument"); 279 } 280 281 /* 282 * Internals 283 */ 284 285 286 // Enable or disable Java programming language access checks on a 287 // reflected object (for example, a method) setAccessible(final AccessibleObject ao, final boolean accessible)288 private static void setAccessible(final AccessibleObject ao, final boolean accessible) { 289 AccessController.doPrivileged(new PrivilegedAction<Object>() { 290 public Object run() { 291 ao.setAccessible(accessible); 292 return null; 293 }}); 294 } 295 296 // Attempt to load and start an agent 297 private void loadClassAndStartAgent( String classname, String methodname, String optionsString)298 loadClassAndStartAgent( String classname, 299 String methodname, 300 String optionsString) 301 throws Throwable { 302 303 ClassLoader mainAppLoader = ClassLoader.getSystemClassLoader(); 304 Class<?> javaAgentClass = mainAppLoader.loadClass(classname); 305 306 Method m = null; 307 NoSuchMethodException firstExc = null; 308 boolean twoArgAgent = false; 309 310 // The agent class must have a premain or agentmain method that 311 // has 1 or 2 arguments. We check in the following order: 312 // 313 // 1) declared with a signature of (String, Instrumentation) 314 // 2) declared with a signature of (String) 315 // 3) inherited with a signature of (String, Instrumentation) 316 // 4) inherited with a signature of (String) 317 // 318 // So the declared version of either 1-arg or 2-arg always takes 319 // primary precedence over an inherited version. After that, the 320 // 2-arg version takes precedence over the 1-arg version. 321 // 322 // If no method is found then we throw the NoSuchMethodException 323 // from the first attempt so that the exception text indicates 324 // the lookup failed for the 2-arg method (same as JDK5.0). 325 326 try { 327 m = javaAgentClass.getDeclaredMethod( methodname, 328 new Class<?>[] { 329 String.class, 330 java.lang.instrument.Instrumentation.class 331 } 332 ); 333 twoArgAgent = true; 334 } catch (NoSuchMethodException x) { 335 // remember the NoSuchMethodException 336 firstExc = x; 337 } 338 339 if (m == null) { 340 // now try the declared 1-arg method 341 try { 342 m = javaAgentClass.getDeclaredMethod(methodname, 343 new Class<?>[] { String.class }); 344 } catch (NoSuchMethodException x) { 345 // ignore this exception because we'll try 346 // two arg inheritance next 347 } 348 } 349 350 if (m == null) { 351 // now try the inherited 2-arg method 352 try { 353 m = javaAgentClass.getMethod( methodname, 354 new Class<?>[] { 355 String.class, 356 java.lang.instrument.Instrumentation.class 357 } 358 ); 359 twoArgAgent = true; 360 } catch (NoSuchMethodException x) { 361 // ignore this exception because we'll try 362 // one arg inheritance next 363 } 364 } 365 366 if (m == null) { 367 // finally try the inherited 1-arg method 368 try { 369 m = javaAgentClass.getMethod(methodname, 370 new Class<?>[] { String.class }); 371 } catch (NoSuchMethodException x) { 372 // none of the methods exists so we throw the 373 // first NoSuchMethodException as per 5.0 374 throw firstExc; 375 } 376 } 377 378 // the premain method should not be required to be public, 379 // make it accessible so we can call it 380 // Note: The spec says the following: 381 // The agent class must implement a public static premain method... 382 setAccessible(m, true); 383 384 // invoke the 1 or 2-arg method 385 if (twoArgAgent) { 386 m.invoke(null, new Object[] { optionsString, this }); 387 } else { 388 m.invoke(null, new Object[] { optionsString }); 389 } 390 391 // don't let others access a non-public premain method 392 setAccessible(m, false); 393 } 394 395 // WARNING: the native code knows the name & signature of this method 396 private void loadClassAndCallPremain( String classname, String optionsString)397 loadClassAndCallPremain( String classname, 398 String optionsString) 399 throws Throwable { 400 401 loadClassAndStartAgent( classname, "premain", optionsString ); 402 } 403 404 405 // WARNING: the native code knows the name & signature of this method 406 private void loadClassAndCallAgentmain( String classname, String optionsString)407 loadClassAndCallAgentmain( String classname, 408 String optionsString) 409 throws Throwable { 410 411 loadClassAndStartAgent( classname, "agentmain", optionsString ); 412 } 413 414 // WARNING: the native code knows the name & signature of this method 415 private byte[] transform( ClassLoader loader, String classname, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer, boolean isRetransformer)416 transform( ClassLoader loader, 417 String classname, 418 Class<?> classBeingRedefined, 419 ProtectionDomain protectionDomain, 420 byte[] classfileBuffer, 421 boolean isRetransformer) { 422 TransformerManager mgr = isRetransformer? 423 mRetransfomableTransformerManager : 424 mTransformerManager; 425 if (mgr == null) { 426 return null; // no manager, no transform 427 } else { 428 return mgr.transform( loader, 429 classname, 430 classBeingRedefined, 431 protectionDomain, 432 classfileBuffer); 433 } 434 } 435 } 436