I need to see how a Java Agent modified my initial class, so I can understand what the code does.
build.gradle
configurations {
jar.archiveName = 'agent2.jar'
}
jar {
manifest {
attributes(
"Premain-Class": "com.training.agentexample.Agent",
"Can-Redefine-Classes": false,
"Can-Set-Native-Method-Prefix": false
)
}
//Fat jar with all dependencies.
from {
(configurations.runtime).collect {
it.isDirectory() ? it : zipTree(it)
}
}
}
dependencies {
compile group: 'org.javassist', name: 'javassist', version: '3.22.0-GA'
}
Main.java
public class Main {
public static void main(String[] args) {
System.currentTimeMillis();
App app = new App();
app.execute("John");
Scanner in = new Scanner(System.in);
System.out.println("Enter text to exit");
in.next(); //Wait until the user clicks to proceed.
}
}
App.java
public class App {
public void execute(String param) {
String message = "Hello World";
System.out.println(message + " " + param);
}
}
Agent.java
public class Agent {
public static void premain(final String agentArgs, final Instrumentation inst) {
System.out.println("Hey, look: I'm instrumenting a freshly started JVM!");
inst.addTransformer(new RuntimeTimerTransformer());
}
}
RuntimeTimerTransformer.java
public class RuntimeTimerTransformer implements ClassFileTransformer {
public byte[] transform(ClassLoader loader, String className, Class redefiningClass, ProtectionDomain domain, byte[] bytes) {
return transformClass(redefiningClass, bytes);
}
private byte[] transformClass(Class classToTransform, byte[] b) {
ClassPool pool = ClassPool.getDefault();
CtClass cl = null;
try {
cl = pool.makeClass(new java.io.ByteArrayInputStream(b));
CtBehavior[] methods = cl.getDeclaredBehaviors();
for (CtBehavior method : methods) {
if (!method.isEmpty()) {
changeMethod(method);
}
}
b = cl.toBytecode();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cl != null) {
cl.detach();
}
}
return b;
}
private void changeMethod(CtBehavior method) throws CannotCompileException {
String name = method.getName();
if (name.equals("execute")) {
addElapsedTime(method);
}
}
private void addElapsedTime(CtBehavior method) throws CannotCompileException {
method.addLocalVariable("$_start", CtClass.longType);
method.addLocalVariable("$_end", CtClass.longType);
method.addLocalVariable("$_total", CtClass.longType);
method.insertBefore("{ $_start = System.nanoTime();\nSystem.out.println($0);\n$1 = \"Robert\"; }");
method.insertAfter("{ $_end = System.nanoTime();\n$_total = $_end - $_start;\nSystem.out.println($_total);\nthrow new RuntimeException(\"Break!\"); }");
}
}
I debug it from Intellij Idea. I set JAVA_TOOL_OPTIONS=-javaagent:"C:\Development\agent-example\build-gradle\libs\agent2.jar"=name=agentExample
Output
Hey, look: I'm instrumenting a freshly started JVM!
Connected to the target VM, address: '127.0.0.1:64486', transport: 'socket'
Exception in thread "main" java.lang.RuntimeException: Break!
at app.App.execute(App.java:10)
at app.app.Main.main(Main.java:12)
app.app.App@3e6fa38a
Hello World !Robert
736583
Picked up JAVA_TOOL_OPTIONS: -javaagent:"C:\Development\agent-example\java-assist-agent\build\libs\agent2.jar"=name=agentExample
Disconnected from the target VM, address: '127.0.0.1:64486', transport: 'socket'
Process finished with exit code 1
I can set breakpoints. My question. How do I see real source code which is being executed? By the way, you can use this code as an example to get started with Java Agent.