Package nginx.clojure.wave

Source Code of nginx.clojure.wave.MethodDatabase

/*
* Copyright (c) 2008-2013, Matthias Mann
* Copyright (C) 2014 Zhang,Yuexiang (xfeep)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*     * Redistributions of source code must retain the above copyright notice,
*       this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     * Neither the name of Matthias Mann nor the names of its
*       contributors may be used to endorse or promote products derived from
*       this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package nginx.clojure.wave;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;

import nginx.clojure.SuspendExecution;
import nginx.clojure.asm.ClassReader;
import nginx.clojure.asm.Type;
import nginx.clojure.logger.LoggerService;
import nginx.clojure.logger.TinyLogService;
import nginx.clojure.logger.TinyLogService.MsgType;
import nginx.clojure.wave.SuspendMethodVerifier.VerifyVarInfo;

/**
* <p>Collects information about classes and their suspendable methods.</p>
* <p>Provides access to configuration parameters and to logging</p>
*
* @author Matthias Mann
* @author Zhang,Yuexiang (xfeep)
*/
public class MethodDatabase implements LoggerService {
 
  public static final String NGINX_CLOJURE_LOG_WAVE_LEVEL = "nginx.clojure.logger.wave.level";
 
  public static final String SUSPEND_BLOCKING_STR = "blocking";
//  public static final String SUSPEND_IGNORE_STR = "ignore";
  public static final String SUSPEND_NONE_STR = "none";
  public static final String SUSPEND_JUST_MARK_STR = "just_mark";
  public static final String SUSPEND_NORMAL_STR = "normal";
  public static final String SUSPEND_FAMILY_STR = "family";
  public static final String SUSPEND_SKIP_STR = "skip";
 
  public static final Integer SUSPEND_NONE = 0;
  public static final Integer SUSPEND_BLOCKING = 1;
 
  public static final Integer SUSPEND_FAMILY = 2;
  /**
   * Those methods with suspend type SUSPEND_JUST_MARK will not or can not be waved,
   * but because they will call some SUSPEND_NORMAL methods, their caller should take care
   * of this case generally will be waved.
   */
  public static final Integer SUSPEND_JUST_MARK = 3;
 
  public static final Integer SUSPEND_NORMAL = 4;
 
  public static final Integer SUSPEND_SKIP = 5;
 
  public static final String[] SUSPEND_TYPE_STRS = new String[] {
    SUSPEND_NONE_STR,
    SUSPEND_BLOCKING_STR,
    SUSPEND_FAMILY_STR,
    SUSPEND_JUST_MARK_STR,
    SUSPEND_NORMAL_STR,
    SUSPEND_SKIP_STR
  };
 
 
  public static final String EXCEPTION_NAME = Type.getInternalName(SuspendExecution.class);
  public static final String EXCEPTION_DESC = Type.getDescriptor(SuspendExecution.class);
 
  public static final Charset UTF_8 = Charset.forName("utf-8");
   
    private final ClassLoader cl;
    private final ConcurrentHashMap<String, ClassEntry> classes;
   
    private final ConcurrentHashMap<String, LazyClassEntry> lazyClasses;
   
    private final ArrayList<FuzzyLazyClassEntry> fuzzlyLazyClasses;
   
    private final ArrayList<String> retransformedClasses = new ArrayList<String>();
   
    private final ArrayList<File> workList;
    private final ArrayList<String> filters;
    private ArrayList<String> userDefinedWaveConfigFiles = new ArrayList<String>();
   
    private Map<String, VerifyVarInfo[][]> verfiyMethodInfos;
   
    private static LoggerService log;
    private boolean verify;
    private boolean dump;
    private boolean allowMonitors;
    private boolean allowBlocking;
    private boolean allowOutofCoroutine = true;
    private boolean hookDumpWaveCfg = false;
    private boolean doNothing = false;
    private boolean runTool = false;
    private Pattern traceClassPattern = null;
    private Pattern traceClassMethodPattern = null;
   
    private String dumpDir;
   
    public MethodDatabase(ClassLoader classloader) {
        if(classloader == null) {
            throw new NullPointerException("classloader");
        }
       
        this.cl = classloader;
       
        classes = new ConcurrentHashMap<String, ClassEntry>();
        lazyClasses = new ConcurrentHashMap<String, LazyClassEntry>();
        fuzzlyLazyClasses = new ArrayList<MethodDatabase.FuzzyLazyClassEntry>();
        workList = new ArrayList<File>();
        filters = new ArrayList<String>();
        getLog();
    }
   
    public ArrayList<String> getRetransformedClasses() {
    return retransformedClasses;
  }

    public boolean isAllowMonitors() {
        return allowMonitors;
    }

    public void setAllowMonitors(boolean allowMonitors) {
        this.allowMonitors = allowMonitors;
    }

    public Pattern getTraceClassPattern() {
    return traceClassPattern;
  }

  public void setTraceClassPattern(Pattern traceClassPattern) {
    this.traceClassPattern = traceClassPattern;
  }
 
  public Pattern getTraceClassMethodPattern() {
    return traceClassMethodPattern;
  }
 
  public void setTraceClassMethodPattern(Pattern traceClassMethodPattern) {
    this.traceClassMethodPattern = traceClassMethodPattern;
  }

  public boolean isAllowBlocking() {
        return allowBlocking;
    }

    public void setAllowBlocking(boolean allowBlocking) {
        this.allowBlocking = allowBlocking;
    }
   
    public boolean isAllowOutofCoroutine() {
    return allowOutofCoroutine;
  }
   
    public void setAllowOutofCoroutine(boolean allowOutofCoroutine) {
    this.allowOutofCoroutine = allowOutofCoroutine;
  }

    public ConcurrentHashMap<String, ClassEntry> getClasses() {
    return classes;
  }
   
    public Map<String, VerifyVarInfo[][]> getVerfiyMethodInfos() {
    return verfiyMethodInfos;
  }
   
    public ConcurrentHashMap<String, LazyClassEntry> getLazyClasses() {
    return lazyClasses;
  }
   
    public ArrayList<FuzzyLazyClassEntry> getFuzzlyLazyClasses() {
    return fuzzlyLazyClasses;
  }
   
    public static LoggerService getLog() {
        if (log == null) {
          log = new TinyLogService(TinyLogService.getSystemPropertyOrDefaultLevel(NGINX_CLOJURE_LOG_WAVE_LEVEL, MsgType.error), System.err, System.err);
        }
        return log;
    }

    public  static void setLog(LoggerService log) {
        MethodDatabase.log = log;
    }

   
    public void setRunTool(boolean runTool) {
    this.runTool = runTool;
  }
   
    public boolean isRunTool() {
    return runTool;
  }

    public void setDoNothing(boolean doNothing) {
    this.doNothing = doNothing;
  }
   
    public boolean isDoNothing() {
    return doNothing;
  }
   
    public boolean isDump() {
    return dump;
  }
   
    public void setDump(boolean dump) {
    this.dump = dump;
  }
   
    public String getDumpDir() {
    return dumpDir;
  }
   
    public void setDumpDir(String dumpDir) {
    this.dumpDir = dumpDir;
  }
   
    public boolean isHookDumpWaveCfg() {
    return hookDumpWaveCfg;
  }
   
    public void setHookDumpWaveCfg(boolean hookDumpWaveCfg) {
    this.hookDumpWaveCfg = hookDumpWaveCfg;
  }
   
    public void setVerify(boolean verify) {
    this.verify = verify;
    if (verify && verfiyMethodInfos == null) {
      verfiyMethodInfos = new ConcurrentHashMap<String, VerifyVarInfo[][]>();
    }
  }
   
    public boolean isVerify() {
    return verify;
  }
   
    public boolean isDebug() {
        return log.isDebugEnabled();
    }

    public void setDebug(boolean debug) {
        if (log instanceof TinyLogService) {
      TinyLogService tlog = (TinyLogService) log;
      tlog.setLevel(MsgType.debug);
    }
    }

   
    public void error(String msg, Exception ex) {
        if(log != null) {
            log.error(msg, ex);
        }
    }
   
    public void checkClass(File f) {
        try {
            FileInputStream fis = new FileInputStream(f);
            CheckInstrumentationVisitor civ = checkFileAndClose(fis, f.getPath());
           
            if(civ != null) {
                recordSuspendableMethods(civ.getName(), civ.getClassEntry());

                if(civ.needsInstrumentation()) {
                    if(civ.isAlreadyInstrumented()) {
                        info("Found instrumented class: %s", f.getPath());
                    } else {
                        info("Found class: %s", f.getPath());
                        workList.add(f);
                    }
                }
            }
        } catch (UnableToInstrumentException ex) {
            throw ex;
        } catch (Exception ex) {
            error(f.getPath(), ex);
        }
    }
   

    public ArrayList<String> getFilters() {
    return filters;
  }
   
    protected Integer checkMethodFamilySuspendType(ClassEntry ce, String fullname) {
      Integer st = ce.check(fullname);
      Integer fst = st;
      if (st == null || st == SUSPEND_NONE) {
        if (ce.getSuperName() != null) {
          ClassEntry sce = classes.get(ce.getSuperName());
            if (sce != null) {
              st = checkMethodFamilySuspendType(sce, fullname);
            }
            if (st != null && (fst == null || fst == SUSPEND_NONE)) {
                fst = st;
              }
        }
        if ((st == null || st == SUSPEND_NONE) && ce.interfaces != null) {
          for (String itf : ce.interfaces) {
            ClassEntry sce = classes.get(itf);
            if (sce != null) {
              st = checkMethodFamilySuspendType(sce, fullname);
              if (st != null && (fst == null || fst == SUSPEND_NONE)) {
                    fst = st;
                  }
            }
          }
        }
      }
        return st == null ? fst : st;
    }
   
    public final Integer checkMethodSuspendType(String className, String method, boolean searchSuperClass) {
      return checkMethodSuspendType(className, method, searchSuperClass, true);
    }
   
    public final Integer checkMethodSuspendType(String className, String method, boolean searchSuperClass, boolean returnDefault) {
        if(method.charAt(0) == '<' && method.charAt(1) == 'c') {
            return SUSPEND_NONE;   // special methods are never suspendable
        }
       
//        if(isJavaCore(className)) {
//            return SUSPEND_NONE;
//        }
//        String fullname = ClassEntry.key(methodName, methodDesc);
        ClassEntry ce = MethodDatabaseUtil.buildClassEntryFamily(this, className);
        if (ce == null) {
          if (method.charAt(0) == '<') {
            warn("contructor's class not found - assuming not suspendable: %s#%s", className, method);
            return SUSPEND_NONE;
          }
          warn("not found class - assuming suspendable: %s#%s", className, method);
          return SUSPEND_NORMAL;
        }
        Integer st = null;
        if (searchSuperClass) {
          st =  checkMethodFamilySuspendType(ce, method);
        }else {
          st = ce.check(method);
        }
      
        if (st == null && returnDefault) {
          if(method.charAt(0) == '<') {
            return SUSPEND_NONE;
          }
           warn("Method not found in class - assuming suspendable: %s#%s", className, method);
             st = SUSPEND_NORMAL;
        }
        return st;
    }


    public void recordSuspendableMethods(String className, ClassEntry entry) {
        ClassEntry oldEntry;
        synchronized(this) {
            oldEntry = classes.put(className, entry);
        }
        if (log.isDebugEnabled()) {
            if(oldEntry != null) {
                if(!oldEntry.equals(entry)) {
                  warn("Duplicate class entries with different data for class: %s", className);
                }
            }
        }
    }
   
  private  boolean typeImplements(String type, ClassEntry ce, String itf) {
    while (!"java/lang/Object".equals(type)) {
      String[] itfs = ce.getInterfaces();
      for (int i = 0; i < itfs.length; ++i) {
        if (itfs[i].equals(itf)) {
          return true;
        }
      }
      for (int i = 0; i < itfs.length; ++i) {
        if (typeImplements(itfs[i], MethodDatabaseUtil.buildClassEntryFamily(this, itfs[i]), itf)) {
          return true;
        }
      }
      type = ce.getSuperName();
      ce = MethodDatabaseUtil.buildClassEntryFamily(this, type);
    }
    return false;
  }
   
    public String getCommonSuperClass(String classA, String classB) {
      ClassEntry ace = MethodDatabaseUtil.buildClassEntryFamily(this, classA);
      ClassEntry bce = MethodDatabaseUtil.buildClassEntryFamily(this, classB);
      if (ace == null || bce == null) {
        return "java/lang/Object";
      }
    if (ace.isInterface && bce.isInterface) {
      if (typeImplements(classA, ace, classB)) {
        return classB;
      }
      if (typeImplements(classB, bce, classA)) {
        return classA;
      }
      return "java/lang/Object";
    }
   
    if (ace.isInterface) {
      if (typeImplements(classB, bce, classA)) {
        return classA;
      } else {
        return "java/lang/Object";
      }
    }
    if (bce.isInterface) {
      if (typeImplements(classA, ace, classB)) {
        return classB;
      } else {
        return "java/lang/Object";
      }
    }
        ArrayList<String> listA = getSuperClasses(classA);
        ArrayList<String> listB = getSuperClasses(classB);
        if(listA == null || listB == null) {
            return null;
        }
        int idx = 0;
        int num = Math.min(listA.size(), listB.size());
        for(; idx<num ; idx++) {
            String superClassA = listA.get(idx);
            String superClassB = listB.get(idx);
            if(!superClassA.equals(superClassB)) {
                break;
            }
        }
        if(idx > 0) {
            return listA.get(idx-1);
        }
        return null;
    }

    public void debug(Object message) {
      log.debug(message);
  }

  public void debug(Object message, Throwable t) {
      log.debug(message, t);
  }

  public void debug(String format, Object... objects) {
      log.debug(format, objects);
  }

  public void error(Object message) {
    log.error(message);
  }

  public void error(Object message, Throwable t) {
    log.error(message, t);
  }

  public void error(String format, Object... objects) {
    log.error(format, objects);
  }

  public void fatal(Object message) {
    log.fatal(message);
  }

  public void fatal(Object message, Throwable t) {
    log.fatal(message, t);
  }

  public void fatal(String format, Object... objects) {
    log.fatal(format, objects);
  }

  public void info(Object message) {
    log.info(message);
  }

  public void info(Object message, Throwable t) {
    log.info(message, t);
  }

  public void info(String format, Object... objects) {
    log.info(format, objects);
  }

  public boolean isDebugEnabled() {
    return log.isDebugEnabled();
  }

  public boolean isErrorEnabled() {
    return log.isErrorEnabled();
  }

  public boolean isFatalEnabled() {
    return log.isFatalEnabled();
  }

  public boolean isInfoEnabled() {
    return log.isInfoEnabled();
  }

  public boolean isTraceEnabled() {
    return log.isTraceEnabled();
  }

  public boolean isWarnEnabled() {
    return log.isWarnEnabled();
  }

  public void trace(Object message) {
    log.trace(message);
  }

  public void trace(Object message, Throwable t) {
    log.trace(message, t);
  }

  public void warn(Object message) {
    log.warn(message);
  }

  public void warn(Object message, Throwable t) {
    log.warn(message, t);
  }

  public void trace(String format, Object... objects) {
    log.trace(format, objects);
  }

  @Override
  public void warn(String format, Object... objects) {
    log.warn(format, objects);
  }
 
  public boolean isException(String className) {
        for(;;) {
            if("java/lang/Throwable".equals(className)) {
                return true;
            }
            if("java/lang/Object".equals(className)) {
                return false;
            }

            String superClass = getDirectSuperClass(className);
            if(superClass == null) {
                warn("Can't determine super class of %s", className);
                return false;
            }
            className = superClass;
        }
    }

    public ArrayList<File> getWorkList() {
        return workList;
    }

    /**
     * <p>Overwrite this function if Coroutines is used in a transformation chain.</p>
     * <p>This method must create a new CheckInstrumentationVisitor and visit the
     * specified class with it.</p>
     * @param className the class the needs to be analysed
     * @return a new CheckInstrumentationVisitor that has visited the specified
     * class or null if the class was not found
     */
    public  CheckInstrumentationVisitor checkClass(String className) {
        InputStream is = cl.getResourceAsStream(className + ".class");
        if(is != null) {
            return checkFileAndClose(is, className);
        }
        return null;
    }
   
    public ClassLoader getClassLoader() {
      return cl;
    }
   
    public  CheckInstrumentationVisitor checkClass(ClassReader r) {
    try {
      CheckInstrumentationVisitor civ = new CheckInstrumentationVisitor();
      r.accept(civ, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES
          | ClassReader.SKIP_CODE);
      return civ;
    } catch (UnableToInstrumentException ex) {
      throw ex;
    } catch (Exception ex) {
      error(r.getClassName(), ex);
    }
    return null;
    }
   
    public CheckInstrumentationVisitor checkFileAndClose(InputStream is, String name) {
        try {
            try {
                ClassReader r = new ClassReader(is);

                CheckInstrumentationVisitor civ = new CheckInstrumentationVisitor();
                r.accept(civ, ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES|ClassReader.SKIP_CODE);
               
                return civ;
            } finally {
                is.close();
            }
        } catch (UnableToInstrumentException ex) {
            throw ex;
        } catch (Exception ex) {
            error(name, ex);
        }
        return null;
    }

   
    public boolean isInterface(String className) {
      ClassEntry ce = MethodDatabaseUtil.buildClassEntryFamily(this, className);
      return ce != null && ce.isInterface;
    }

    private ArrayList<String> getSuperClasses(String className) {
        ArrayList<String> result = new ArrayList<String>();
        for(;;) {
            result.add(0, className);
            if("java/lang/Object".equals(className)) {
                return result;
            }

            String superClass = getDirectSuperClass(className);
            if(superClass == null) {
                return result;
            }
            className = superClass;
        }
    }

    protected String getDirectSuperClass(String className) {
      ClassEntry ce = MethodDatabaseUtil.buildClassEntryFamily(this, className);
      return ce == null ? "java/lang/Object" : ce.superName;
    }
   
    public boolean shouldIgnore(String className) {
      for (String f : filters) {
        if (className.startsWith(f)) {
          return true;
        }
      }
      return false;
    }
   
    public boolean meetTraceTargetClass(String clz) {
      return traceClassPattern != null && traceClassPattern.matcher(clz).find();
    }
   
    public boolean meetTraceTargetClassMethod(String clz, String method) {
      return traceClassMethodPattern != null && traceClassMethodPattern.matcher(clz + "." + method).find();
    }
   
    public boolean meetTraceTargetClassMethod(String clzAndmethod) {
      return traceClassMethodPattern != null && traceClassMethodPattern.matcher(clzAndmethod).find();
    }
   
    public static boolean isJavaCore(String className) {
        return className.startsWith("java/") || className.startsWith("javax/") ||
                className.startsWith("sun/") || className.startsWith("com/sun/");
    }
   
    public static String[] toStringArray(List<?> l) {
        if(l.isEmpty()) {
            return null;
        }
        return l.toArray(new String[l.size()]);
    }
   
    public ArrayList<String> getUserDefinedWaveConfigFiles() {
    return userDefinedWaveConfigFiles;
  }
   
    private static final ClassEntry CLASS_NOT_FOUND = new ClassEntry("<class not found>", new String[0], false);

   
    public static final class LazyClassEntry {
      private final LinkedHashMap<String, Integer> methods = new LinkedHashMap<String, Integer>();
      private final String resource;
      public LazyClassEntry(String resource) {
        this.resource = resource;
    }
      public final LinkedHashMap<String, Integer> getMethods() {
      return methods;
    }
      public final String getResource() {
      return resource;
    }
    }
   
    public static final class FuzzyLazyClassEntry {
      private final LinkedHashMap<String, Integer> methods = new LinkedHashMap<String, Integer>();
      private final String resource;
      private final Pattern pattern;
     
      public FuzzyLazyClassEntry(Pattern pattern, String resource) {
        this.pattern = pattern;
        this.resource = resource;
    }
      public final LinkedHashMap<String, Integer> getMethods() {
      return methods;
    }
      public final String getResource() {
      return resource;
    }
     
      public final Pattern getPattern() {
      return pattern;
    }
    }
   
    public static final class ClassEntry {
     

     
        private final ConcurrentHashMap<String, Integer> methods;
        private final boolean isInterface;
        private final String superName;
        private final String[] interfaces;
        private boolean alreadyInstrumented;

        public ClassEntry(String superName, String[] interfaces, boolean isInterface) {
            this.superName = superName;
            this.interfaces = interfaces;
            this.isInterface = isInterface;
            this.methods = new ConcurrentHashMap<String, Integer>();
        }
       
        public boolean isInterface() {
      return isInterface;
    }
       
        public String[] getInterfaces() {
      return interfaces;
    }
       
        public ConcurrentHashMap<String, Integer> getMethods() {
      return methods;
    }
       
        public String getSuperName() {
      return superName;
    }
       
        public final void set(String name, String desc, Integer suspendable) {
            String nameAndDesc = key(name, desc);
            methods.put(nameAndDesc, suspendable);
        }
       
        public final void set(String nameAndDesc, Integer suspendable) {
            methods.put(nameAndDesc, suspendable);
        }
       
        public final Integer get(String nameAndDesc) {
            return methods.get(nameAndDesc);
        }
       
        public final Integer check(String name, String desc) {
            return methods.get(key(name, desc));
        }
       
        public final Integer check(String fullname) {
            return methods.get(fullname);
        }
       

        @Override
        public int hashCode() {
            return superName.hashCode() * 67 + methods.hashCode();
        }

        @Override
        public boolean equals(Object obj) {
            if(!(obj instanceof ClassEntry)) {
                return false;
            }
            final ClassEntry other = (ClassEntry)obj;
            return (superName == other.superName || superName != null && superName.equals(other.superName)) && methods.equals(other.methods);
        }
       
        public static String key(String methodName, String methodDesc) {
            return methodName.concat(methodDesc);
        }
       
        public boolean isAlreadyInstrumented() {
      return alreadyInstrumented;
    }
       
        public void setAlreadyInstrumented(boolean alreadyInstrumented) {
      this.alreadyInstrumented = alreadyInstrumented;
    }
       
    }

}
TOP

Related Classes of nginx.clojure.wave.MethodDatabase

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.