AOP和ASM字节码插桩笔记

安卓AOP三剑客:APT,AspectJ,Javassist
细究JVM栈帧&ASM字节码的核心技术
对比图
AOP和ASM字节码插桩笔记_第1张图片

Hunter 一个底层基于ASM 和 Gradle Transform API 实现的框架

依赖 https://asm.ow2.io/index.html

implementation  'org.ow2.asm:asm:7.1'
implementation  'org.ow2.asm:asm-commons:7.1'

流程

ClassReader读取字节码->Visitor处理字节码->ClassWriter生成字节码

  1. 准备待插桩Class javac InjectTest.java
  2. javap -v InjectTest.class 反编译得到,关于jvm的操作数栈可以自行了解

以上这两步可以用插件 ASM Bytecode viewer 查看java文件,main方法完整代码生成的字节码是这样的,后面字节码插桩方法照着对应的地方翻译过去就可以了

  public static main([Ljava/lang/String;)V throws java/lang/InterruptedException 
    INVOKESTATIC java/lang/System.currentTimeMillis ()J
    LSTORE 1
    LDC 100
    INVOKESTATIC java/lang/Thread.sleep (J)V
    INVOKESTATIC java/lang/System.currentTimeMillis ()J
    LLOAD 1
    LSUB
    LSTORE 3
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    NEW java/lang/StringBuilder
    DUP
    INVOKESPECIAL java/lang/StringBuilder.<init> ()V
    LDC "time: "
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    LLOAD 3
    INVOKEVIRTUAL java/lang/StringBuilder.append (J)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
    RETURN
    MAXSTACK = 4
    MAXLOCALS = 5
}
	public class InjectTest {
    public static void main(String[] args) throws InterruptedException {
        //long l=System.currentTimeMillis();
        Thread.sleep(100);
        //long allTime = System.currentTimeMillis() - l;
        //System.out.println("time: "+ allTime);
    }
	}

	public class ASMUnitTest {
    @Test
    public void test() throws IOException {

        //1
        FileInputStream file=new FileInputStream("/Users/fred/AndroidStudioProjects/Test/app/src/test/java/com/example/test/asm/InjectTest.class");

        //2
  		ClassReader classReader = new ClassReader(file);
        ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
        classReader.accept(new ClassVisitor(Opcodes.ASM5,classWriter) {
       
            @Override
            public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
                MethodVisitor methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions);
                MyMethodVistor myMethodVistor = new MyMethodVistor(Opcodes.ASM5, methodVisitor, access, name, descriptor);
                System.out.println("access:" + access + " name:" + name);
                return myMethodVistor;
            }
        },ClassReader.EXPAND_FRAMES);
        //3
        byte[] bytes=classWriter.toByteArray();
        FileOutputStream fos=new FileOutputStream("/Users/fred/AndroidStudioProjects/Test/app/src/test/java/com/example/test/asm/InjectTest.class");
        fos.write(bytes);
        fos.close();
    }
	public class MyMethodVistor extends AdviceAdapter{
        /**
         * Constructs a new {@link AdviceAdapter}.
         *
         * @param api           the ASM API version implemented by this visitor. Must be one of {@link
         *                      Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
         * @param methodVisitor the method visitor to which this adapter delegates calls.
         * @param access        the method's access flags (see {@link Opcodes}).
         * @param name          the method's name.
         * @param descriptor    the method's descriptor (see {@link //Type Type}).
         */
        protected MyMethodVistor(int api, MethodVisitor methodVisitor, int access, String name, String descriptor) {
            super(api, methodVisitor, access, name, descriptor);
        }

        @Override
        protected void onMethodEnter() {
            super.onMethodEnter();
            //方法进入 但是要有方法声明了ASMTest注解的地方才插入
            //long l=System.currentTimeMillis();
            //INVOKESTATIC java/lang/System.currentTimeMillis ()J
    		//LSTORE 1
            invokeStatic(Type.getType("Ljava/lang/System;"),new Method("currentTimeMills","()J"));
            int index = newLocal(Type.LONG_TYPE);
            storeLocal(index,Type.LONG_TYPE);
        }

        @Override
        protected void onMethodExit(int opcode) {
            super.onMethodExit(opcode);
            System.out.println("onMethodExit:"+opcode);
            //这里退出前处理
            //long allTime = System.currentTimeMillis() - l;
        	//System.out.println("time: "+ allTime);
        }

        /*@Override
        public void monitorEnter() {
            super.monitorEnter();
            //同步代码块的进入
        }

        @Override
        public void monitorExit() {
            super.monitorExit();
            //同步代码块的退出
        }*/
    }

AOP和ASM字节码插桩笔记_第2张图片

MethodVisitor中的visitAnnotation方法AOP和ASM字节码插桩笔记_第3张图片
插件第二个tab可以直接生成字节码数组
AOP和ASM字节码插桩笔记_第4张图片
transform gradle插件自动埋点
AOP和ASM字节码插桩笔记_第5张图片
获取所有输入的class文件,逐一处理AOP和ASM字节码插桩笔记_第6张图片
AOP和ASM字节码插桩笔记_第7张图片

AOP和ASM字节码插桩笔记_第8张图片

AOP和ASM字节码插桩笔记_第9张图片
AOP和ASM字节码插桩笔记_第10张图片

AOP和ASM字节码插桩笔记_第11张图片

你可能感兴趣的:(安卓,jvm,android,asm)