2

我正在试验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();    
}

}

显现

构建.xml

    ...
    <清单>
        <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" />
    </清单>
    ...

目标

Program.java (入口点)

package jack;

import jack.superman.Hello;

public class Program {
    /**
    * @param args
    */
    public static void main(String[] args) {
        Hello.sayHello();
    }

}

你好.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)

我在这里做错了什么?如何避免“破坏”该方法?

4

1 回答 1

2

我测试了你的代码,我得到了同样的错误,但经过一些测试和阅读文档后,我让它工作了。问题出在你MethodVisitorClassVisitor. 这是代码。

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

登录调用...

登录完成!

你好

于 2013-09-26T17:09:53.413 回答