Java Instrumentation
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.
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
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
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
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
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……..
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
0 comments:
Post a Comment