Package org.eurekaJ.agent

Source Code of org.eurekaJ.agent.EurekaJTransformer

package org.eurekaJ.agent;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.LoaderClassPath;
import javassist.NotFoundException;
import javassist.bytecode.AccessFlag;

import org.apache.log4j.Logger;
import org.eurekaJ.agent.model.ClassInstrumentationInfo;
import org.eurekaJ.agent.model.MethodInstrumentation;

public class EurekaJTransformer implements ClassFileTransformer {

  private static String logdef = "private static org.apache.log4j.Logger _log;";
  private String[] ignore = new String[] { "sun/", "java/", "javax/", "org/apache/jasper/servlet/JspServlet", "org/apache/jasper/runtime/HttpJspBase" };
  private static Hashtable<ClassLoader, ClassLoader> classLoaders = new Hashtable<ClassLoader, ClassLoader>();
  private static ClassPool classPool = new ClassPool(true);
  private static Logger log = Logger.getLogger(EurekaJTransformer.class);

  public EurekaJTransformer() {

  }

  private CtClass makeClass(byte[] classFileBuffer) {
    CtClass madeClass = null;

    try {
      madeClass = classPool.makeClass(new ByteArrayInputStream(classFileBuffer, 0, classFileBuffer.length));
      if (madeClass == null) {
        log.error("Unable to load class: " + madeClass);
      }
    } catch (IOException e1) {
      // TODO Auto-generated catch block
      e1.printStackTrace();
    } catch (RuntimeException e1) {
      // TODO Auto-generated catch block
      e1.printStackTrace();
    }

    return madeClass;
  }

  private ClassInstrumentationInfo getSubtypeInstrumentation(CtClass ctClass) {
    ClassInstrumentationInfo cii = null;
   
    //Check if class implements an instrumented interface
    for (CtClass currClassInterface : getImplementations(ctClass)) {
      ClassInstrumentationInfo cii2 = EurekaJAgentLauncher.getSubtypeInstrumentationHash().get(currClassInterface.getName().replaceAll("/", "."));
      if (cii2 != null && cii2.isDoesImplement()) {
        log.info("Class " + currClassInterface.getName().replaceAll("/", ".") + " is instrumented as implements. Instrumenting class: " + ctClass.getName());
        cii = cii2;
        break;
      }
    }
   
    //if interface is not instrumented, try superclass
    CtClass superClass = getSuperclass(ctClass);
    if (superClass != null) {
      //Class has a superclass
      ClassInstrumentationInfo cii2 = EurekaJAgentLauncher.getSubtypeInstrumentationHash().get(superClass.getName().replaceAll("/", "."));
      if (cii2 != null && cii2.isDoesExtend()) {
        cii = cii2;
      }
    }
   
       
    return cii;
  }

  private ClassInstrumentationInfo getWildcardInstrumentation(CtClass ctClass, String fullyQualifiedPackageName) {
    ClassInstrumentationInfo cii = null;

    String[] parts = fullyQualifiedPackageName.split("\\.");
    while (parts.length > 0) {
      // Slit package name and look back in the hierarchy
      String tempName = "";
      for (int i = 0; i < parts.length; i++) {
        tempName += parts[i] + ".";
      }
      tempName += "*";
      ClassInstrumentationInfo cii2 = EurekaJAgentLauncher.getInstrumentationHash().get(tempName);
      if (cii2 != null) { // Matching wildcard package
        cii = cii2;
        break;
      }
      // Reduce parts array by 1
      String[] parts2 = new String[parts.length - 1];
      System.arraycopy(parts, 0, parts2, 0, parts2.length);
      parts = parts2;
    }

    return cii;
  }

  /**
   * Check if this class is known in the loader. Add it to the loader if it is
   * not already added VERY important in JEE environments
   *
   * @param loader
   */
  private void loadClassInClassloader(ClassLoader loader) {
    if (!classLoaders.containsKey(loader)) {
      classPool.insertClassPath(new LoaderClassPath(loader));
      classLoaders.put(loader, loader);
    }
  }
 
  private boolean isStatic(int acccessFlags) {
    return (acccessFlags & AccessFlag.STATIC) != 0;
  }

  private CtClass getSuperclass(CtClass currClass) {
    if (currClass == null) {
      return null;
    }
    CtClass superClass = null;
    try {
      superClass = currClass.getSuperclass();
    } catch (NotFoundException e) {
      // TODO Auto-generated catch block
      // e.printStackTrace();
    }

    return superClass;
  }

  private List<CtClass> getImplementations(CtClass currClass) {
    List<CtClass> implementations = new ArrayList<CtClass>();
    try {
      for (CtClass impl : currClass.getInterfaces()) {
        implementations.add(impl);
      }
    } catch (NotFoundException e) {
      // TODO Auto-generated catch block
      // e.printStackTrace();
      implementations.clear();
    }

    return implementations;
  }

  public boolean instrumentMethod(CtClass currClass, CtMethod method, ClassInstrumentationInfo cii) {
    boolean methodInstrumented = false;

    if (currClass.getName().endsWith("Servlet")) {
      log.info("\t\tInstrumenting Servlet: " + currClass.getName());
    }
   
    try {
      MethodInstrumentation mi = cii.getMethod(method.getName());
      if (currClass.getName().endsWith("Servlet")) {
        log.info("\t\tInstrumenting Servlet Method: " + mi + " :: " + method.getName());
      }
      if (method.getMethodInfo().isConstructor() || method.getName().startsWith("class") ||
          method.getName().startsWith("get") || method.getName().startsWith("set") ||
          method.getName().startsWith("is")) {
        // Simple methods and constructors not supported yet.
      } else if (mi != null) {
        if (mi.getClassType().equalsIgnoreCase("Increment")) {
          addIncrement(currClass, method.getName(), cii);
        } else if (mi.getClassType().equalsIgnoreCase("Decrement")) {
          addDecrement(currClass, method.getName(), cii);
        } else {
          addTiming(currClass, method.getName(), cii);
          /*
           * TODO: if (valueInstrumentation) {
           * addValueInstrumentation(currClass, method.getName(),
           * cii); }
           */
        }
        methodInstrumented = true;
      }
    } catch (NotFoundException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (CannotCompileException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }

    return methodInstrumented;
  }
 
  private boolean ignoreClass(String className) {
    boolean ignoreVal = false;
    /*for (int i = 0; i < ignore.length; i++) {
      if (className.startsWith(ignore[i])) {
        if (className.startsWith("java/sql") || className.startsWith("javax/faces/webapp") || className.endsWith("Servlet")) {
          ignoreVal = false;
        } else {
          ignoreVal = true;
        }
      }
    }*/
    return ignoreVal;
  }

  public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer)
      throws IllegalClassFormatException {
    // TODO Auto-generated method stub

    if (ignoreClass(className)) {
      return classfileBuffer;
    }
   
    //Make Class
    CtClass currClass = makeClass(classfileBuffer);
    if (currClass == null) { //If class cannot be made, return as is.
      return classfileBuffer;
    }

    byte[] bytestream = null;
    boolean methodInstrumented = false;
    String fullyQualifiedPackageName = className.replace("/", ".");   

    //Get Subtype instrumentation (both extends and implements)
    ClassInstrumentationInfo cii = getSubtypeInstrumentation(currClass);
    if (cii == null) {
      //Try up to 6 levels up
      for (int i = 0; i < 10; i++) {
        CtClass superClass = getSuperclass(currClass);
        if (superClass != null) {
          cii = getSubtypeInstrumentation(superClass);
        }
      }
    }

    if (cii == null) {
      // No match for Superclass or interface. Looking for direct match
      cii = EurekaJAgentLauncher.getInstrumentationHash().get(fullyQualifiedPackageName);
      // TODO: If direct match, and instrumentation is value, mark it so
      // that
      // the value can be returned
    }

    if (cii == null) {
      // No match for superclass or exact class. Look for wildcard in package
      cii = getWildcardInstrumentation(currClass, fullyQualifiedPackageName);
    }

    if (cii != null) {
      log.info("Class is instrumented: " + cii + " :: " + className);
      loadClassInClassloader(loader);

      try {
        if (currClass != null && !currClass.isInterface() && !currClass.getClassFile().isAbstract() && !currClass.isFrozen()) {
          // addLogging(currClass, className);
          CtMethod[] declaredMethods = currClass.getDeclaredMethods();
          for (CtMethod method : declaredMethods) {
            methodInstrumented = instrumentMethod(currClass, method, cii);
          }
        }

        if (methodInstrumented) {
          bytestream = currClass.toBytecode();
        }
      } catch (CannotCompileException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
    }

    return bytestream;
  }

  private void addTiming(CtClass clas, String mname, ClassInstrumentationInfo cii) throws NotFoundException, CannotCompileException, IOException {
    // get the method information
    StringBuffer before = new StringBuffer().append("{");
    StringBuffer after = new StringBuffer().append("{");
   
    MethodInstrumentation mi = cii.getMethod(mname);
    CtMethod oldMethod = clas.getDeclaredMethod(mname);
    oldMethod.addLocalVariable("_eurekaJTimingStart", CtClass.longType);
    //CtClass stackTraceClass = classPool.get("org.eurekaJ.agent.model.CallStackTraceBuilder");
    //oldMethod.addLocalVariable("_eurekaJStackTraceBuilder", stackTraceClass);
   
    if (clas.getPackageName().endsWith("sql") || clas.getName().endsWith("Statement")) {
      log.info("\n\n\n\t\t\tSQL Instrumetation: " + clas.getPackageName() + " ::: " + clas.getName());
    }
   
    before.append("_eurekaJTimingStart = System.currentTimeMillis();\n");
    //before.append("org.eurekaJ.agent.model.CallStackTraceBuilderFactory.getCallStackTraceBuilder().enter(\"" + mname + "\", \"" + oldMethod.getSignature() + "\", \"" + clas.getName() + "\", \"" + mi.getClassType() + "\", \"" + cii.getPath() + "\",_eurekaJTimingStart);\n");
   
    // Report statistic for this classtype only (overall)
    after.append("org.eurekaJ.agent.EurekaJStringLogger.addGroupStatistics(\"" + mi.getClassType()
        + "\", (System.currentTimeMillis() - _eurekaJTimingStart), _eurekaJTimingStart, \"" + mi.getClassType() + "\");\n");
    // Report statistic for this classtype and path (overall)
    after.append("org.eurekaJ.agent.EurekaJStringLogger.addGroupStatistics(\"" + mi.getClassType() + ":" + cii.getPath()
        + "\", (System.currentTimeMillis() - _eurekaJTimingStart), _eurekaJTimingStart, \"" + mi.getClassType() + "\");\n");
    // Report statistic for this classtype, path and class (overall)
    after.append("org.eurekaJ.agent.EurekaJStringLogger.addGroupStatistics(\"" + mi.getClassType() + ":" + cii.getPath() + ":" + clas.getName()
        + "\", (System.currentTimeMillis() - _eurekaJTimingStart), _eurekaJTimingStart, \"" + mi.getClassType() + "\");\n");
    // Report statistic for this method call (classtype, path, class and method)
    after.append("org.eurekaJ.agent.EurekaJStringLogger.addStatistic(\"" + clas.getName() + " " + mname
        + "\", (System.currentTimeMillis() - _eurekaJTimingStart), _eurekaJTimingStart, \"" + mi.getClassType() + "\", \"" + cii.getPath() + "\");\n");
   
   
    //after.append("org.eurekaJ.agent.model.CallStackTraceBuilderFactory.getCallStackTraceBuilder().leave(System.currentTimeMillis() - _eurekaJTimingStart);\n");
   
    oldMethod.insertBefore(before.append("}").toString());
    oldMethod.insertAfter(after.append("}").toString());
   
  }

  private void addIncrement(CtClass clas, String mname, ClassInstrumentationInfo cii) throws NotFoundException, CannotCompileException, IOException {
    CtMethod oldMethod = clas.getDeclaredMethod(mname);
    log.info("\tadding increment for class: " + clas.getName() + " method: " + mname);
    // Report increment value for this path
    oldMethod.insertBefore("org.eurekaJ.agent.EurekaJStringLogger.addIncrement(\"" + cii.getPath() + "\");\n");
   
  }

  private void addDecrement(CtClass clas, String mname, ClassInstrumentationInfo cii) throws NotFoundException, CannotCompileException, IOException {
    CtMethod oldMethod = clas.getDeclaredMethod(mname);
    // Report increment value for this path
    log.info("\tadding decrement for class: " + clas.getName() + " method: " + mname);
    oldMethod.insertBefore("org.eurekaJ.agent.EurekaJStringLogger.addDecrement(\"" + cii.getPath() + "\");\n");
  }

  private void addValueInstrumentation(CtClass clas, String mname, ClassInstrumentationInfo cii) throws NotFoundException, CannotCompileException,
      IOException {
    CtMethod oldMethod = clas.getDeclaredMethod(mname);
    String returntype = oldMethod.getReturnType().getName();

    log.info("Class " + clas.getName() + " has unknown return type: " + returntype);
    if (returntype.equals("int") || returntype.equals("java.lang.Integer") || returntype.equals("long") || returntype.equals("java.lang.Long")
        || returntype.equals("double") || returntype.equals("java.lang.Double") || returntype.equals("float") || returntype.equals("java.lang.Float")) {
      // Get return value

    }

    /*
     * String fqcn = clas.getName(); String oldMethodSignature =
     * oldMethod.getSignature(); // Keep any modifiers that may exist on the
     * method being replaced int modifiers = oldMethod.getModifiers(); int
     * accessFlags = oldMethod.getMethodInfo().getAccessFlags(); //
     * System.out.println("old method: " + oldMethod.getSignature());
     *
     * // MethodInfo info = oldMethod.getMethodInfo();
     *
     * // rename old method to synthetic name, then duplicate the method
     * with // original name for use as interceptor String newMethodname =
     * mname + "$impl"; oldMethod.setName(newMethodname); CtMethod
     * implMethod = CtNewMethod.copy(oldMethod, mname, clas, null);
     *
     * // start the body text generation by saving the start time to local
     * // variable, then call the timed method; // the actual code generated
     * needs to depend on whether the timed method // returns a value String
     * type = oldMethod.getReturnType().getName();
     *
     * StringBuffer body = new StringBuffer();
     * body.append("{\nlong eurekaJTimingstart = System.currentTimeMillis();\n"
     * ); body.append(
     * "org.eurekaJ.agent.model.CallStackTraceBuilder eurekaJStackTraceBuilder = org.eurekaJ.agent.model.CallStackTraceBuilderFactory.getCallStackTraceBuilder();\n"
     * ); // System.out.println("eurekaJStackTraceBuilder.enter(\"" + mname
     * + // "\", \"" + oldMethodSignature + "\", \"" + fqcn + //
     * "\",eurekaJTimingstart);");
     * body.append("eurekaJStackTraceBuilder.enter(\"" + mname + "\", \"" +
     * oldMethodSignature + "\", \"" + fqcn + "\",eurekaJTimingstart);\n");
     * if (!type.equals("void")) { body.append(type + " result = "); }
     * body.append(newMethodname + "($$);\n");
     *
     * // finish body text generation with call to print the timing //
     * information, and return saved value(if not void) //
     * body.append("_log.info(\"Call to method " + mname + //
     * " took \" + (System.currentTimeMillis() - start) + \" (ms) \");\n");
     * body.append(
     * "org.eurekaJ.agent.EurekaJStringLogger.appendToBuffer(\"ClassInstrumentation: DefaultAgent "
     * + clas.getName() + " " + mname +
     * " \" + eurekaJTimingstart + \" \" + (System.currentTimeMillis() - eurekaJTimingstart) + \" "
     * + cii.getClassType() + " " + cii.getPath() + "\");\n");
     *
     * body.append(
     * "eurekaJStackTraceBuilder.leave(System.currentTimeMillis() - eurekaJTimingstart);\n"
     * );
     *
     * if (!type.equals("void")) { body.append("return result;\n"); }
     * body.append("}");
     *
     * // print the generated code block just to show what was done //
     * System.out.println("Interceptor method body:"); //
     * System.out.println(body.toString());
     *
     * // replace the body of the interceptor method with generated code
     * block // and add it to class implMethod.setBody(body.toString());
     *
     * // Transfer the modifiers over from the old method to the newly
     * created // one implMethod.setModifiers(modifiers);
     * implMethod.getMethodInfo().setAccessFlags(accessFlags);
     *
     * // System.out.println("new impl method: " +
     * implMethod.getSignature()); // System.out.println("new method: " +
     * oldMethod.getSignature());
     *
     * clas.addMethod(implMethod);
     */
  }
}
 
TOP

Related Classes of org.eurekaJ.agent.EurekaJTransformer

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.