我正在尝试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调用...
  
  登录完成!
  
  你好

10-06 13:44