美文网首页
java instrumentation attach模式快速入

java instrumentation attach模式快速入

作者: 青_雉 | 来源:发表于2020-12-16 22:24 被阅读0次

本文旨在简单粗暴体验instrumentation attach模式的玩法,给读者一个直观的体验,概念方面不多介绍

场景

有一个spring的http接口定义如下,每次调用返回一个随机uuid,此处的RandomUtil采用的hutool的工具类。

@GetMapping("/play")
@ResponseBody
public String health() {
  return RandomUtil.simpleUUID();
}

期望通过编写一个agent,attach到当前进程实现串改程序逻辑,每次调用都返回hello

开发一个agent jar

入口函数

先要写一个agentmain函数,类似我们写helloword的main函数

public static void agentmain(String agentArgs, Instrumentation inst)  throws ClassNotFoundException, UnmodifiableClassException, InterruptedException {
  //注册字节码转换逻辑
  inst.addTransformer(new PlayClassFileTransformer(), true);
  //使之生效
  inst.retransformClasses(RandomUtil.class);
  System.out.println("Agent Main Done");
}

字节码转换逻辑

函数内部注册了PlayClassFileTransformer, 内部实现逻辑:

  • 如果不是RandomUtil,则返回null,表示不作替换
  • 否则替换新的字节码内容,字节码内容来自本地提前准备好的一个class文件
public class PlayClassFileTransformer implements ClassFileTransformer {

    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
        ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
        if (!"cn/hutool/core/util/RandomUtil".equals(className)){
            return null;
        }

        return getBytesFromFile("/Users/***/code/***/instrument-play/docs/RandomUtil.class");
    }

    public static byte[] getBytesFromFile(String fileName) {
        try {
            // precondition
            File file = new File(fileName);
            InputStream is = new FileInputStream(file);
            long length = file.length();
            byte[] bytes = new byte[(int) length];

            // Read in the bytes
            int offset = 0;
            int numRead = 0;
            while (offset <bytes.length
                && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
                offset += numRead;
            }

            if (offset < bytes.length) {
                throw new IOException("Could not completely read file "
                    + file.getName());
            }
            is.close();
            return bytes;
        } catch (Exception e) {
            System.out.println("error occurs in _ClassTransformer!"
                + e.getClass().getName());
            return null;
        }
    }
}

注意: 此处字节码是我提前准备好的,基于hutool的源码随便改了一笔,把simpleUUID函数的返回值改为了hello。

retransformClasses

注册完转换器后,替换逻辑执行的时机需要依赖于此,所以需要手动执行这个函数,否则替换逻辑是不生效的。以下引用一小段ClassFileTransformer的注释:

the transformer will be called for every new class definition and every class redefinition.

另外,此处因为需要指定需要retransform的类型,所以agent的工程里也引入了对hutool的依赖:

<dependency>
  <groupId>cn.hutool</groupId>
  <artifactId>hutool-all</artifactId>
  <version>4.1.3</version>
  <scope>provided</scope>
</dependency>

manifest文件

jar包生成后需要manifest文件,所以添加如下maven配置:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-assembly-plugin</artifactId>
  <configuration>
    <descriptorRefs>
      <descriptorRef>jar-with-dependencies</descriptorRef>
    </descriptorRefs>
    <archive>
      <manifestEntries>
        <Agent-Class>org.example.instrument.AgentMain</Agent-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>

打包

mvn clean package

ATTACH

接下来需要把agent jar attach到目标进程上去。

此处我们假设目标进程,已启动,进程号为8888,则attach的代码如下:

public static void main(String[] args) throws InterruptedException, IOException, AgentLoadException, AgentInitializationException, AttachNotSupportedException {
    VirtualMachine vmObj = null;
    try {
        vmObj = VirtualMachine.attach("8888");
        if (vmObj != null) {
            vmObj.loadAgent("<jar path>/instrument-play-1.0-SNAPSHOT-jar-with-dependencies.jar", null);
        }
    } finally {
        if (null != vmObj) {
            vmObj.detach();
        }
    }
}

效果体验

完整源码

查看

相关文章

网友评论

      本文标题:java instrumentation attach模式快速入

      本文链接:https://www.haomeiwen.com/subject/jtoogktx.html