Introduction of Instrumentation

Java probe technology, with Instrumentation, developers can build an application-independent Agent to monitor and assist programs running on the JVM, and even replace and modify the definition of some classes without intruding on business code

Mature application

APM (Application Performance Management) tools in the Java field are almost implemented based on Instrumentation.

  • Zipkin: Twitter’s open-source distributed tracking tool, integrated with Spring Cloud Sleuth, is widely used and stable
  • Skywalking: A distributed tracking, analysis, and alarm tool developed by Huawei. It is now an open source project of Apache
  • Cat: A distributed link tracking tool developed by Dianping.

Arthas is an open source Java diagnostic tool for Alibaba based on this.

So many excellent products are using Java probe Instrumentation, are you interested in understanding it.

Using the process

Import proxy class

Execute before the main method

public class MyAgent {

    static Instrumentation instrumentation;

    public static void premain(String agentArgs, Instrumentation inst)
            throws Exception {
        try {
            instrumentation = inst;
            inst.addTransformer(new MyClassFileTransformer());
        } catch (Exception e) {
        }
    }

}
Copy the code

You need to provide one of the following two methods, if two, the first one has the higher priority

public static void premain(String agentOps, Instrumentation instrumentation);
public static void premain(String agentOps);
Copy the code

Custom ClassFileTransformer

package top.soft1010.java.myagent; import javassist.*; import javassist.expr.ExprEditor; import javassist.expr.MethodCall; import javassist.scopedpool.ScopedClassPool; import javassist.scopedpool.ScopedClassPoolRepositoryImpl; import java.io.IOException; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; import java.security.ProtectionDomain; /** * Created by bjzhangjifu on 2021/9/27. */ public class MyClassFileTransformer implements ClassFileTransformer { @Override public byte[] transform(ClassLoader loader, String className, Class<? > classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { System.out.println("Transforming " + className); // Return if (! className.startsWith("top/soft1010")) { return classfileBuffer; If (loader == null) {return classfileBuffer; if (loader == null) {return classfileBuffer; } try { // if (className ! = null && className.indexOf("/") ! = -1) { // className = className.replaceAll("/", "."); // } CtClass ctClass = ClassPool.getDefault().makeClass(new java.io.ByteArrayInputStream(classfileBuffer)); CtMethod[] methods = ctClass.getDeclaredMethods(); for (CtMethod method : methods) { try { method.insertAfter("System.out.println("--after--");" ); method.insertBefore("System.out.println("--before--");" ); } catch (CannotCompileException e) { e.printStackTrace(); } } return ctClass.toBytecode(); } catch (Exception e) { e.printStackTrace(); } return classfileBuffer; }}Copy the code

ClassFileTransformer is used to modify the class file before the JVM loads the class. It has only one transform method, which can be implemented to modify the class bytecode (using Javassist, ASM, etc.) and return the modified class bytecode, with two caveats:

  1. If this method returns NULL, we return the class without processing it. Otherwise, the original class is replaced by the byte[] we return
  2. Instrumentation#addTransformer(ClassFileTransformer)
  3. When more than one Transformer exists, the byte array returned by one Transformer call becomes the input for the next Transformer call

run

Here we’ll just write a main method class, or a JAR

public class MyTest { public static void main(String[] args) { new MyTest().test(); } private void test() { System.out.println(" ----- test ----- "); }}Copy the code

Execute the command

java -javaagent:myagent.jar top.soft1010.java.myagent.MyTest
Copy the code

The interview

1. Modify the bytecode when loading the class?

Yes, using a Java probe, JavaAgent. JavaAgent was introduced after JDK 1.5 and can also be called a Java agent. JavaAgent is an interceptor that runs before main. Its built-in method is called Premain, which means that the premain method is executed first and then main. The premain method has an Instrumentation parameter, with which we can add a custom FileTransformer. This interface has only one method, transform. With this method we can get the bytecode of the loaded class, of course we can also modify the bytecode. For example, add monitoring information before and after the methods of the specified class.Copy the code

2. Discuss the application of Java probe technology javaAgent

Skywalking: A distributed tracking, analysis and alarm tool developed by Wu Sheng (Huawei), is now a distributed link tracking tool developed by Cat: Dianping, an open source project owned by Apache. Arthas: A Java diagnostics tool that is open source for Ali and of course many large companies have their own custom APM tools based on this as wellCopy the code

The source code

The source code involved in the article