zoukankan      html  css  js  c++  java
  • java instrumentation &JVMTI

    Java Instrumentation (参考:http://www.ibm.com/developerworks/cn/java/j-lo-jse61/

    简介:

    使用Instrumentation,开发者可以构建独立于应用程序的代理程序,用来检测和协助运行在JVM上的程序,甚至能够替换和修改某些类的定义

    遇到问题:

    运行在tomcat容器中的应用程序,由于main方法是在Bootstrap.jar中容器启动时执行的,

    Agent-class方式在替换的方法中只获取到tomcat相关jar包中的一些类:

    上图中是在transform方法中打印出来的,这个方法会在二中介绍

    具体介绍:

    下面是两种方式PreMain 和 agentMain,分别为main函数执行之前,和执行之后的操作

    一:PreMain

    main函数执行之间,扫描判断特定的类,然后以字节数组的方式加载代理类的字节码文件,替换目标类:

    示例代码:

    public class TransClass {
        public int getNumber() {
            return 2;
        }
    }
    TransClass
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.lang.instrument.ClassFileTransformer;
    import java.lang.instrument.IllegalClassFormatException;
    import java.security.ProtectionDomain;
    
    class Transformer implements ClassFileTransformer {
        public static final String classNumberReturns2 = "D://AOP//TransClass.class.2";
        public static byte[] getBytesFromFile(String fileName) {
        try {
            // precondition
            File file = new File(fileName);
            InputStream is = new FileInputStream(file);
            long length = file.length();
            byte[] bytes = new byte[(int) length];
        
            // Read in the bytes
            int offset = 0;
            int numRead = 0;
            while (offset <bytes.length && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
                offset += numRead;
            }
            if (offset < bytes.length) {
                throw new IOException("Could not completely read file "+ file.getName());
            }
            is.close();
            return bytes;
            } catch (Exception e) {
                System.out.println("error occurs in _ClassTransformer!"+ e.getClass().getName());
                return null;
            }
        }
    
        public byte[] transform(ClassLoader l, String className, Class<?> c,
            ProtectionDomain pd, byte[] b) throws IllegalClassFormatException {
            if (!className.equals("TransClass")) {
                return null;
            }
            return getBytesFromFile(classNumberReturns2);
        }
    } 
    Transformer
    public class TestMainInJar {
        public static void main(String[] args) {
            System.out.println(new TransClass().getNumber());
        }
    }
    TestMainInJar
    import java.lang.instrument.UnmodifiableClassException;
    import java.lang.instrument.Instrumentation;
    public class Premain {
    public static void premain(String agentArgs, Instrumentation inst)
        throws ClassNotFoundException, UnmodifiableClassException {
            inst.addTransformer(new Transformer());
        }
    } 
    Premain
    Manifest-Version: 1.0
    Premain-Class: Premain
    MANIFEST.MF

    步骤:

    1. TransClass.java 中 改成 return 2; 之后编译生成的TransClass.class改名为TransClass.class.2 ,以防止跟原TransClass.class重名

    2. 打包:jar -cvf0 TestInstrument1.jar TransClass.class Transformer.class TestMainInJar.class Premain.class

                打好jar后替换manifest.mf文件,(没找到怎么把这个文件直接打进jar包)

    3. 命令行执行

        1)java -javaagent:TestInstrument1.jar -cp TestInstrument1.jar TestMainInJar

        2)java -cp TestInstrument1.jar TestMainInJar

     (TransClass.class.2 和 TestInstrument1.jar 需要放置到  D:AOP 目录下)

        用第一个命令执行的时候,会把TransClass.class.2的内容加载进来替换jar包中的TransClass.class,而第二个命令是正常执行jar包中的TransClass.class

    二: Agent-class

    import java.lang.instrument.Instrumentation;
    
    public class LoadedAgent {
        @SuppressWarnings("rawtypes")
        public static void agentmain(String args, Instrumentation inst){
            Class[] classes = inst.getAllLoadedClasses();
        //inst.addTransformer(new Transformer());
            for(Class cls :classes){
                System.out.println(cls.getName());
            }
        }
    }
    LoadedAgent
    public class TargetVM {
        public static void main(String[] args) throws InterruptedException{
            while(true){
                Thread.sleep(1000);
            }
        }
    }
    TargetVM
    import java.io.IOException;
    
    import com.sun.tools.attach.AgentInitializationException;
    import com.sun.tools.attach.AgentLoadException;
    import com.sun.tools.attach.AttachNotSupportedException;
    import com.sun.tools.attach.VirtualMachine;
    
    public class Test {
        public static void main(String[] args) throws AttachNotSupportedException,
                IOException, AgentLoadException, AgentInitializationException {
            VirtualMachine vm = VirtualMachine.attach("1244");
            vm.loadAgent("D:/AOP/agentmain/agentAop.jar");
    
        }
    
    }
    Test
    Manifest-Version: 1.0
    Agent-Class: LoadedAgent
    MANIFEST.MF

    步骤:

        1. 将LoadedAgent.class 和 Manifest.mf打进jar包

        2. 执行TargetVM.class 获取进程号PID

        3. 执行Test.class pid作为参数

      

    注意:

        1. 编译时需要用到jdk中lib目录下的tools.jar  javac -cp tools.jar Test.java

        2. 执行命令(1244 是进程号) :java -classpath "D:/AOP/agentmain/tools.jar" Test 1244    

       

  • 相关阅读:
    SVN补充
    java面试资料总结
    JAVABEAN EJB POJO区别
    Hibernate使用原生sql语句
    Discrete Logging(poj 2417)
    卡牌游戏(bzoj 3191)
    Activation(hdu 4089)
    Aeroplane chess(hdu 4405)
    LOOPS(hdu 3853)
    巧克力(zoj 1363)
  • 原文地址:https://www.cnblogs.com/-wangjiannan/p/3903736.html
Copyright © 2011-2022 走看看