zoukankan      html  css  js  c++  java
  • JVM插码之五:Java agent+ASM实战--监控所有方法执行时间

    本文建立在对instrumentation和agent有初步的了解的前提下阅读,关于这2个类的讲解在其它文章中。

    这是一个maven项目,pom中需要的配置,lib中有asm的jar包

    pom.xml文件:

    <project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.dxz</groupId>
        <artifactId>chama</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <packaging>jar</packaging>
    
        <name>chama</name>
        <url>http://maven.apache.org</url>
    
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.ow2.asm</groupId>
                <artifactId>asm-test</artifactId>
                <version>6.2</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.ow2.asm</groupId>
                <artifactId>asm-analysis</artifactId>
                <version>6.2</version>
            </dependency>
            <dependency>
                <groupId>org.ow2.asm</groupId>
                <artifactId>asm-commons</artifactId>
                <version>6.2</version>
            </dependency>
                    <dependency>
                <groupId>org.ow2.asm</groupId>
                <artifactId>asm-tree</artifactId>
                <version>6.2</version>
            </dependency>
                    <dependency>
                <groupId>org.ow2.asm</groupId>
                <artifactId>asm-util</artifactId>
                <version>6.2</version>
            </dependency>
            <dependency>
                <groupId>javassist</groupId>
                <artifactId>javassist</artifactId>
                <version>3.12.1.GA</version>
            </dependency>
    
            <!-- https://mvnrepository.com/artifact/cglib/cglib -->
            <dependency>
                <groupId>cglib</groupId>
                <artifactId>cglib</artifactId>
                <version>3.2.5</version>
            </dependency>
    
            <!-- https://mvnrepository.com/artifact/oro/oro -->
            <dependency>
                <groupId>oro</groupId>
                <artifactId>oro</artifactId>
                <version>2.0.8</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>3.8.1</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                        <encoding>utf-8</encoding>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-shade-plugin</artifactId>
                    <version>3.0.0</version>
                    <executions>
                        <execution>
                            <phase>package</phase>
                            <goals>
                                <goal>shade</goal>
                            </goals>
                            <configuration>
                                <transformers>
                                    <transformer
                                        implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                        <manifestEntries>
                                            <!-- <Premain-Class>com.dxz.chama.javaagent.patter.TimeMonitorPatterAgent</Premain-Class> -->
                                            <!-- <Premain-Class>com.dxz.chama.javaagent.StatAgent</Premain-Class> -->
                                            <Premain-Class>com.dxz.chama.javaagent.asm.UdAgent</Premain-Class>
                                        </manifestEntries>
                                    </transformer>
                                </transformers>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </project>

    打包后的agent的jar包中manifest属性如下:

    agent类,只有一个方法,就是把自定义的类修改器添加到instrumentation中。

    package com.dxz.chama.javaagent.asm;
    
    import java.lang.instrument.Instrumentation;
    
    public class UdAgent {
        public static void premain(String agentArgs, Instrumentation instrumentation){
            instrumentation.addTransformer(new LogTransformer());
        }
    }

    类转换器实现:

    package com.dxz.chama.javaagent.asm;
    
    import java.io.IOException;
    import java.lang.instrument.ClassFileTransformer;
    import java.lang.instrument.IllegalClassFormatException;
    import java.security.ProtectionDomain;
    
    import org.objectweb.asm.ClassReader;
    import org.objectweb.asm.ClassWriter;
    
    public class LogTransformer implements ClassFileTransformer {
        @Override
        public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
                ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
            try {
                ClassReader cr = new ClassReader(className);
                ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
                TimeCountAdpter timeCountAdpter = new TimeCountAdpter(cw);
    
                cr.accept(timeCountAdpter, ClassReader.EXPAND_FRAMES);
    
                return cw.toByteArray();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }
    }

    实际修改字节码的方法,这里给每个类添加了一个字段UDASMCN,用于记录当前类的名字(方便打印信息)。同时记录每个方法的名字,以及执行时间。

    package com.dxz.chama.javaagent.asm;
    
    import org.objectweb.asm.ClassVisitor;
    import org.objectweb.asm.FieldVisitor;
    import org.objectweb.asm.MethodVisitor;
    import org.objectweb.asm.Opcodes;
    import org.objectweb.asm.Type;
    import org.objectweb.asm.commons.AnalyzerAdapter;
    import org.objectweb.asm.commons.LocalVariablesSorter;
    
    public class TimeCountAdpter extends ClassVisitor implements Opcodes {
        private String owner;
        private boolean isInterface;
        private String filedName = "UDASMCN";
        private int acc = Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC + Opcodes.ACC_FINAL;
        private boolean isPresent = false;
    
        private String methodName;
    
        public TimeCountAdpter(ClassVisitor classVisitor) {
            super(ASM6, classVisitor);
        }
    
        @Override
        public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
            super.visit(version, access, name, signature, superName, interfaces);
            owner = name;
            isInterface = (access & ACC_INTERFACE) != 0;
        }
    
        @Override
        public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
            if (name.equals(filedName)) {
                isPresent = true;
            }
            return super.visitField(access, name, descriptor, signature, value);
        }
    
        @Override
        public MethodVisitor visitMethod(int access, String name, String descriptor, String signature,
                String[] exceptions) {
            MethodVisitor mv = cv.visitMethod(access, name, descriptor, signature, exceptions);
    
            if (!isInterface && mv != null && !name.equals("<init>") && !name.equals("<clinit>")) {
                methodName = name;
                AddTimerMethodAdapter at = new AddTimerMethodAdapter(mv);
                at.aa = new AnalyzerAdapter(owner, access, name, descriptor, at);
                at.lvs = new LocalVariablesSorter(access, descriptor, at.aa);
    
                return at.lvs;
            }
    
            return mv;
        }
    
        public void visitEnd() {
            if (!isInterface) {
                FieldVisitor fv = cv.visitField(acc, filedName, "Ljava/lang/String;", null, owner);
                if (fv != null) {
                    fv.visitEnd();
                }
            }
            cv.visitEnd();
        }
    
        class AddTimerMethodAdapter extends MethodVisitor {
            private int time;
            private int maxStack;
            public LocalVariablesSorter lvs;
            public AnalyzerAdapter aa;
    
            public AddTimerMethodAdapter(MethodVisitor methodVisitor) {
                super(ASM6, methodVisitor);
            }
    
            @Override
            public void visitCode() {
                mv.visitCode();
                mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "nanoTime", "()J", false);
                time = lvs.newLocal(Type.LONG_TYPE);
                mv.visitVarInsn(LSTORE, time);
                maxStack = 4;
            }
    
            @Override
            public void visitInsn(int opcode) {
                if (((opcode >= IRETURN && opcode <= RETURN) || opcode == ATHROW) && !isPresent) {
                    mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "nanoTime", "()J", false);
                    mv.visitVarInsn(LLOAD, time);
                    mv.visitInsn(LSUB);
                    mv.visitVarInsn(LSTORE, time);
    
                    mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
    
                    mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
                    mv.visitInsn(DUP);
                    mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);
                    mv.visitFieldInsn(GETSTATIC, owner, filedName, "Ljava/lang/String;");
                    mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append",
                            "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
    
                    mv.visitLdcInsn("  " + methodName + ":");
                    mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append",
                            "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
    
                    mv.visitVarInsn(LLOAD, time);
                    mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(J)Ljava/lang/StringBuilder;",
                            false);
                    mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
                    mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
    
                    maxStack = Math.max(aa.stack.size() + 4, maxStack);
                }
                mv.visitInsn(opcode);
            }
    
            @Override
            public void visitMaxs(int maxStack, int maxLocals) {
                super.visitMaxs(Math.max(maxStack, this.maxStack), maxLocals);
            }
        }
    
    }

    打包成jar包后,在另一个程序启动时调用,启动参数如下:

    -javaagent:D:studychama argetchama-0.0.1-SNAPSHOT.jar
    执行效果:
    时间单位是纳秒,可以看到每个方法执行完时,都会打印这个方法 的执行时间,以com/Main main:11457636为例,说明类com/Main的main方法执行力11毫秒。

  • 相关阅读:
    很不错的WebCart控件,分享给大家
    Atitit 功能扩展法细则条例 目录 1. 界面ui扩展 2 1.1. 使用h5做界面 2 1.2. 自制h5 ide。。简化ui自定义配置 2 2. 业务逻辑扩展 2 2.1. Bpm流程引擎还
    Atitit 持久化与数据存储标准化规范 目录 1. 存储的附加功能 2 1.1. 基本存取功能 2 1.2. 全文检索(imap 2 1.3. 属性检索 2 1.4. 查询语言 2 2. 基于内容
    Atitit 常见硬件集成列表 目录 1.1. 小程序设备类 1 1.2. atitit.常见手机的传感器与外设 attilax总结 1 1.3. Pc机外设 1 1.4. 设备管理器 2 1.1
    Atitit 项目wechat微信截屏生成vcf通讯录384 个 384个人 42个节拍,平均每个8个人 技术点 im图像裁剪, ocr Tesseract Vcf格式 /wechatTel
    atitit 音频 项目 系列功能表 音乐 v3 t67.docx Atitit 音频 项目 系列功能表 音频 音乐 语言领域的功能表 听歌识曲功能 酷我功能。 铃声 功能。。 音频切割(按照副歌部
    Atitit spring springboot 集成mybatis法 目录 1.1. 使用spring管理数据源。。需要修改spring、 配置 1 1.2. 直接代码集成,无需修改任何配置 1
    Atitit 艾提拉音频资源列表与统计 t6 六月份战果与7月份规划.docx 目录 1. 第一层次 原始资源类 采集资源类 1 1.1. K歌类采集资源 整理 1 1.2. K歌类资源初步分类
    Atitit 长距离无线通信法 LoRa NBIoT NBCIoT LoRa是Semtech公司的创新发明,该技术向用户提供显著的长距离、低功耗、安全数据传输机制。使用LoRa技术构建的公用网
    Atitit 读取音频音乐文件的bpm 目录 1.1. Librosa是一个用于音频、音乐分析、处理的python工具包, 1 1.2. \bpm.py 1 1.3. Echo 2 1.4. Cod
  • 原文地址:https://www.cnblogs.com/duanxz/p/6090190.html
Copyright © 2011-2022 走看看