Package alt.jiapi.util

Source Code of alt.jiapi.util.HotSpotAdvisor$HSInstrumentor

package alt.jiapi.util;

import java.lang.reflect.Modifier;

import alt.jiapi.InstrumentationDescriptor;
import alt.jiapi.InstrumentationException;
import alt.jiapi.MethodInstrumentor;
import alt.jiapi.Rule;
import alt.jiapi.reflect.BranchInstruction;
import alt.jiapi.reflect.Instruction;
import alt.jiapi.reflect.InstructionFactory;
import alt.jiapi.reflect.InstructionList;
import alt.jiapi.reflect.JiapiClass;
import alt.jiapi.reflect.JiapiMethod;
import alt.jiapi.reflect.Loader;
import alt.jiapi.reflect.Signature;
import alt.jiapi.reflect.instruction.Invocation;
import alt.jiapi.reflect.instruction.OpcodeGroups;
import alt.jiapi.reflect.instruction.Opcodes;

import org.apache.log4j.Category;

/**
* HotSpotAdvisor is used to copy instructions from
* HotSpotAdvice to class being instrumented. This makes
* it possible to instrument target classes without any
* knowledge about Java bytecodes or instruction set.<p>
*
* Conseptually, this class, with the aid of HotSpotAdvise,
* provides the same thing as
* <a href="http://eclipse.org/aspectj/">AspectJ</a>s'
* join-point, before advice, after advice and around advice.<p>
*
* At some point in time, we might add point-cut semantics
* here.
* At the moment, point-cut semantics can be simulated to some extent with
* the aid of InstrumentationDescriptor<p>
*
* At the moment there is not such thing as HotSpotContext.
* For example, HotSpotAdvice knows, that an invocation is being made,
* but it does not know what are the call parameters of the invocation,
* and it does not know what the invocation returned.<p>
*
* Future versions of HotSpotAdvice might have this knowledge.
*
* @see HotSpotAdvice
* @see HotSpot for the definition of hotspot
*/
public class HotSpotAdvisor {
    private static Category log = Category.getInstance(HotSpotAdvisor.class);
    /**
     * This field can be used in constructor to search for any
     * invocations being made. Note, that a call to super classes'
     * constructor, is technically just an invocation. If you like
     * to exclude calls to super class constructor, define your own
     * byte array like this:
     * <pre>
     *    byte[] invocations = new byte[] {
     *        Opcodes.INVOKESTATIC,
     *        Opcodes.INVOKEVIRTUAL,
     *        Opcodes.INVOKEINTERFACE
     *    };
     * </pre>
     */
    public static final byte[] INVOCATIONS = OpcodeGroups.INVOKE_INSTRUCTIONS;
    /**
     * This field can be used in constructor to search for hotspots,
     * that makes a method return normally.
     */
    public static final byte[] RETURNS = OpcodeGroups.RETURN_INSTRUCTIONS;
    /**
     * This field can be used in constructor to search for hotspots,
     * that do some sort of field access. Like get or set field.
     */
    public static final byte[] FIELD_ACCESSES = OpcodeGroups.FIELD_ACCESS_INSTRUCTIONS;

    /**
     * Creates new HotSpotAdvisor.
     *
     * @param id InstrumentationDescriptor to use. A Special
     *        instruction copy Instrumentor is added to this
     *        descriptor, so users should only add inclusion/exclusion
     *        rules to descriptor.
     * @param advice A HotSpotAdvice where instructions to be copied are
     *        taken.
     * @param hotSpot this byte represents an opcode
     *        of the hotspot.
     * @see alt.jiapi.reflect.instruction.Opcodes
     */
    public HotSpotAdvisor(InstrumentationDescriptor id,
                          HotSpotAdvice advice,
                          byte hotSpot) {
        this(id, advice, new byte[] {hotSpot});
    }

    /**
     * Creates new HotSpotAdvisor.
     *
     * @param id InstrumentationDescriptor to use. A Special
     *        instruction copy Instrumentor is added to this
     *        descriptor, so users should only add inclusion/exclusion
     *        rules to descriptor.
     * @param advice A HotSpotAdvice where instructions to be copied are
     *        taken.
     * @param hotSpots an array of bytes. Each byte represents an opcode
     *        of the hotspot. So, multiple opcodes may be used.
     *
     * @see #INVOCATIONS
     * @see #RETURNS
     * @see #FIELD_ACCESSES
     * @see alt.jiapi.reflect.instruction.Opcodes
     * @see alt.jiapi.reflect.instruction.OpcodeGroups
     */
    public HotSpotAdvisor(InstrumentationDescriptor id,
                          HotSpotAdvice advice,
                          byte[] hotSpots) {
        this(id, advice, hotSpots, "*");
    }


    /**
     * Creates new HotSpotAdvisor.
     *
     * @param id InstrumentationDescriptor to use. A Special
     *        instruction copy Instrumentor is added to this
     *        descriptor, so users should only add inclusion/exclusion
     *        rules to descriptor.
     * @param advice A HotSpotAdvice where instructions to be copied are
     *        taken.
     * @param hotSpots an array of bytes. Each byte represents an opcode
     *        of the hotspot. So, multiple opcodes may be used.
     *
     * @see #INVOCATIONS
     * @see #RETURNS
     * @see #FIELD_ACCESSES
     * @see alt.jiapi.reflect.instruction.Opcodes
     * @see alt.jiapi.reflect.instruction.OpcodeGroups
     */
    public HotSpotAdvisor(InstrumentationDescriptor id,
                          HotSpotAdvice advice,
                          byte[] hotSpots, String resolution) {
        try {
            // Do we need this. In my testing, advice got instrumented
            // by accident by include=samples* rule.
//             System.out.println("Adding exclusion rule '" +
//                                advice.getClass().getName() + "'");
            id.addExclusionRule(advice.getClass().getName());
        }
        catch(Exception e) {
        }

        id.addInstrumentor(new HSInstrumentor(advice, hotSpots, resolution));
    }
   

    /**
     * This private Instrucmentor makes the actual bytecode
     * copy procedure.
     */
    private class HSInstrumentor extends MethodInstrumentor {
        private byte[] hotSpots;
        private JiapiMethod adviceMethod;
        private Rule rule;
       
        HSInstrumentor(HotSpotAdvice advice, byte[] hotSpots, String resolution) {
            try {
                this.rule = new Rule(resolution);
                Loader l = new Loader();
                log.debug("Loading advice " + advice.getClass().getName());
                JiapiClass clazz = l.loadClass(advice.getClass().getName());

                log.debug("Getting advice() method ");
                this.adviceMethod =
                    clazz.getDeclaredMethod("advice", new String[0]);
                this.hotSpots = hotSpots;
            }
            catch(NoSuchMethodException nsme) {
                // Should not happen, since it is an abstract method and
                // there has to be an implementation for it.
                log.error("Internal error: Could not find HotSpotAdvice.advice() method", nsme);
                throw new InstrumentationException("Could not find method advice() from " + advice.getClass() + ": " + nsme);
            }
            catch(ClassNotFoundException cnfe) {
                // Should not happen, can happen if Advice was loaded
                // from a place other than classpath
                log.error("Internal error: Could load class HotSpotAdvice", cnfe);

                throw new InstrumentationException("Could not load class " + advice.getClass() + ": " + cnfe);
            }
            catch(java.io.IOException ioe) {
                // Should not happen
                log.error("Internal error: Could load class HotSpotAdvice", ioe);

                throw new InstrumentationException("Could not load class " + advice.getClass() + ": " + ioe);
            }
            catch(Exception e) {
                log.error("Failed to initialize HotSpotAdvisor", e);

                throw new InstrumentationException("Failed to initialize HotSpotAdvisor: " + e);
            }
        }

        /**
         * Instrument given method. Find hotspots, and copy instructions
         * from advice before/after/around hotspot
         */
        public void instrument(JiapiMethod m) throws InstrumentationException {
            log.debug("Instrumenting " + m.getDeclaringClass().getName() +
                      "#" + m);

            HotSpotLocator hsl =
                new HotSpotLocator(m.getInstructionList(), hotSpots);

            HotSpot[] hsa = hsl.getHotSpots();
            for (int i = 0; i < hsa.length; i++) {
                HotSpot hs = hsa[i];
                if (rule.match(hs.getName())) {
                    copyInstructions(hs);
                }
            }
        }

        /**
         * Actual copying of instructions to hotspot.
         */
        private void copyInstructions(HotSpot hs) {
            InstructionList am_il = adviceMethod.getInstructionList();
            InstructionList il = hs.getInstructionList();
      Instruction firstHotSpotInstruction = il.get(0);

            //
            // NOTE: We must handle local variables correctly
            // NOTE: should be done in InstructionList.add/insert(Instruction)
            am_il = changeLocalVars(am_il, il.getDeclaringMethod().getMaxLocals()-1);

            int idx = indexOfDoHotSpot();
                       
            if (idx == -1) {
                log.debug("Replacing hotspot with advice-method");
                il.clear();
                il.add(am_il);
                return;
            }
            else {
                InstructionList before = am_il.createView(0, idx - 1);

      // If first hs instruction is branch target, we
      // will have to change this to first instruction of
      // before-list, so that we do not introduce dead-code.
    if (before.size() > 0) {
        checkForBranchTarget(firstHotSpotInstruction, il.getDeclaringMethod().getInstructionList(), before.get(0));
    }

                InstructionList after = null;
                if (idx+1 < am_il.size()) {
                    // Strip 'return' statement
                    after = am_il.createView(idx + 1, am_il.size() - 1);
                }
               

                //System.out.println("il before change " + il);
                il.insert(0, before);
                if (after != null) {
                    il.add(after);
                }
            }


            // process advice list
            processAdviceTriggers(il, hs);
        }


        /**
         * Finds <b>first</b> occurense of a call to doHotSpot();
         * If not found, return -1.
         *
         * Is it a bug, if there are more occurences of doHotSpot()
         * calls in Advice. And if it is a bug, is it a bug in
         * this class(not taking this into account), or Advice
         * (being rogue Advice). Should we enable this???
         *
         * Anyway, at the moment, instrumented class will not pass bytecode
         * verifier(I think), since there will be a non-existent call to
         * super.doHotSpot().
         */
        private int indexOfDoHotSpot() {
            InstructionList il = adviceMethod.getInstructionList();
            HotSpotLocator hsl = new HotSpotLocator(il, Opcodes.INVOKEVIRTUAL);

            HotSpot[] dohsCandidates = hsl.getHotSpots();
            for (int i = 0; i < dohsCandidates.length; i++) {
                Invocation inv =
                    (Invocation)dohsCandidates[i].getHotSpotInstruction();

                if (!inv.getClassName().equals(adviceMethod.getDeclaringClass().getName())) {
                    // Process only calls to HotSpotAdvice class
                    continue;
                }

                if ("doHotSpot".equals(inv.getMethodName())) {
//                     if (inv.getAttribute("synthetic") != null) {
//                         // Found it
//                         return il.indexOf(inv);
//                     }
//                     continue;
                    return il.indexOf(inv);
                }
            }
           
            return -1;
        }


        /**
         * Change usage of local variables in target InstructionList so,
         * that it will not overlap with 'other' list.
         *
         * If Advice declares local variables, they start from index 0,
         * but so do local vars in target class. So this method changes
         * local variable accesses in advice to start from
         * <target.getMaxLocals()>
         *
         * @param advice InstructionList to change
         * @param maxLocalsInOtherList Max-Locals in other list
         */
        private InstructionList changeLocalVars(InstructionList advice,
                                                int maxLocalsInOtherList) {
//             if (maxLocalsInOtherList == 0) {
//                 // Nothing to do, other list does not contain
//                 // local variables
//                 return advice;
//             }

            // BUG: These should not be needed. For some reason,
            //      JiapiMethod.getMaxLocals() returns wrong values?
            //      Commenting these out has no harm other than
            //      missing unused local variable slots
            maxLocalsInOtherList--;
            // following could be added, if no longs/doubles are in target
            // list; they reserve two slots for local vars. Another
            // silly stuff for longs/doubles in JVMs
            // maxLocalsInOtherList--;
           
            InstructionList newList = new InstructionList();
           
            InstructionFactory factory = new InstructionFactory();
            for(int i = 0; i < advice.size(); i++) {
                Instruction ins = advice.get(i);
                newList.add(ins);
                switch(ins.getOpcode()) {
                    // -- ALOAD family --------------------------------------
                    case Opcodes.ALOAD_0:
                        newList.replace(i, factory.aload(maxLocalsInOtherList));
                        break;
                    case Opcodes.ALOAD_1:
                        newList.replace(i, factory.aload(maxLocalsInOtherList + 1));
                        break;
                    case Opcodes.ALOAD_2:
                        newList.replace(i, factory.aload(maxLocalsInOtherList + 2));
                        break;
                    case Opcodes.ALOAD_3:
                        newList.replace(i, factory.aload(maxLocalsInOtherList + 3));
                        break;
                    case Opcodes.ALOAD:
                        // Handle this differently. This
                        // form of handling does not break exception table
                        // So, we minimize damage by handling this differently
                        ins.getBytes()[1] += (byte)maxLocalsInOtherList;
                        break;
                    case Opcodes.ASTORE_0:
                        newList.replace(i, factory.astore(maxLocalsInOtherList));
                        break;
                    case Opcodes.ASTORE_1:
                        newList.replace(i, factory.astore(maxLocalsInOtherList + 1));
                        break;
                    case Opcodes.ASTORE_2:
                        newList.replace(i, factory.astore(maxLocalsInOtherList + 2));
                        break;
                    case Opcodes.ASTORE_3:
                        newList.replace(i, factory.astore(maxLocalsInOtherList + 3));
                        break;
                    case Opcodes.ASTORE:
                        // Handle this differently. This
                        // form of handling does not break exception table
                        // So, we minimize damage by handling this differently
                        ins.getBytes()[1] += (byte)maxLocalsInOtherList;
                        break;

                    // -- ILOAD family --------------------------------------
                    case Opcodes.ILOAD_0:
                        newList.replace(i, factory.iload(maxLocalsInOtherList));
                        break;
                    case Opcodes.ILOAD_1:
                        newList.replace(i, factory.iload(maxLocalsInOtherList + 1));
                        break;
                    case Opcodes.ILOAD_2:
                        newList.replace(i, factory.iload(maxLocalsInOtherList + 2));
                        break;
                    case Opcodes.ILOAD_3:
                        newList.replace(i, factory.iload(maxLocalsInOtherList + 3));
                        break;
                    case Opcodes.ILOAD:
                        // Handle this differently. This
                        // form of handling does not break exception table
                        // So, we minimize damage by handling this differently
                        ins.getBytes()[1] += (byte)maxLocalsInOtherList;
                        break;
                    case Opcodes.ISTORE_0:
                        newList.replace(i, factory.istore(maxLocalsInOtherList));
                        break;
                    case Opcodes.ISTORE_1:
                        newList.replace(i, factory.istore(maxLocalsInOtherList + 1));
                        break;
                    case Opcodes.ISTORE_2:
                        newList.replace(i, factory.istore(maxLocalsInOtherList + 2));
                        break;
                    case Opcodes.ISTORE_3:
                        newList.replace(i, factory.istore(maxLocalsInOtherList + 3));
                        break;
                    case Opcodes.ISTORE:
                        // Handle this differently. This
                        // form of handling does not break exception table
                        // So, we minimize damage by handling this differently
                        ins.getBytes()[1] += (byte)maxLocalsInOtherList;
                        break;

                    // -- DLOAD family --------------------------------------
                    case Opcodes.DLOAD_0:
                        newList.replace(i, factory.dload(maxLocalsInOtherList));
                        break;
                    case Opcodes.DLOAD_1:
                        newList.replace(i, factory.dload(maxLocalsInOtherList + 1));
                        break;
                    case Opcodes.DLOAD_2:
                        newList.replace(i, factory.dload(maxLocalsInOtherList + 2));
                        break;
                    case Opcodes.DLOAD_3:
                        newList.replace(i, factory.dload(maxLocalsInOtherList + 3));
                        break;
                    case Opcodes.DLOAD:
                        // Handle this differently. This
                        // form of handling does not break exception table
                        // So, we minimize damage by handling this differently
                        ins.getBytes()[1] += (byte)maxLocalsInOtherList;
                        break;
                    case Opcodes.DSTORE_0:
                        newList.replace(i, factory.dstore(maxLocalsInOtherList));
                        break;
                    case Opcodes.DSTORE_1:
                        newList.replace(i, factory.dstore(maxLocalsInOtherList + 1));
                        break;
                    case Opcodes.DSTORE_2:
                        newList.replace(i, factory.dstore(maxLocalsInOtherList + 2));
                        break;
                    case Opcodes.DSTORE_3:
                        newList.replace(i, factory.dstore(maxLocalsInOtherList + 3));
                        break;
                    case Opcodes.DSTORE:
                        // Handle this differently. This
                        // form of handling does not break exception table
                        // So, we minimize damage by handling this differently
                        ins.getBytes()[1] += (byte)maxLocalsInOtherList;
                        break;

                    // -- LLOAD family --------------------------------------
                    case Opcodes.LLOAD_0:
                        newList.replace(i, factory.lload(maxLocalsInOtherList));
                        break;
                    case Opcodes.LLOAD_1:
                        newList.replace(i, factory.lload(maxLocalsInOtherList + 1));
                        break;
                    case Opcodes.LLOAD_2:
                        newList.replace(i, factory.lload(maxLocalsInOtherList + 2));
                        break;
                    case Opcodes.LLOAD_3:
                        newList.replace(i, factory.lload(maxLocalsInOtherList + 3));
                        break;
                    case Opcodes.LLOAD:
                        // Handle this differently. This
                        // form of handling does not break exception table
                        // So, we minimize damage by handling this differently
                        ins.getBytes()[1] += (byte)maxLocalsInOtherList;
                        break;
                    case Opcodes.LSTORE_0:
                        newList.replace(i, factory.lstore(maxLocalsInOtherList));
                        break;
                    case Opcodes.LSTORE_1:
                        newList.replace(i, factory.lstore(maxLocalsInOtherList + 1));
                        break;
                    case Opcodes.LSTORE_2:
                        newList.replace(i, factory.lstore(maxLocalsInOtherList + 2));
                        break;
                    case Opcodes.LSTORE_3:
                        newList.replace(i, factory.lstore(maxLocalsInOtherList + 3));
                        break;
                    case Opcodes.LSTORE:
                        // Handle this differently. This
                        // form of handling does not break exception table
                        // So, we minimize damage by handling this differently
                        ins.getBytes()[1] += (byte)maxLocalsInOtherList;
                        break;
                       
                    // -- FLOAD family --------------------------------------
                    case Opcodes.FLOAD_0:
                        newList.replace(i, factory.fload(maxLocalsInOtherList));
                        break;
                    case Opcodes.FLOAD_1:
                        newList.replace(i, factory.fload(maxLocalsInOtherList + 1));
                        break;
                    case Opcodes.FLOAD_2:
                        newList.replace(i, factory.fload(maxLocalsInOtherList + 2));
                        break;
                    case Opcodes.FLOAD_3:
                        newList.replace(i, factory.fload(maxLocalsInOtherList + 3));
                        break;
                    case Opcodes.FLOAD:
                        // Handle this differently. This
                        // form of handling does not break exception table
                        // So, we minimize damage by handling this differently
                        ins.getBytes()[1] += (byte)maxLocalsInOtherList;
                        break;
                    case Opcodes.FSTORE_0:
                        newList.replace(i, factory.fstore(maxLocalsInOtherList));
                        break;
                    case Opcodes.FSTORE_1:
                        newList.replace(i, factory.fstore(maxLocalsInOtherList + 1));
                        break;
                    case Opcodes.FSTORE_2:
                        newList.replace(i, factory.fstore(maxLocalsInOtherList + 2));
                        break;
                    case Opcodes.FSTORE_3:
                        newList.replace(i, factory.fstore(maxLocalsInOtherList + 3));
                        break;
                    case Opcodes.FSTORE:
                        // Handle this differently. This
                        // form of handling does not break exception table
                        // So, we minimize damage by handling this differently
                        ins.getBytes()[1] += (byte)maxLocalsInOtherList;
                        break;
                       
                }
            }

            return newList;
        }


        /**
         * Process all the advice triggers found from advice() method,
         * such as a call to getHotSpotName()
         */
        private void processAdviceTriggers(InstructionList il, HotSpot hs) {
           
            InstructionFactory factory = il.getInstructionFactory();
            // Search for calls to possible trigger methods in
            // advice method.
            HotSpotLocator hsl = new HotSpotLocator(il, Opcodes.INVOKEVIRTUAL);

            HotSpot[] triggerCandidates = hsl.getHotSpots();
            for (int i = 0; i < triggerCandidates.length; i++) {
                Invocation inv =
                    (Invocation)triggerCandidates[i].getHotSpotInstruction();

                if (!inv.getClassName().equals(adviceMethod.getDeclaringClass().getName())) {
                    // Process only calls to HotSpotAdvice class
                    continue;
                }

                // Index of the trigger instruction
                int idx = il.indexOf(inv);

                // Following assumes, that trigger call is
                // void-void method.
                // First, we remove a call to Advice class...
                InstructionList view = il.createView(idx-1, idx + 1);
    view.clear();

                // ...then we replace, that call with trigger action
                // instructions.
                if ("getHotSpotName".equals(inv.getMethodName())) {
                    view.add(factory.pushConstant(hs.getName()));
                }
                else if ("getArguments".equals(inv.getMethodName())) {
                    // NOT IMPLEMENTED
                    // This needs some further thinking before implementing
                }
                else if ("getInstrumentedObject".equals(inv.getMethodName())) {
        JiapiMethod jm = il.getDeclaringMethod();
        int modifiers = jm.getModifiers();
        if (Modifier.isStatic(modifiers)) {
      // Static methods do not have reference to 'this'
      //view.add(factory.pushNull());
      view.add(factory.pushConstant(jm.getDeclaringClass().getName()));
      view.add(factory.invoke(Modifier.STATIC, "java.lang.Class", "forName", new Signature("java.lang.Class", new String[] {"java.lang.String"})));
        }
        else {
      view.add(factory.aload(0));
        }
                    // NOT IMPLEMENTED
                    // This needs some further thinking before implementing
                }
            }
        }
    }


    public void checkForBranchTarget(Instruction firstHotSpotInstruction,
             InstructionList methodList,
             Instruction firstBeforeInstruction) {
  for (int idx = 0; idx < methodList.size(); idx++) {
      Instruction i = (Instruction)methodList.get(idx);

      if (i instanceof BranchInstruction) {
    BranchInstruction bi = (BranchInstruction)i;
    if (bi.getTarget() == firstHotSpotInstruction) {
        bi.setTarget(firstBeforeInstruction);
    }
      }
  }
    }

}
TOP

Related Classes of alt.jiapi.util.HotSpotAdvisor$HSInstrumentor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.