java agent了解与使用

目录

1. java.lang.instrument

2. 自定义agent

3. 使用举例

4. 系统图示


通过这篇文章,可以初步了解agent。包括instrument包、如何自定义agent、使用举例。

后续的例子为,向某个class method添加log,然后恢复原代码。agent执行后的结果为:

// 如下为preAgent修改class字节码,main()内调用class某个方法的结果
=== before targetMethod method ===
from main param


// 如下为调用agent之后,调用class某个方法的结果
=== before targetMethod method ===
person age is 10
from main param101
from main param101

// 如下为调用agent恢复字节码的结果
=== before targetMethod method ===
from main param

接下来一起了解下,想做到这个效果,需要掌握哪些知识。 

1. java.lang.instrument

1.1. what

提供了用于定义'给java应用安装测量仪器的工具'的API,工具例如监听或收集应用程序的信息,还可以修改代码逻辑,达到热部署的目的。允许定义的工具去修改class文件,即向方法的字节码中插入额外的字节码,以此实现仪器的行为。

1.2. 主要类

1.2.1 java.lang.instrument.ClassDefinition

定义了需要重定义的class和新的class bytes的绑定关系,其实例作为Instrumentation.redefineClasses方法的参数,

1.2.2. java.lang.instrument.ClassFileTransformer

agent需要实现该接口。在jvm定义class之前,转换class字节码。

1.2.3. java.lang.instrument.Instrumentation

提供了一些行为,用于向java代码添加仪器。这里所谓的仪器,就是方法中添加的额外字节码。用于为工具收集信息。不需要提供实现。

每次调用agent static方法,传递一个新的Instrumentation实例,可以随时调用它的行为。

addTransformer 注册一个转换器,可通过参数设置该转换器是否支持二次转换,但前提是agent的配置也支持二次转换。
removeTransformer 注销一个转换器
isRetransformClassesSupported jvm配置是否支持class的二次转化。 要求agent JAR文件中Can-Retransform-Classes manifest attribute is set to true且jvm支持。
retransformClasses 二次转化
isRedefineClassesSupported jvm配置是否支持class的二次定义。 要求agent JAR文件中Can-Redefine-Classes manifest attribute is set to true且jvm支持。
redefineClasses 二次定义,需要提供新的class字节码
isModifiableClass class是否由 retransformation or redefinition修改过
getAllLoadedClasses jvm加载的所有class
getInitiatedClasses 由参数classLoader参与初始化的class
appendToBootstrapClassLoaderSearch 明确说明jar文件是由bootstrap classLoader定义的。
appendToSystemClassLoaderSearch 明确说明jar文件是由system classLoader定义的。
isNativeMethodPrefixSupported jvm配置是否支持setting a native method prefix。 要求agent JAR文件中Can-Set-Native-Method-Prefix manifest attribute is set to true且jvm支持。

2. 自定义agent

2.1. 自定义agent的入口

before jvm call main

  • 优先调用 public static void premain(String agentArgs, Instrumentation inst);找不到,则调用public static void premain(String agentArgs);

sometime after jvm call main

  • 优先调用 public static void agentmain(String agentArgs, Instrumentation inst);找不到,则调用public static void agentmain(String agentArgs)

需要考虑的问题:

  • 如何调用某个jvm中的afterAgent?
    • jdk ​​​tools.jar com.sun.tools.attach.VirtualMachine
  • 调用agent的参数规范
    • 指令种类
    • 关注的class
    • 关注的method
    • transformer的参数

2.2. 自定义transformer

需要考虑的问题:

  • 如何变更class字节码
    • javassist(门槛低,不需要了解class file规范。)
    • asm(门槛高,需要了解class file的规范,直接操控字节码。)
  • 使用javassist添加字节码需要注意什么?
    • insertAt lineNum是对于java文件而言
    • 不可以访问method定义的局部变量,否则编译失败;
    • 可以访问方法的入参
    • 可以访问实例的field
    • 可以自定义局部变量
  • 参数规范
  • 考虑针对哪些class和method
  • 如何恢复已转化的class

2.3. 定义manifest文件

Manifest-Version: 1.0
Main-Class: com.chl.demo.agent.AfterAgent
Premain-Class: com.chl.demo.agent.PreAgent
Agent-Class: com.chl.demo.agent.AfterAgent
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Can-Set-Native-Method-Prefix: false

2.4. jar包

需要考虑的问题:

  • 是否需要把依赖的包放入jar?
  • 如何包含manifest文件

2.5. 指令

preAgent和afterAgent均可用

  • java -javaagent:path/xxxagent.jar -jar xxxapplication.jar

仅afterAgent可用

  • java -jar xxxapplication.jar -javaagent:path/xxxagent.jar

3. 使用举例

https://github.com/chlInGithub/java_agent_demohttps://github.com/chlInGithub/java_agent_demo

本地演示步骤

run配置的application

效果为

premain has two params s
premain has two params e
main
=== before targetMethod method ===
from main param

jps找到上述应用的进程id

// 例如
jps
155696 ApplicationMain

修改字节码

AfterAgentClient main中设置上述查到的进程ID

修改agent jar包地址

run main

// 控制台输出
agentmain has two params 1#com.chl.demo.agent.target.AgentTarget#targetMethod#0@num = 2;int j = 101;param = param + 101;person = new com.chl.demo.agent.target.Person();
person.setAge(10);
if (null != person) {
System.out.println("person age is " + person.getAge());
}
com/chl/demo/agent/target/AgentTarget
classBeingRedefined is not null ? true
classfileBuffer is not null ? true
insertAt 0
finally remove ok
=== before targetMethod method ===
person age is 10
from main param101
from main param101

恢复修改

loadAgent第二个参数中1#改为2#

run main

// 控制台输出
agentmain has two params 2#com.chl.demo.agent.target.AgentTarget#targetMethod#[email protected]("=== this is from agentMain param1 ===");
com/chl/demo/agent/target/AgentTarget
RecoverTransformer classBeingRedefined is not null ? true
RecoverTransformer classfileBuffer is not null ? true
finally remove ok
=== before targetMethod method ===
from main param

4. 系统图示

java agent了解与使用_第1张图片

参考

Javassist Tutorial

java.lang.instrument (Java Platform SE 8 )

你可能感兴趣的:(陈海龙的格物之路-JAVA篇,java,agent,java,instrument)