With this tutorial let us learn to instrument Java byte code using Java instrumentation. Mostly profilers, application monitoring agents, event loggers use Java instrumentation. This will serve as introductory level tutorial and once it is done, you can write a basic Java agent and do instrumentation on the Java byte code.
Key Components of Java Instrumentation
- Agent – is a jar file containing agent and transformer class files.
- Agent Class – A java class file, containing a method named ‘premain’.
- Manifest – manifest.mf file containing the “premain-class” property.
- Transformer – A Java class file implementing the interface ClassFileTransformer.
Instrumentation Agent Class
Agent class contains the premain method and that is key in Java insturmentation. This is similar to the ‘main’ method. This class is loaded by the same system classloader as it loads the other Java classes. premain method can have the following signatures,- public static void premain(String agentArgs, Instrumentation inst);
- public static void premain(String agentArgs);
- Similar to passing arguments to main method of an application, we can pass arguments to agent via agentArgs parameter. The difference is main method accepts array as parameter, but premain accepts only a String. May be we should find our own way, like send a long string and parse it inside the code to pass multiple parameters.
- If the premain method throws an exception and it goes uncaught then the JVM will abort instantly.
- instrumentation is an instance passed by the classloader using which we can register our transformers.
Instrumentation Transformer
Transformer classes are to be registered with ‘instrumentation’ instance. All those transformers that are registered with instrumentation instance will be invoked by the classloader every time a new class is loaded by that. ClassFileTransformer has got a method with following signature, which must be implemented,- public byte[] transform(ClassLoader loader, String className,
Class classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws llegalClassFormatException
Instrumentation Activity Sequence
Following diagram summarizes the activity flow in Java instrumentation,Example for Java Instrumentation
In this example, we will use Java instrumentation to modify the loaded bytecode (java classes) and add statements. Using which we will find out how much duration a method executes. This is a real time use case for java performance profiling.DurationTransformer.java
This is the Java class that will be registered with the Java instrumentation agent. This will modify the class’ bytecode by inserting new lines and the original class will be replaced by the classloader.package com.javapapers.java.instrumentation; import java.io.ByteArrayInputStream; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; import java.security.ProtectionDomain; import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod; //this class will be registered with instrumentation agent public class DurationTransformer implements ClassFileTransformer { public byte [] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte [] classfileBuffer) throws IllegalClassFormatException { byte [] byteCode = classfileBuffer; // since this transformer will be called when all the classes are // loaded by the classloader, we are restricting the instrumentation // using if block only for the Lion class if (className.equals( "com/javapapers/java/instrumentation/Lion" )) { System.out.println( "Instrumenting......" ); try { ClassPool classPool = ClassPool.getDefault(); CtClass ctClass = classPool.makeClass( new ByteArrayInputStream( classfileBuffer)); CtMethod[] methods = ctClass.getDeclaredMethods(); for (CtMethod method : methods) { method.addLocalVariable( "startTime" , CtClass.longType); method.insertBefore( "startTime = System.nanoTime();" ); method.insertAfter( "System.out.println(\"Execution Duration " + "(nano sec): \"+ (System.nanoTime() - startTime) );" ); } byteCode = ctClass.toBytecode(); ctClass.detach(); System.out.println( "Instrumentation complete." ); } catch (Throwable ex) { System.out.println( "Exception: " + ex); ex.printStackTrace(); } } return byteCode; } } |
DurationAgent.java
package com.javapapers.java.instrumentation; import java.lang.instrument.Instrumentation; public class DurationAgent { public static void premain(String agentArgs, Instrumentation inst) { System.out.println( "Executing premain........." ); inst.addTransformer( new DurationTransformer()); } } |
Lion.java
package com.javapapers.java.instrumentation; //to be instrumented java class public class Lion { public void runLion() throws InterruptedException { System.out.println( "Lion is going to run........" ); Thread.sleep(2000L); } } |
TestInstrumentation.java
package com.javapapers.java.instrumentation; public class TestInstrumentation { public static void main(String args[]) throws InterruptedException { Lion l = new Lion(); l.runLion(); } } |
Java Instrumentation Project Execution
Execution of this program is not done as usual and you have to follow the steps as below- Compile the program and prepare the Java Jar file. This is the agent jar.
- Pass the agent jar prepared for instrumentation using -javaagent argument while the execution of the program.
- You can download the Java instrumentation sample project and it is an Eclipse project.
- Build it using Eclipse and you will get the class files.
- You may run the ‘jar’ command from command line and prepare the agent jar.jar cvfm instrumentation.jar ..\meta-inf\manifest.mf . Run this from the bin folder.
- Execute the Java instrumentation project using command, java -cp .;./lib/javassist-3.14.0-GA.jar -javaagent:./instrumentation.jar com.javapapers.java.instrumentation.TestInstrumentation. Run it from the instrumentation Java project root.
Output of the Java instrumentation Project
Executing premain………
Instrumenting……
Instrumentation complete.
Lion is going to run……..
No comments:
Post a Comment