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项目和主项目在同一级别或者在同一个项目下。