Package nginx.clojure.wave

Source Code of nginx.clojure.wave.JavaAgent

/*
* 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.
*/

/*
* Copyright (c) 2012, Enhanced Four
* 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 'Enhanced Four' 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.IOException;
import java.io.RandomAccessFile;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import java.util.regex.Pattern;

import nginx.clojure.Stack;
import nginx.clojure.asm.ClassReader;
import nginx.clojure.asm.ClassVisitor;
import nginx.clojure.asm.ClassWriter;
import nginx.clojure.asm.MethodVisitor;
import nginx.clojure.asm.Opcodes;
import nginx.clojure.asm.commons.JSRInlinerAdapter;
import nginx.clojure.asm.util.CheckClassAdapter;
import nginx.clojure.logger.TinyLogService;
import nginx.clojure.wave.MethodDatabase.ClassEntry;

/*
* Created on Nov 21, 2010
*
* @author Riven
* @author Matthias Mann
* @author Zhang,Yuexing (xfeep)
*/
public class JavaAgent {
 
  public static final String NGINX_CLOJURE_WAVE_UDFS = "nginx.clojure.wave.udfs";
  public static final String NGINX_CLOJURE_WAVE_TRACE_CLASSMETHODPATTERN = "nginx.clojure.wave.trace.classmethodpattern";
  public static final String NGINX_CLOJURE_WAVE_TRACE_CLASSPATTERN = "nginx.clojure.wave.trace.classpattern";
  public static final String NGINX_CLOJURE_WAVE_DUMPDIR = "nginx.clojure.wave.dumpdir";
  public static MethodDatabase db;
 
    public static void premain(String agentArguments, Instrumentation instrumentation) {
      ClassFileTransformer cft = buildClassFileTransformer(agentArguments);
      if (cft != null) {
        instrumentation.addTransformer(cft, true);
        for (String c : db.getRetransformedClasses()) {
          try {
          instrumentation.retransformClasses(db.getClassLoader().loadClass(c));
        } catch (Throwable e) {
          db.warn("retransformClasses error:" + c, e);
        }
        }
      }
    }
   
    public static ClassFileTransformer buildClassFileTransformer(String agentArguments) {

        MethodDatabase db = JavaAgent.db = new MethodDatabase(Thread.currentThread().getContextClassLoader());
        boolean checkArg = false;
//        boolean runTool = false;
        boolean append = false;

        if(agentArguments != null) {
            for(char c : agentArguments.toCharArray()) {
                switch(c) {
                    case 'v':
                        db.setVerify(true);
                        break;

                    case 'm':
                        db.setAllowMonitors(true);
                        break;
                    case 'd':
                     
                      break;
                    case 'c':
                        checkArg = true;
                        break;

                    case 'b':
                        db.setAllowBlocking(true);
                        break;
                    case 't':
                      db.setRunTool(true);
                      break;
                    case 'a':
                      append = true;
                      break;
                    case 'h':
                      db.setHookDumpWaveCfg(true);
                      break;
                    case 'p' :
                      db.setDump(true);
                      break;
                    case 'n':
                      TinyLogService.createDefaultTinyLogService().info("nginx clojure will do nothing about class waving!");
                      //do nothing!!
                      db.setDoNothing(true);
                      return null;
                    default:
                        throw new IllegalStateException("Usage: nvdmcbtap (do Nothing, Verbose, Debug, allow Monitors, Check class, allow Blocking, run configuration generation Tool,  Append result, dumP waved class)");
                }
            }
        }

        MethodDatabase.getLog();
       
    if (System.getProperty(NGINX_CLOJURE_WAVE_DUMPDIR) != null) {
      db.setDumpDir(System.getProperty(NGINX_CLOJURE_WAVE_DUMPDIR));
    } else {
      db.setDumpDir(System.getProperty("java.io.tmpdir") + "/nginx-clojure-wave-dump");
    }
       
        if (System.getProperty(NGINX_CLOJURE_WAVE_TRACE_CLASSPATTERN) != null) {
          db.setTraceClassPattern(Pattern.compile(System.getProperty(NGINX_CLOJURE_WAVE_TRACE_CLASSPATTERN)));
        }
       
        if (System.getProperty(NGINX_CLOJURE_WAVE_TRACE_CLASSMETHODPATTERN) != null) {
          db.setTraceClassMethodPattern(Pattern.compile(System.getProperty(NGINX_CLOJURE_WAVE_TRACE_CLASSMETHODPATTERN)));
        }
       
        //load system configurations for method database
        try {
          db.info("load system coroutine wave file %s", "nginx/clojure/wave/coroutine-method-db.txt");
      MethodDatabaseUtil.load(db, "nginx/clojure/wave/coroutine-method-db.txt");
    } catch (IOException e) {
      db.error("can not load nginx/clojure/wave/coroutine-method-db.txt", e);
    }
        String udfs = System.getProperty(NGINX_CLOJURE_WAVE_UDFS);
    if (udfs != null) {
      for (String udf : udfs.split(",|;")) {
        try {
          db.info("load use defined coroutine wave file %s", udf);
          MethodDatabaseUtil.load(db, udf);
        } catch (IOException e) {
          db.warn("can not load " + udf, e);
        }
      }
    }
       
        Stack.setDb(db);
       
        MethodDatabaseUtil.buildClassEntryFamily(db, "nginx/clojure/wave/MethodDatabaseUtil");
        SuspendMethodVerifier.db = db;
       
        if (db.isRunTool()) {
          SuspendMethodTracer.db = db;
          SuspendMethodTracer.quiteFlags.set(false);
          return new CoroutineConfigurationToolWaver(db, append);
        }else {
          return new CoroutineWaver(db, checkArg);
        }
       
   
    }

    static byte[] instrumentClass(MethodDatabase db, byte[] data, boolean check) {
        ClassReader r = new ClassReader(data);
        ClassWriter cw = new DBClassWriter(db, r);
        ClassVisitor cv = check ? new CheckClassAdapter(cw) : cw;
        ClassEntry ce = MethodDatabaseUtil.buildClassEntryFamily(db, r);
        if(db.shouldIgnore(r.getClassName())) {
            return null;
        }
        db.trace("TRANSFORM: %s", r.getClassName());
        InstrumentClass ic = new InstrumentClass(r.getClassName(), ce, cv, db, false);
        r.accept(ic, ClassReader.SKIP_FRAMES);
        return cw.toByteArray();
    }
   
  public static void dumpClass(byte[] classfileBuffer, File df, MethodDatabase db)  {
    try{
      RandomAccessFile raf = new RandomAccessFile(df, "rw");
      raf.setLength(0);
      raf.write(classfileBuffer, 0, classfileBuffer.length);
      raf.close();
    }catch(IOException e) {
      e.printStackTrace();
      db.error("dump file : " + df.getName(), e);
    }
  }

    public static class CoroutineWaver implements ClassFileTransformer {
        private final MethodDatabase db;
        private final boolean check;

        public CoroutineWaver(MethodDatabase db, boolean check) {
            this.db = db;
            this.check = check;
        }

        @Override
        public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
                ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {

          if (className.startsWith("java/util/LinkedHashMap")) {
            return null;
          }
          if (db.meetTraceTargetClass(className)) {
            db.info("meet traced class %s", className);
          }
     
            try {
                byte[] bs = instrumentClass(db, classfileBuffer, check);
                if (db.isDump() && bs != null && bs.length != classfileBuffer.length) {
                  File wavedFile = new File(new File(db.getDumpDir() + "/waved"), className + ".class");
                  wavedFile.getParentFile().mkdirs();
                  dumpClass(bs, wavedFile, db);
                }
              if (db.meetTraceTargetClass(className)) {
                File orgFile = new File(new File(db.getDumpDir() + "/org"), className + ".class");
                orgFile.getParentFile().mkdirs();
                dumpClass(classfileBuffer, orgFile, db);
              }
                return bs;
            } catch(Throwable ex) {
              if (db.isDump()){
                File errDumpFile = new File(new File(db.getDumpDir() + "/failed"), className + ".class");
                errDumpFile.getParentFile().mkdirs();
                dumpClass(classfileBuffer, errDumpFile, db);
              }
                db.error("Unable to instrument:" + className, ex);
                return null;
            }
           
        }

    }
   
    public static class CoroutineConfigurationToolWaver implements ClassFileTransformer {

      private final MethodDatabase db;
      private final boolean append;
     
      public CoroutineConfigurationToolWaver(MethodDatabase db, boolean append) {
        this.db = db;
        this.append = append;
        if (db.isHookDumpWaveCfg()) {
            Runtime.getRuntime().addShutdownHook(new Thread() {
              @Override
              public void run() {
                String file = System.getProperty("nginx.clojure.wave.CfgToolOutFile");
                if (file == null) {
                  file = "nginx.clojure.wave.cfgtooloutfile";
                  CoroutineConfigurationToolWaver.this.db.warn("system property 'nginx.clojure.wave.CfgToolOutFile' not found, use '%s' as file path", file);
                }
                try {
                SuspendMethodTracer.dump(file, CoroutineConfigurationToolWaver.this.append);
              } catch (Throwable e) {
                CoroutineConfigurationToolWaver.this.db.error("dump error!", e);
              }
              }
            });
        }
    }
//      filter:org/objectweb/asm/     
//      filter:nginx/clojure/asm/
//      filter:java/
//      filter:sun/
//      filter:com/sun/
//      filter:clojure/asm
//      filter:clojure/lang
//      filter:clojure/core
//      filter:org/junit
    @Override
    public byte[] transform(ClassLoader loader, final String className, Class<?> classBeingRedefined,
        ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
     
          if (db.meetTraceTargetClass(className)) {
            db.info("meet traced class %s", className);
          }
     
      if (SuspendMethodTracer.quiteFlags.get()) {
        return classfileBuffer;
      }
      try {
       
        ClassReader cr = new ClassReader(classfileBuffer);
        ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES) {
          @Override
          protected String getCommonSuperClass(String type1, String type2) {
            return db.getCommonSuperClass(type1, type2);
          }
        };
       
        ClassEntry ce = MethodDatabaseUtil.buildClassEntryFamily(db, cr);
//        if (className.startsWith("sun/launcher/")
//            || className.startsWith("clojure/asm")
//            || className.startsWith("org/objectweb/asm/")
//            || className.startsWith("clojure/asm")
//            || className.startsWith("org/junit")
////            || className.startsWith("clojure/main")
////            || className.startsWith("clojure/lang")
//            || className.startsWith("sun/misc/ClassFileTransformer")
//            || className.startsWith("nginx/clojure/asm")
//            || className.startsWith("nginx/clojure/wave")
//            || className.startsWith("java/util/ArrayList")
//            || className.startsWith("org/eclipse/jetty/server/")
//            || className.startsWith("clojure/lang/Compiler")
//            || className.startsWith("java/net/URLClassLoader")
//            || className.startsWith("java/io/PrintStream")
//            || className.startsWith("java/util/concurrent/ConcurrentHashMap")
//            || className.startsWith("java/util/concurrent/atomic/AtomicBoolean")
//            || className.startsWith("java/")
//            || className.startsWith("sun/")
//            || className.startsWith("com/sun/")
//            || className.startsWith("org/eclipse/jdt/")
//            || className.endsWith("ClassLoader")
//            || className.startsWith("clojure/lang/PersistentHashMap")
//            || className.startsWith("clojure/lang/Keyword")
//            || className.startsWith("clojure/lang/Symbol")
//            || className.startsWith("clojure/lang/Namespace")
//            || className.startsWith("clojure/lang/Persistent")
//            || className.startsWith("java/lang"))
        if (db.shouldIgnore(className)){
          db.debug("skip class %s", className);
          return classfileBuffer;
        }
        db.debug("loading class %s", className);
//        ClassVisitor cv = db.isVerbose() ?  new TraceClassVisitor(cw, new PrintWriter(System.out)) : cw;
        ClassVisitor cv = new ClassVisitor(Opcodes.ASM4, cw) {
          @Override
          public MethodVisitor visitMethod(int access, String name,
              String desc, String signature, String[] exceptions) {
            MethodVisitor mv = super.visitMethod(access, name,
                desc, signature, exceptions);
            if ( (Opcodes.ACC_NATIVE & access) != 0
                ||  (Opcodes.ACC_ABSTRACT & access) != 0
//                || name.startsWith("<cinit>")
                ) {
              db.debug("skip native or abstract method: %s.%s%s", className, name, desc);
              return mv;
            }
            return new JSRInlinerAdapter(new SuspendMethodTracerAdvice(db, className, mv, access, name, desc), access, name, desc, signature, exceptions);
          }
        };

        cr.accept(cv, ClassReader.EXPAND_FRAMES);
        byte[] rt = cw.toByteArray();
       
        if (db.isDump()) {
          File wavedFile = new File(new File(db.getDumpDir() + "/waved-by-tool"), className + ".class");
                    wavedFile.getParentFile().mkdirs();
                    dumpClass(rt, wavedFile, db);
        }
       
        return rt;
       
      } catch(Throwable ex) {
                  db.error("Unable to transform:" + className, ex);
                  return null;
          }
    }
     
    }
}
TOP

Related Classes of nginx.clojure.wave.JavaAgent

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.