├── app ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── git │ └── hui │ └── java │ ├── AttachMain.java │ ├── BaseMain.java │ └── DemoClz.java ├── cost-agent ├── build.sh ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── git │ └── hui │ └── agent │ ├── CostAgent.java │ └── CostTransformer.java ├── pom.xml ├── readme.md └── simple-agent ├── build.sh ├── pom.xml └── src └── main ├── java └── com │ └── git │ └── hui │ └── agent │ └── SimpleAgent.java └── resources └── META-INF └── MANIFEST.MF /app/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | java-agent 7 | com.git.hui.demo 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | app 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/java/com/git/hui/java/AttachMain.java: -------------------------------------------------------------------------------- 1 | package com.git.hui.java; 2 | 3 | import com.sun.tools.attach.AgentInitializationException; 4 | import com.sun.tools.attach.AgentLoadException; 5 | import com.sun.tools.attach.AttachNotSupportedException; 6 | import com.sun.tools.attach.VirtualMachine; 7 | 8 | import java.io.IOException; 9 | 10 | /** 11 | * Created by @author yihui in 19:48 20/3/15. 12 | */ 13 | public class AttachMain { 14 | 15 | 16 | public static void main(String[] args) 17 | throws IOException, AgentLoadException, AgentInitializationException, AttachNotSupportedException { 18 | // 用jps -l 查出目标应用的进程号,替换下面的参数 19 | VirtualMachine vm = VirtualMachine.attach("36633"); 20 | // 用你自己的agent绝对地址替换 21 | vm.loadAgent( 22 | "/Users/user/Project/GitHub/study-demo/java-agent/target/java-agent-1.0-SNAPSHOT-jar-with-dependencies.jar"); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/git/hui/java/BaseMain.java: -------------------------------------------------------------------------------- 1 | package com.git.hui.java; 2 | 3 | /** 4 | * Created by @author yihui in 19:41 20/3/15. 5 | */ 6 | public class BaseMain { 7 | 8 | public int print(int i) { 9 | System.out.println("i: " + i); 10 | try { 11 | Thread.sleep(1000); 12 | } catch (InterruptedException e) { 13 | e.printStackTrace(); 14 | } 15 | 16 | if (i > 5) { 17 | // 主要是为了演示抛异常,也会执行方法耗时统计的case 18 | throw new RuntimeException(); 19 | } 20 | 21 | return i + 2; 22 | } 23 | 24 | public void run() { 25 | int i = 1; 26 | while (true) { 27 | try { 28 | i = print(i); 29 | } catch (Exception e) { 30 | e.printStackTrace(); 31 | } 32 | } 33 | } 34 | 35 | public static void main(String[] args) { 36 | BaseMain main = new BaseMain(); 37 | main.run(); 38 | // 39 | // DemoClz demoClz = new DemoClz(); 40 | // int cnt = 0; 41 | // for (int i = 0; i < 20; i++) { 42 | // if (++cnt % 2 == 0) { 43 | // i = demoClz.print(i); 44 | // } else { 45 | // i = demoClz.count(i); 46 | // } 47 | // } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/com/git/hui/java/DemoClz.java: -------------------------------------------------------------------------------- 1 | package com.git.hui.java; 2 | 3 | /** 4 | * Created by @author yihui in 22:07 20/3/15. 5 | */ 6 | public class DemoClz { 7 | 8 | public int print(int i) { 9 | System.out.println("print: " + i); 10 | try { 11 | Thread.sleep(1000); 12 | } catch (InterruptedException e) { 13 | e.printStackTrace(); 14 | } 15 | return i + 2; 16 | } 17 | 18 | public int count(int i) { 19 | System.out.println("count: " + i); 20 | try { 21 | Thread.sleep(50); 22 | } catch (InterruptedException e) { 23 | e.printStackTrace(); 24 | } 25 | return i + 1; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /cost-agent/build.sh: -------------------------------------------------------------------------------- 1 | mvn assembly:assembly 2 | -------------------------------------------------------------------------------- /cost-agent/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | java-agent 7 | com.git.hui.demo 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | cost-agent 13 | 14 | 15 | 16 | javassist 17 | javassist 18 | 3.12.1.GA 19 | 20 | 21 | 22 | 23 | 24 | 25 | org.apache.maven.plugins 26 | maven-assembly-plugin 27 | 28 | 29 | jar-with-dependencies 30 | 31 | 32 | 33 | com.git.hui.agent.CostAgent 34 | com.git.hui.agent.CostAgent 35 | true 36 | true 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | attached 45 | 46 | package 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /cost-agent/src/main/java/com/git/hui/agent/CostAgent.java: -------------------------------------------------------------------------------- 1 | package com.git.hui.agent; 2 | 3 | import java.lang.instrument.Instrumentation; 4 | 5 | /** 6 | * Created by @author yihui in 10:46 20/3/16. 7 | */ 8 | public class CostAgent { 9 | 10 | /** 11 | * jvm 参数形式启动,运行此方法 12 | * 13 | * manifest需要配置属性Premain-Class 14 | * 15 | * @param agentArgs 16 | * @param inst 17 | */ 18 | public static void premain(String agentArgs, Instrumentation inst) { 19 | System.out.println("premain"); 20 | customLogic(inst); 21 | } 22 | 23 | /** 24 | * 动态 attach 方式启动,运行此方法 25 | * 26 | * manifest需要配置属性Agent-Class 27 | * 28 | * @param agentArgs 29 | * @param inst 30 | */ 31 | public static void agentmain(String agentArgs, Instrumentation inst) { 32 | System.out.println("agentmain"); 33 | customLogic(inst); 34 | } 35 | 36 | /** 37 | * 统计方法耗时 38 | * 39 | * @param inst 40 | */ 41 | private static void customLogic(Instrumentation inst) { 42 | inst.addTransformer(new CostTransformer(), true); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /cost-agent/src/main/java/com/git/hui/agent/CostTransformer.java: -------------------------------------------------------------------------------- 1 | package com.git.hui.agent; 2 | 3 | import javassist.ClassPool; 4 | import javassist.CtClass; 5 | import javassist.CtMethod; 6 | 7 | import java.io.ByteArrayInputStream; 8 | import java.lang.instrument.ClassFileTransformer; 9 | import java.security.ProtectionDomain; 10 | 11 | /** 12 | * Created by @author yihui in 19:58 20/3/15. 13 | */ 14 | public class CostTransformer implements ClassFileTransformer { 15 | @Override 16 | public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, 17 | ProtectionDomain protectionDomain, byte[] classfileBuffer) { 18 | if (!className.startsWith("com/git/hui/java/")) { 19 | return classfileBuffer; 20 | } 21 | 22 | CtClass cl = null; 23 | try { 24 | ClassPool classPool = ClassPool.getDefault(); 25 | cl = classPool.makeClass(new ByteArrayInputStream(classfileBuffer)); 26 | 27 | for (CtMethod method : cl.getDeclaredMethods()) { 28 | // 我们统计一下所有的方法调用耗时 29 | method.addLocalVariable("start", CtClass.longType); 30 | method.insertBefore("start = System.currentTimeMillis();"); 31 | String methodName = method.getLongName(); 32 | // 请注意第二个参数,设置为true,则表示即便抛出异常了,下面的代码也会执行;相当于将它封装在finally里面了 33 | method.insertAfter("System.out.println(\"" + methodName + " cost: \" + (System" + 34 | ".currentTimeMillis() - start));", true); 35 | } 36 | 37 | byte[] transformed = cl.toBytecode(); 38 | return transformed; 39 | } catch (Exception e) { 40 | e.printStackTrace(); 41 | } 42 | return classfileBuffer; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.git.hui.demo 8 | java-agent 9 | pom 10 | 1.0-SNAPSHOT 11 | 12 | simple-agent 13 | app 14 | cost-agent 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ## java-agent 2 | 3 | 提供了一个基于idea + maven构建java agent开发测试环境的项目,其中simple-agent子工程演示了一个hello world版本的java agent项目,并提供了两种打包方式,直接执行`build.sh`即可在target下获取到可用的agent包 4 | 5 | app项目作为测试目标程序,同样提供了两种使用姿势,通过-jvm参数指定agent,以及通过attach方式挂载目标进程 6 | 7 | 更多详细内容,请参考博文: 8 | 9 | - [200303-如何优雅的在java中统计代码块耗时](https://mp.weixin.qq.com/s/3cw3WsTUG94-C9894EBXUQ) 10 | - [200316-IDEA + maven零基础构建java agent项目](https://blog.hhui.top/hexblog/2020/03/16/200316-IDEA-maven%E9%9B%B6%E5%9F%BA%E7%A1%80%E6%9E%84%E5%BB%BAjava-agent%E9%A1%B9%E7%9B%AE/) 11 | - [200316-手把手教你实现一个方法耗时统计的java agent](https://blog.hhui.top/hexblog/2020/03/16/200316-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%AE%9E%E7%8E%B0%E4%B8%80%E4%B9%AA%E6%96%B9%E6%B3%95%E8%80%97%E6%97%B6%E7%BB%9F%E8%AE%A1%E7%9A%84java-agent/) 12 | 13 | 14 | -------------------------------------------------------------------------------- /simple-agent/build.sh: -------------------------------------------------------------------------------- 1 | mvn assembly:assembly 2 | -------------------------------------------------------------------------------- /simple-agent/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | java-agent 7 | com.git.hui.demo 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | simple-agent 13 | 14 | 15 | 16 | 17 | org.apache.maven.plugins 18 | maven-assembly-plugin 19 | 20 | 21 | jar-with-dependencies 22 | 23 | 24 | 25 | src/main/resources/META-INF/MANIFEST.MF 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | attached 40 | 41 | package 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /simple-agent/src/main/java/com/git/hui/agent/SimpleAgent.java: -------------------------------------------------------------------------------- 1 | package com.git.hui.agent; 2 | 3 | import java.lang.instrument.Instrumentation; 4 | 5 | /** 6 | * Created by @author yihui in 16:39 20/3/15. 7 | */ 8 | public class SimpleAgent { 9 | 10 | /** 11 | * jvm 参数形式启动,运行此方法 12 | * 13 | * manifest需要配置属性Premain-Class 14 | * 15 | * @param agentArgs 16 | * @param inst 17 | */ 18 | public static void premain(String agentArgs, Instrumentation inst) { 19 | System.out.println("premain"); 20 | } 21 | 22 | /** 23 | * 动态 attach 方式启动,运行此方法 24 | * 25 | * manifest需要配置属性Agent-Class 26 | * 27 | * @param agentArgs 28 | * @param inst 29 | */ 30 | public static void agentmain(String agentArgs, Instrumentation inst) { 31 | System.out.println("agentmain"); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /simple-agent/src/main/resources/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Premain-Class: com.git.hui.agent.SimpleAgent 3 | Agent-Class: com.git.hui.agent.SimpleAgent 4 | Can-Redefine-Classes: true 5 | Can-Retransform-Classes: true 6 | --------------------------------------------------------------------------------