问题
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.
回答1:
This is not an answer I was looking for, but this is how close I could get. I still need a better solution, especially what if I cannot access agent code. Your ideas a welcome!
1) Modify transform
method.
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();
OutputStream os = new FileOutputStream(CLASS_OUTPUT_DIR + cl.getName() + CLASS_EXTENSION);
BufferedOutputStream bos = new BufferedOutputStream(os);
bos.write(b);
bos.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cl != null) {
cl.detach();
}
}
return b;
}
Update
See update bellow. I used Intellij Idea to open bytecode classes.2) Then open the bytecode. I used jd-gui-1.4.0.jar. This is what I got.
Compiled App.class
public class App
{
/* Error */
public void execute(String param)
{
// Byte code:
// 0: invokestatic 55 java/lang/System:nanoTime ()J
// 3: lstore 4
// 5: getstatic 4 java/lang/System:out Ljava/io/PrintStream;
// 8: aload_0
// 9: invokevirtual 58 java/io/PrintStream:println (Ljava/lang/Object;)V
// 12: ldc 60
// 14: astore_1
// 15: ldc 2
// 17: astore_2
// 18: ldc 3
// 20: astore_3
// 21: getstatic 4 java/lang/System:out Ljava/io/PrintStream;
// 24: aload_2
// 25: invokevirtual 5 java/io/PrintStream:println (Ljava/lang/String;)V
// 28: getstatic 4 java/lang/System:out Ljava/io/PrintStream;
// 31: new 6 java/lang/StringBuilder
// 34: dup
// 35: invokespecial 7 java/lang/StringBuilder:<init> ()V
// 38: aload_2
// 39: invokevirtual 8 java/lang/StringBuilder:append (Ljava/lang/String;)Ljava/lang/StringBuilder;
// 42: aload_3
// 43: invokevirtual 8 java/lang/StringBuilder:append (Ljava/lang/String;)Ljava/lang/StringBuilder;
// 46: aload_1
// 47: invokevirtual 8 java/lang/StringBuilder:append (Ljava/lang/String;)Ljava/lang/StringBuilder;
// 50: invokevirtual 9 java/lang/StringBuilder:toString ()Ljava/lang/String;
// 53: invokevirtual 5 java/io/PrintStream:println (Ljava/lang/String;)V
// 56: goto +3 -> 59
// 59: aconst_null
// 60: astore 11
// 62: invokestatic 55 java/lang/System:nanoTime ()J
// 65: lstore 6
// 67: lload 6
// 69: lload 4
// 71: lsub
// 72: lstore 8
// 74: getstatic 4 java/lang/System:out Ljava/io/PrintStream;
// 77: lload 8
// 79: invokevirtual 63 java/io/PrintStream:println (J)V
// 82: new 65 java/lang/RuntimeException
// 85: dup
// 86: ldc 67
// 88: invokespecial 69 java/lang/RuntimeException:<init> (Ljava/lang/String;)V
// 91: athrow
// 92: athrow
// Line number table:
// Java source line #6 -> byte code offset #15
// Java source line #7 -> byte code offset #18
// Java source line #8 -> byte code offset #21
// Java source line #9 -> byte code offset #28
// Java source line #10 -> byte code offset #56
// Local variable table:
// start length slot name signature
// 0 59 0 this App
// 0 59 1 param String
// 18 41 2 message String
// 21 38 3 appender String
// 0 59 4 $_start long
// 0 59 6 $_end long
// 0 59 8 $_total long
}
}
And in Idea
来源:https://stackoverflow.com/questions/50487601/look-at-the-source-code-of-a-java-class-modified-by-a-java-agent