java.lang.Instrument agent Agent use details

  • 2020-05-19 04:48:06
  • OfStack

java.lang.Instrument agent Agent use

The java.lang.Instrument package was introduced in JDK5, where the programmer dynamically modifies the class code by modifying the bytecode of the method. This is usually preprocessed before the main method call of the class, by java specifying the proxy class of the class. The transform method of ClassFileTransformer is called before the class's bytecode is loaded into JVM, so as to modify the method of the original class and implement AOP. The advantage of this is that it will not generate a new class as dynamic proxy or CGLIB technology implements AOP, and it does not need the interface of the original class.

(1) an agent (agent) is an interceptor (interceptor) that executes agent code before your main method, i.e. before the main method is executed. The code for agent runs in the same JVM as your main method, is loaded with the same system classloader, and is managed by the same security policy (security policy) and context (context). The name of the agent (agent) is a bit misleading, as it is not quite the same as the agent we understand. java agent is easy to use. How to write 1 java agent? You just need to implement the premain method: public static void premain(String agentArgs, Instrumentation inst). If you can't find the premain definition above, JDK 6 will try calling the premain definition below: public static void premain(String agentArgs)

(2) the Agent class must be typed into the jar package, and then the META-INF/MAINIFEST.MF inside must contain the property Premain-Class. Here is an example of MANIFEST.MF:

Manifest-Version: 1.0 Premain-Class:MyAgent1 Created-By:1.6.0_06

Then add MANIFEST.MF to your jar package. Here is the Manifest Attributes listing of the agent jar file: Premain-Class if JVM starts with a proxy specified, then this property specifies the proxy class, the class that contains the premain method. This property is required if the agent is specified when JVM is started. If this property does not exist, JVM is aborted. Note: this property is the class name, not the file name or path. This property specifies the proxy class if the implementation supports a mechanism to start the proxy at some point after VM is started. That is, the class that contains the agentmain method. This property is required, and if it does not exist, the agent will not start. Note: this is the class name, not the file name or path. Boot-Class-Path sets the list of paths to boot the classloader search. The path represents a directory or library (often referred to on many platforms as JAR or zip libraries). When the platform-specific mechanism for finding a class fails, the bootstrap classloader searches for these paths. Search the path in the order listed. The path in the list is separated by one or more Spaces. The path USES the hierarchical URI's path component syntax. If the path begins with a slash character ("/"), it is an absolute path, otherwise it is a relative path. The relative path is resolved according to the absolute path of the agent JAR file. Ignore incorrectly formatted paths and paths that do not exist. If the agent is started at some point after VM is started, the path that does not represent the JAR file is ignored. This property is optional. Can-Redefine-Classes Boolean value (true or false, case independent). Can you redefine the classes required for this proxy? Values other than true are treated as false. This property is optional and has a default value of false. The Boolean value of Can-Retransform-Classes (true or false, case independent). Whether the classes required by this proxy can be reconverted. Values other than true are treated as false. This property is optional and has a default value of false. Can-Set-Native-Method-Prefix Boolean value (true or false, case independent). Can you set the native method prefix required by this agent? Values other than true are treated as false. This property is optional and the default value is false.

(3) all these Agent jar packages will be automatically added to the classpath of the program. So you don't need to manually add them to classpath. Unless you want to specify the order classpath.

(4) there is no limit to the number of parameters of -javaagent in one java program, so any number of java agent can be added. All java agent will be executed in the order you define. Such as:

java -javaagent:MyAgent1.jar -javaagent:MyAgent2.jar -jar MyProgram.jar

Let's say that the main function in MyProgram.jar is in MyProgram. MyAgent1.jar, MyAgent2.jar, jar, MyAgent1, MyAgent2, jar, jar, premain

MyAgent1.premain - > MyAgent2.premain - > MyProgram.main

(5) in addition, premain after main function will not be executed, for example:

java -javaagent:MyAgent1.jar -jar MyProgram.jar -javaagent:MyAgent2.jar

MyAgent2 is placed after MyProgram.jar, so premain of MyAgent2 will not be executed, so the result will be:

MyAgent1.premain - > MyProgram.main

(6) each java agent can receive one parameter of string type, that is, agentArgs in premain, which is defined by java option. Such as:

java -javaagent:MyAgent2.jar=thisIsAgentArgs -jar MyProgram.jar

The value of agentArgs received by premain in MyAgent2 will be "thisIsAgentArgs" (not including the double quotes).

(7) Instrumentation in parameter: through Instrumentation inst in parameter, add your own ClassFileTransformer to change class file. The custom Transformer implementation here implements the transform method, which provides modifications to the bytecode of the actual class to be executed, even to the point of executing another class method. For example, write agent class:


package org.toy;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.ClassFileTransformer;
public class PerfMonAgent {
  private static Instrumentation inst = null;
  /**
   * This method is called before the application's main-method is called,
   * when this agent is specified to the Java VM.
   **/
  public static void premain(String agentArgs, Instrumentation _inst) {
    System.out.println("PerfMonAgent.premain() was called.");
    // Initialize the static variables we use to track information.
    inst = _inst;
    // Set up the class-file transformer.
    ClassFileTransformer trans = new PerfMonXformer();
    System.out.println("Adding a PerfMonXformer instance to the JVM.");
    inst.addTransformer(trans);
  }
}

Write ClassFileTransformer class:



package org.toy;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtBehavior;
import javassist.CtClass;
import javassist.NotFoundException;
import javassist.expr.ExprEditor;
import javassist.expr.MethodCall;
public class PerfMonXformer implements ClassFileTransformer {
  public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
    byte[] transformed = null;
    System.out.println("Transforming " + className);
    ClassPool pool = ClassPool.getDefault();
    CtClass cl = null;
    try {
      cl = pool.makeClass(new java.io.ByteArrayInputStream(
          classfileBuffer));
      if (cl.isInterface() == false) {
        CtBehavior[] methods = cl.getDeclaredBehaviors();
        for (int i = 0; i < methods.length; i++) {
          if (methods[i].isEmpty() == false) {
            doMethod(methods[i]);
          }
        }
        transformed = cl.toBytecode();
      }
    } catch (Exception e) {
      System.err.println("Could not instrument " + className
          + ", exception : " + e.getMessage());
    } finally {
      if (cl != null) {
        cl.detach();
      }
    }
    return transformed;
  }
  private void doMethod(CtBehavior method) throws NotFoundException,
      CannotCompileException {
    // method.insertBefore("long stime = System.nanoTime();");
    // method.insertAfter("System.out.println(\"leave "+method.getName()+" and time:\"+(System.nanoTime()-stime));");
    method.instrument(new ExprEditor() {
      public void edit(MethodCall m) throws CannotCompileException {
        m.replace("{ long stime = System.nanoTime(); $_ = $proceed($$); System.out.println(\""
                + m.getClassName()+"."+m.getMethodName()
                + ":\"+(System.nanoTime()-stime));}");
      }
    });
  }
}

The above two classes are the core of agent. jvm starts up and calls PerfMonAgent.premain before the application is loaded. Then PerfMonAgent.premain instantiates a custom ClassFileTransforme, PerfMonXformer, through inst.addTransformer (trans). Add PerfMonXformer instance to Instrumentation instance (from jvm into), and this makes the application of class loading, PerfMonXformer. transform will be called, in this way you can change the class loading, really a little magic, in order to change the class bytecode, I use the jboss javassist, although you don't so much as to use, but jboss javassist is really strong, and allows you to easily change the bytecode class.

In the above method, I added long stime = System.nanoTime () to the method entry of each class by changing the bytecode of the class. , System.out.println (" methodClassName.methodName: "+(System.nanoTime () -stime));

Thank you for reading, I hope to help you, thank you for your support of this site!


Related articles: