Java Instrumentation will give a demonstration of how powerful Java is. Most importantly, this power can be realized by a developer for innovative means. For example using Java instrumentation, we can access a class that is loaded by the Java classloader from the JVM and modify its bytecode by inserting our custom code, all these done at runtime. Don’t worry about security, these are governed by the same security context applicable for Java classes and respective classloaders.
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.
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,
In general, System classloader after loading the Agent Class, then invokes the premain (premain is roughly equal to main method for normal Java classes) method in the above given order. Either 1 or 2 must be present in the class. Initiating the tool’s agent in Java instrumentation is JVM implementation dependent, so you got to read the implementation specs. premain method runs under the same security context as the application main method.
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,
When the transformer is invoked by the classloader, it will pass all these arguments, on which class loading this is invoked, etc and the bytecode of the class being loaded. So that’s our entry ticket to manipulating the class loaded. We can take a copy of the classfileBuffer and make modifications to the copy and return it. So the returned bytes are loaded by the classloader which will include the modifications we have done.
Following diagram summarizes the activity flow in 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.
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; } }
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()); } }
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); } }
package com.javapapers.java.instrumentation; public class TestInstrumentation { public static void main(String args[]) throws InterruptedException { Lion l = new Lion(); l.runLion(); } }
Execution of this program is not done as usual and you have to follow the steps as below
Executing premain………
Instrumenting……
Instrumentation complete.
Lion is going to run……..
Execution Duration (nano sec): 2000755165
The line ‘Execution Duration’ is what is inserted by the Java instrumentation and this line is not present in the original Java code
Comments are closed for "Java Instrumentation".
This is a difficult concept to understand. You have made it amazingly simple and easy. Thanks.
Nice article. thanks!
Joy,
As usual your explanation is awesome. Long back i did code coverage using EMMA which is depending on this concept. Now i understood this concept very well.
Thank you verymuch
Thanks for your article on this topic Joe.
Thanks Patrick.
Welcome Karthik.
Prefect really !
Great work!!
Excellent! presentation in its simplest representation.Just a request if you could start a series for profiling in java w.r.t to performance bottleneck.
Very well explained, indeed.
Thanks, nice post!
Thanks for the simple and detailed presentation. I learnt something today. Keep up the good work. Cheers!
I understood the concept really thanks for that and I tried to execute the code but it is not loading the class which has premain method , I am get error as ClassNotFoundException can you please help me out here I am pasting the same for your ref.
$ java -javaagent:tracer.jar premainclassloader/TestInstrumentation
java.lang.ClassNotFoundException: DurationAgent
at java.net.URLClassLoader$1.run(Unknown Source)
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(Unknown Source)
at sun.instrument.InstrumentationImpl.loadClassAndCallPremain(Unknown Source)
FATAL ERROR in native method: processing of -javaagent failed
Exception in thread “main”
$ java -cp ../../javassist.jar/javassist.jar -javaagent:tracer.jar premainclassloader.TestInstrumentation
java.lang.ClassNotFoundException: DurationAgent
at java.net.URLClassLoader$1.run(Unknown Source)
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(Unknown Source)
at sun.instrument.InstrumentationImpl.loadClassAndCallPremain(Unknown Source)
FATAL ERROR in native method: processing of -javaagent failed
Exception in thread “main”
Thanks
Bhanuprakash
Thank you very much sir, I was struggling hard to do it.. very nice and simple explanation
Hi ,This article is really useful and gives clear explanation of what exactly byte code is.Can you please help me running the sameple application(javainstrumentation) in eclipse.I have imported the project but what should i do next could you please reply?
Hi Joe,
Can you please give the realtime scenario where Java Instrumentation is used?
Great and Simple explanation. I have not yet tried the code but at least with this article I understand the basic concepts. Thanks.
Fantastic explanation! Thanks.