java agent笔记

1、介绍

Java agent是java虚拟机提供的一整套后门,可以对虚拟就进行跟踪、分析,以及基于ASM字节码技术修改字节码,使用方法一般在java -jar 加上 -javaagent 指定编写的agent代码包。

两种方式

一种是premain 方法,是在跟踪main方法之前的拦截器。

还有一种是agentmain方法,是跟踪程序运行时的拦截器,一般是配合attach api使用,利用VirtualMachine.attach(pid),随后对运行中程序进行跟踪分析修改。

2、功能

Arthas:阿里开发的在线诊断工具,无需重启就可以在线诊断,就是基于Java agent开发的。

热部署:btrace之类的,自动重启,也是基于agent开发的。

3、基本使用

premain打印一共加载类个数

并且对主类的test方法的执行时间进行统计

agent代码:

package com.taoge;

/**
 * Desc:
 *
 * @author taoxuefeng
 * @date 2021/6/24
 */

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;

/**
 * Created by uc on 2018/4/18.
 */
public class agentDemo {

    public static void premain(String agentArgs, Instrumentation inst) {
        System.out.println("=========premain方法执行1========");
        int length = inst.getAllLoadedClasses().length;
        System.out.println("共加载类:" + length + "个");
        inst.addTransformer(new ClassFileTransformer() {
            public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
                //过滤一下类,classname为全类名
                if (className.equals("com/taoge/Main3")) {
                    try {
                        ClassPool classPool = ClassPool.getDefault();
                        CtClass ctClass = classPool.makeClass(new ByteArrayInputStream(classfileBuffer));

                        for (CtMethod method : ctClass.getDeclaredMethods()) {
                            if (method.getName().contains("test")) {
                                // 所有方法,统计耗时;请注意,需要通过`addLocalVariable`来声明局部变量
                                method.addLocalVariable("start", CtClass.longType);
                                method.insertBefore("start = System.currentTimeMillis();");
                                String methodName = method.getLongName();
                                method.insertAfter("System.out.println(\"" + methodName + " cost: \" + (System" +
                                        ".currentTimeMillis() - start));");
                            }
                        }
                        byte[] transformed = ctClass.toBytecode();
                        return transformed;
                    } catch (IOException e) {
                        e.printStackTrace();
                    } catch (CannotCompileException e) {
                        e.printStackTrace();
                    }
                }
                return classfileBuffer;
            }
        });
    }


}

主类代码:

package com.taoge;

/**
 * Desc:
 *
 * @author taoxuefeng
 * @date 2021/6/22
 */
public class Main3 {

    public static void main(String[] args) {
        System.out.println("java agent开始执行!!");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        test();

    }

    public static void test() {
        try {
            System.out.println("运行10s");
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

核心类Instrumentation

核心方法addTransformer可以对class文件进行修改,以上例子就是对test方法统计执行时间,主要表现为在方法前(insertBefore)定义long变量,start = System.currentTimeMillis();方法末尾执行insertAfter,计算执行时间。

注意点:classname是全类名,但是分隔符是/,例如com.taoge.Main变成了com/taoge/Main

4、打包

agent代码pom文件

<build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                    <archive>
                        <manifestEntries>
                            <Premain-Class>com.taoge.agentDemo</Premain-Class>
                            <Can-Redefine-Classes>true</Can-Redefine-Classes>
                            <Can-Retransform-Classes>true</Can-Retransform-Classes>
                        </manifestEntries>
                    </archive>
                </configuration>

                <executions>
                    <execution>
                        <goals>
                            <goal>attached</goal>
                        </goals>
                        <phase>package</phase>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

Premain-Class:premain类地址

点击assembly:assembly便可生成agent文件,在target目录下

使用时在主类-javaagent:/Users/taoxuefeng/self_code/agentdemo/target/agentdemo-1.0-SNAPSHOT-jar-with-dependencies.jar

运行结果:

注意点:

对agent项目进行debug必须保证:agent项目和主项目在同一级别或者在同一个项目下。

推荐阅读博客1

推荐阅读博客2

推荐阅读博客3

推荐阅读博客4

results matching ""

    No results matching ""