Java Instrumentation

Last modified on August 1st, 2014 by Joe.

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.

instrumentation

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

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,

  1. public static void premain(String agentArgs, Instrumentation inst);
  2. public static void premain(String agentArgs);

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.

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,

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.

Instrumentation Activity Sequence

Following diagram summarizes the activity flow in Java instrumentation,

Java Instrumentation Activity Flow

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

  1. Compile the program and prepare the Java Jar file. This is the agent jar.
  2. Pass the agent jar prepared for instrumentation using -javaagent argument while the execution of the program.

Output of the Java instrumentation Project

Java Instrumentation Sample Project: compile and run


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

Download Example Project Source Code:JavaInstrumentation

Comments on "Java Instrumentation"

  1. Patrick says:

    This is a difficult concept to understand. You have made it amazingly simple and easy. Thanks.

  2. sudha says:

    Nice article. thanks!

  3. sridhar says:

    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

  4. Karthik Prabakaran says:

    Thanks for your article on this topic Joe.

  5. Joe says:

    Thanks Patrick.

  6. Joe says:

    Welcome Karthik.

  7. Mahdi says:

    Prefect really !

  8. damith says:

    Great work!!

  9. Ali says:

    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.

  10. sanjeev says:

    Very well explained, indeed.

  11. Binh Nguyen Thanh says:

    Thanks, nice post!

  12. Rumi Ismail says:

    Thanks for the simple and detailed presentation. I learnt something today. Keep up the good work. Cheers!

  13. Bhanuprakash says:

    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

  14. Anonymous says:

    Thank you very much sir, I was struggling hard to do it.. very nice and simple explanation

  15. Prakash says:

    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?

  16. suganya says:

    Hi Joe,

    Can you please give the realtime scenario where Java Instrumentation is used?

  17. Manoj says:

    Great and Simple explanation. I have not yet tried the code but at least with this article I understand the basic concepts. Thanks.

  18. Manjunath says:

    Fantastic explanation! Thanks.

Comments are closed for "Java Instrumentation".