Save This Page
Home » openjdk-7 » sun » instrument » [javadoc | source]
    1   /*
    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   
   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
   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 must have a premain or agentmain method that
  307           // has 1 or 2 arguments. We check in the following order:
  308           //
  309           // 1) declared with a signature of (String, Instrumentation)
  310           // 2) declared with a signature of (String)
  311           // 3) inherited with a signature of (String, Instrumentation)
  312           // 4) inherited with a signature of (String)
  313           //
  314           // So the declared version of either 1-arg or 2-arg always takes
  315           // primary precedence over an inherited version. After that, the
  316           // 2-arg version takes precedence over the 1-arg version.
  317           //
  318           // If no method is found then we throw the NoSuchMethodException
  319           // from the first attempt so that the exception text indicates
  320           // the lookup failed for the 2-arg method (same as JDK5.0).
  321   
  322           try {
  323               m = javaAgentClass.getDeclaredMethod( methodname,
  324                                    new Class[] {
  325                                        String.class,
  326                                        java.lang.instrument.Instrumentation.class
  327                                    }
  328                                  );
  329               twoArgAgent = true;
  330           } catch (NoSuchMethodException x) {
  331               // remember the NoSuchMethodException
  332               firstExc = x;
  333           }
  334   
  335           if (m == null) {
  336               // now try the declared 1-arg method
  337               try {
  338                   m = javaAgentClass.getDeclaredMethod(methodname,
  339                                                    new Class[] { String.class });
  340               } catch (NoSuchMethodException x) {
  341                   // ignore this exception because we'll try
  342                   // two arg inheritance next
  343               }
  344           }
  345   
  346           if (m == null) {
  347               // now try the inherited 2-arg method
  348               try {
  349                   m = javaAgentClass.getMethod( methodname,
  350                                    new Class[] {
  351                                        String.class,
  352                                        java.lang.instrument.Instrumentation.class
  353                                    }
  354                                  );
  355                   twoArgAgent = true;
  356               } catch (NoSuchMethodException x) {
  357                   // ignore this exception because we'll try
  358                   // one arg inheritance next
  359               }
  360           }
  361   
  362           if (m == null) {
  363               // finally try the inherited 1-arg method
  364               try {
  365                   m = javaAgentClass.getMethod(methodname,
  366                                                new Class[] { String.class });
  367               } catch (NoSuchMethodException x) {
  368                   // none of the methods exists so we throw the
  369                   // first NoSuchMethodException as per 5.0
  370                   throw firstExc;
  371               }
  372           }
  373   
  374           // the premain method should not be required to be public,
  375           // make it accessible so we can call it
  376           // Note: The spec says the following:
  377           //     The agent class must implement a public static premain method...
  378           setAccessible(m, true);
  379   
  380           // invoke the 1 or 2-arg method
  381           if (twoArgAgent) {
  382               m.invoke(null, new Object[] { optionsString, this });
  383           } else {
  384               m.invoke(null, new Object[] { optionsString });
  385           }
  386   
  387           // don't let others access a non-public premain method
  388           setAccessible(m, false);
  389       }
  390   
  391       // WARNING: the native code knows the name & signature of this method
  392       private void
  393       loadClassAndCallPremain(    String  classname,
  394                                   String  optionsString)
  395               throws Throwable {
  396   
  397           loadClassAndStartAgent( classname, "premain", optionsString );
  398       }
  399   
  400   
  401       // WARNING: the native code knows the name & signature of this method
  402       private void
  403       loadClassAndCallAgentmain(  String  classname,
  404                                   String  optionsString)
  405               throws Throwable {
  406   
  407           loadClassAndStartAgent( classname, "agentmain", optionsString );
  408       }
  409   
  410       // WARNING: the native code knows the name & signature of this method
  411       private byte[]
  412       transform(  ClassLoader         loader,
  413                   String              classname,
  414                   Class               classBeingRedefined,
  415                   ProtectionDomain    protectionDomain,
  416                   byte[]              classfileBuffer,
  417                   boolean             isRetransformer) {
  418           TransformerManager mgr = isRetransformer?
  419                                           mRetransfomableTransformerManager :
  420                                           mTransformerManager;
  421           if (mgr == null) {
  422               return null; // no manager, no transform
  423           } else {
  424               return mgr.transform(   loader,
  425                                       classname,
  426                                       classBeingRedefined,
  427                                       protectionDomain,
  428                                       classfileBuffer);
  429           }
  430       }
  431   }

Save This Page
Home » openjdk-7 » sun » instrument » [javadoc | source]