java agent-03-Java Instrumentation 结合 bytekit 实战笔记 agent attach

news/2024/12/16 11:00:18/

java-agent-系列">java agent 系列

java agent 介绍

java agent-02-Java Instrumentation API

java agent-03-Java Instrumentation 结合 bytekit 实战笔记 agent attach

java agent-03-Java Instrumentation 结合 bytekit 实战笔记 agent premain

拓展阅读

前面几篇文档,我们简单介绍了一下 java Instrumentation。

java agent 介绍

Java Instrumentation API

本篇我们结合一下 bytekit 进行实际的文件修改。

测试代码

整体目录

    │  │  └─com│  │      └─github│  │          └─houbb│  │              └─bytekit│  │                  └─learn│  │                      └─agentattach│  │                          │  AgentAttachMain.java│  │                          │  MyAttachMain.java│  │                          │  MyClassFileTransformer.java│  │                          │  package-info.java│  │                          ││  │                          └─interceptor│  │                                  SampleInterceptor.java│  ││  └─resources│      └─META-INF│              MANIFEST.MF│└─test└─java└─com.github.houbb.bytekit.learn.agentattachSample.javaTestMain.java

MANIFEST.MF

Manifest-Version: 1.0
Agent-Class: com.github.houbb.bytekit.learn.agentattach.AgentAttachMain
Can-Redefine-Classes: true
Can-Retransform-Classes: true

AgentAttachMain-核心入口

这里指定了核心入口 AgentAttachMain

  • AgentAttachMain.java

动态 Attach 的 agent 与通过 JVM 启动 javaagent 参数指定的 agent jar 包的方式有所不同,动态 Attach 的 agent 会执行 agentmain 方法,而不是 premain 方法。

java">package com.github.houbb.bytekit.learn.agentattach;import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;public class AgentAttachMain {/*** 动态 Attach 的 agent 会执行 agentmain 方法,而不是 premain 方法。** @param agentArgs* @param inst* @throws ClassNotFoundException* @throws UnmodifiableClassException*/public static void agentmain(String agentArgs, Instrumentation inst) throws ClassNotFoundException, UnmodifiableClassException {System.out.println("agentmain called");Class classes[] = inst.getAllLoadedClasses();for (int i = 0; i < classes.length; i++) {String className = classes[i].getName();System.out.println(className);// 这里是正常的全称if (className.equals("com.github.houbb.bytekit.learn.agentattach.Sample")) {System.out.println("Reloading start: " + className);// 真实的替换final ClassFileTransformer transformer = new MyClassFileTransformer();inst.addTransformer(transformer, true);inst.retransformClasses(classes[i]);inst.removeTransformer(transformer);System.out.println("Reloading done: " + className);break;}}}}

MyClassFileTransformer 和上一篇类似,这里不过使用 bytekit 时要区分一下 install 的方式,或者会卡主。

此处不再赘述。

attach-main

创建MyAttachMain类,实现attach到目标进程 (为了方便我还是放在agent项目中)

因为是跨进程通信,Attach 的发起端是一个独立的 java 程序,这个 java 程序会调用 VirtualMachine.attach 方法开始和目标 JVM 进行跨进程通信。

下面的PID通过jps查看对应的进程ID,如11901

jarPath 为当前 agent 的完整包路径。

java">package com.github.houbb.bytekit.learn.agentattach;import com.github.houbb.bytekit.tool.utils.AttachHelper;
import com.sun.tools.attach.VirtualMachine;public class MyAttachMain {/*** 指定 pid 进行 attch** @param args* @throws Exception*/public static void main(String[] args) throws Exception {String pid = "15708";String jarPath = "D:\\github\\bytekit-learn\\bytekit-learn-agentattach\\target\\bytekit-learn-agentattach-1.0-SNAPSHOT.jar";AttachHelper.attach(jarPath, pid);// 通过 jps 查看VirtualMachine vm = VirtualMachine.attach(pid);try {vm.loadAgent(jarPath);} finally {vm.detach();}}}

测试

启动测试类

为了演示 attach,我们提供一个一直循环的测试类:

java">public class TestMain {public static void main(String[] args) throws InterruptedException {while (true) {Sample sample = new Sample();String result = sample.hello("123", false);System.out.println(result);TimeUnit.SECONDS.sleep(3);}}}

首先启动测试类。通过 jps 获取对应的信息

14028 RemoteMavenServer36
15100 Launcher
15708 TestMain

编译 agent

通过 mvn clean install 编译我们的 agent 包,生成在路径:

D:\\github\\bytekit-learn\\bytekit-learn-agentattach\\target\\bytekit-learn-agentattach-1.0-SNAPSHOT.jar

修改 attach-main 并启动

直接把 MyAttachMain 的 pid + agent 路径修改为对应的。启动测试:

对应的日志如下,实现已经被替换了

com.github.houbb.bytekit.learn.agentattach.Sample
Reloading start: com.github.houbb.bytekit.learn.agentattach.Sample
start transform name=== com/github/houbb/bytekit/learn/agentattach/Sample
/** Decompiled with CFR.*/
package com.github.houbb.bytekit.learn.agentattach;public class Sample {private int exceptionCount = 0;/** WARNING - void declaration*/public String hello(String string, boolean bl) {try {String string2;void str;void exception;try {String string3 = "(Ljava/lang/String;Z)Ljava/lang/String;";String string4 = "hello";Object[] objectArray = new Object[]{string, new Boolean(bl)};Class<Sample> clazz = Sample.class;Sample sample = this;System.out.println("atEnter, args[0]: " + objectArray[0]);}catch (RuntimeException runtimeException) {Class<Sample> clazz = Sample.class;RuntimeException runtimeException2 = runtimeException;System.out.println("exception handler: " + clazz);runtimeException2.printStackTrace();}if (exception != false) {++this.exceptionCount;throw new RuntimeException("test exception, str: " + (String)str);}String string5 = string2 = "hello " + (String)str;System.out.println("atExit, returnObject: " + string5);return string2;}catch (RuntimeException runtimeException) {int n = this.exceptionCount;RuntimeException runtimeException3 = runtimeException;System.out.println("atExceptionExit, ex: " + runtimeException3.getMessage() + ", field exceptionCount: " + n);throw runtimeException;}}
}end transform name=== com/github/houbb/bytekit/learn/agentattach/Sample
Reloading done: com.github.houbb.bytekit.learn.agentattach.Sample
atEnter, args[0]: 123
atExit, returnObject: hello 123
hello 123
atEnter, args[0]: 123
atExit, returnObject: hello 123
hello 123
atEnter, args[0]: 123
atExit, returnObject: hello 123
hello 123
atEnter, args[0]: 123
atExit, returnObject: hello 123
hello 123

拓展阅读

VirtualMachine 类不存在

添加jdk tools.jar解决com.sun.tools.attach.VirtualMachine 类找不到的问题

发现配置了 java_home 及相关信息还是不行,可以手动在项目中引入。

idea 就是 libs 种添加依赖。

参考资料

https://blog.51cto.com/zhangxueliang/5667216

https://www.cnblogs.com/756623607-zhang/p/12575509.html


http://www.ppmy.cn/news/1555566.html

相关文章

⭐Redis - 手动实现分布式锁 Redisson 的使用

概述 定义&#xff1a;分布式系统或集群模式下&#xff0c;多进程或多节点之间 “可见” 并且 “互斥” 的锁机制 功能&#xff1a;确保同一时刻只有一个进程或节点能够获取某项资源的访问权 特点 互斥高可用多进程可见高并发 (高性能)安全性 (避免死锁问题) 常见的分布式锁 …

【软件工程】第八章·单元/集成测试程序

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;软件开发必练内功_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 1. 前…

vue2-请求代理,动态target

当你在 Vue 2 项目中将 axios 的 baseURL 配置为 http://192.168.11.111:8762 时&#xff0c;所有请求都被认为是绝对路径请求&#xff0c;这种请求会直接发送到目标服务器&#xff0c; 跳过开发服务器的代理。 baseURL具体值 这就是为什么代理配置无法拦截 /exportPdf 的原因…

商协会管理系统:沃德商协会管理系统微信小程序公众号

智慧化会员体系 在线入会、会费缴纳、到期提醒、会员管理、消息群发、线上证书、会员通讯录、有效供需匹配等。 智敏化内容运营活动接龙&#xff0c;问卷调查&#xff0c;党建新闻资讯发布&#xff0c;多方位满足会员内容信息运营。 智能化活动构建为商会提供多种活动营解决…

【网络】五种IO模型多路转接select/poll/epollReactor反应堆模式

主页&#xff1a;醋溜马桶圈-CSDN博客 专栏&#xff1a;计算机网络原理_醋溜马桶圈的博客-CSDN博客 gitee&#xff1a;mnxcc (mnxcc) - Gitee.com 目录 1.五种 IO 模型 1.1 阻塞 IO 1.2 非阻塞 IO 1.3 信号驱动 IO 1.4 IO 多路转接 1.5 异步 IO 2.高级 IO 重要概念 2.1 …

Redis缓存应用场景【Redis场景上篇】

文章目录 1.缓存基础2.缓存异步场景1.缓存穿透2.缓存击穿3.缓存雪崩总结 3.缓存一致性 1.缓存基础 Redis由于性能高效&#xff0c;通常可以做数据库存储的缓存。一般而言&#xff0c;缓存分为服务端缓存和客户端缓存。缓存有以下三种模式&#xff1a; Cache Aside&#xff08…

利用PHP和phpSpider实现网站搜索功能的数据采集

利用PHP和phpSpider实现网站搜索功能的数据采集&#xff0c;可以分为以下几个步骤&#xff1a; 1. 环境准备 安装PHP&#xff1a;确保你的开发环境中已经安装了PHP。 安装Composer&#xff1a;Composer是PHP的依赖管理工具&#xff0c;用于安装和管理PHP包。 安装phpSpider&…

conda学习

参考: Anaconda 官网教程 https://freelearning.anaconda.cloud/get-started-with-anaconda/18202conda配置虚拟环境/conda环境迁移/python环境迁移 https://blog.csdn.net/qq_43369406/article/details/127140839 环境&#xff1a; macOS 15.2Anaconda Navigator 2.4.2 x.1…