我正在尝试ASM 4.0,但在运行代理程序时遇到了一些麻烦。我有一个应该转换字节码的类TraceTransformer
,但是出了点问题–似乎在我尝试对其进行转换时,我访问的每个类都被破坏了。
代理商
JavaTraceAgent.java:
package traceagent;
import java.lang.instrument.Instrumentation;
public class JavaTraceAgent {
private static Instrumentation instrumentation;
private static String agentId = "Uninitialized";
public static void premain(String args, Instrumentation inst) throws Exception {
instrumentation = inst;
instrumentation.addTransformer(new TraceTransformer());
}
public static void agentmain(String args, Instrumentation inst) throws Exception {
instrumentation = inst;
instrumentation.addTransformer(new TraceTransformer());
}
public static void initialize() {
if (instrumentation == null) {
JavaAgentLoader.loadAgent();
}
}
public static void logMethodInvocation() {
System.out.println("Logging invocation...");
//Thread.dumpStack();
System.out.println("logging complete!");
}
public static void setAgentId(String _agentId) {
agentId = _agentId;
}
}
TraceTransformer.java
package traceagent;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
public class TraceTransformer implements ClassFileTransformer {
@Override
public byte[] transform(ClassLoader loader, String className,
Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
if (!className.contains("superman")) return classfileBuffer;
System.out.println("TraceTransformer invoked on " + className);
ClassReader reader = new ClassReader(classfileBuffer);
ClassWriter writer = new ClassWriter(reader, 0);
ClassVisitor visitor = new ClassVisitor(Opcodes.ASM4, writer) {
@Override
public MethodVisitor visitMethod(int access, final String name, String desc, String signature, String[] exceptions) {
return new MethodVisitor(Opcodes.ASM4) {
@Override
public void visitCode() {
if (name.contains("sayHello")) {
super.visitMethodInsn(Opcodes.INVOKESTATIC, "traceagent/JavaTraceAgent", "logMethodInvocation", "()V");
System.out.println("Injecting logger on " + name);
}
super.visitCode();
}
};
}
};
reader.accept(visitor, 0);
return writer.toByteArray();
}
}
表现
build.xml:
... <manifest> <attribute name="Built-By" value="${user.name}"/> <attribute name="Class-Path" value="${manifest.classpath}"/> <attribute name="Agent-Class" value="traceagent.JavaTraceAgent" /> <attribute name="Can-Redefine-Classes" value="true" /> <attribute name="Can-Retransform-Classes" value="true" /> <attribute name="Premain-Class" value="traceagent.JavaTraceAgent" /> </manifest> ...
Target
Program.java (entry point):
package jack;
import jack.superman.Hello;
public class Program {
/**
* @param args
*/
public static void main(String[] args) {
Hello.sayHello();
}
}
Hello.java:
package jack.superman;
public class Hello {
public static void sayHello() {
System.out.println("Hello");
}
}
当前程序
...\src> javac -d . jack\Program.java
...\src> java --javagent:jack\TAgent.jar jack.Program
TraceTransformer invoked on jack/superman/Hello
Injecting logger on sayHello
Exception in thread "main" java.lang.NoSuchMethodError: jack.superman.Hello.sayHello()V
at jack.Program.main(Program.java:10)
我在这里做错了什么?如何避免“破坏”方法?
最佳答案
我测试了您的代码,但遇到了同样的错误,但是经过一些测试和阅读了文档后,它才起作用。问题出在您的MethodVisitor
中。这是代码。
ClassVisitor visitor = new ClassVisitor(Opcodes.ASM4, writer) {
@Override
public MethodVisitor visitMethod(int access, final String name, String desc, String signature, String[] exceptions) {
MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
if (!name.contains("sayHello")) return mv;
MethodVisitor mv2 = new MethodVisitor(Opcodes.ASM4, mv) {
public void visitCode() {
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "traceagent/JavaTraceAgent", "logMethodInvocation", "()V");
}
};
return mv2;
}
};
输出是
在jack / superman / Hello上调用TraceTransformer
Loggin调用...
登录完成!
你好