通过检测字节代码调用类的静态方法时的NoClassDefFoundError

时间:2017-03-07 23:42:51

标签: java java-bytecode-asm

我试图在分配对象但遇到问题时调用静态方法。我已将代码缩减为较小的工作示例。

MemorySnifferAgent.java

public class MemorySnifferAgent {
    public static void premain(String agentArguments, Instrumentation instrumentation) {
        Callback.main(); //Make sure the class is loaded ?
        instrumentation.addTransformer(new MemoryAllocationTransformer());
    }
}

MemoryAllocationTransformer.java

public class MemoryAllocationTransformer implements ClassFileTransformer {
    public byte[] instrumentByteCode(byte[] bytecode) {
        ClassReader reader = new ClassReader(bytecode);
        ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
        CustomClassReader customVisitor = new CustomClassReader(Opcodes.ASM4, writer);
        reader.accept(customVisitor, 0);
        return writer.toByteArray();
    }

    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
        byte[] bytes = instrumentByteCode(classfileBuffer);
        return  bytes;
    }
}

CustomClassReader.java

public class CustomClassReader extends ClassVisitor {
    public CustomClassReader(int api, ClassWriter classWriter) {
        super(api, classWriter);
    }

    public MethodVisitor visitMethod(final int access, final String name,
                                     final String desc, final String signature, final String[] exceptions) {
        MethodVisitor methodWritter = super.visitMethod(access, name, desc, signature, exceptions);
        return new CustomMethodWritter(api, name, methodWritter);
    }
}

CustomMethodWritter.java

class CustomMethodWritter extends MethodVisitor {
    String name;

    public CustomMethodWritter(int i, String name, MethodVisitor methodWriter) {
        super(i, methodWriter);
        this.name = name;
    }

    @Override
    public void visitTypeInsn(int opcode, String type) {
        super.visitTypeInsn(opcode, type);
        if (opcode == Opcodes.NEW) {
            super.visitMethodInsn(Opcodes.INVOKESTATIC, "com/chasingnanos/Callback", "onAllocation", "()V", false);
        }
    }
}

注意:

  • 我意识到,点击所有分配需要点击所有NEW *操作码和反射API
  • super.visitMethodInsn(Opcodes.INVOKESTATIC,&#34; com / chasingnanos / Callback&#34;,&#34; onAllocation&#34;,&#34;()V&#34;,false);似乎是问题所在。虽然我无法找到问题所在。

道歉,如果这是一个非常基本的问题。我是字节码操作的新手。

我得到的错误:

java.lang.NoClassDefFoundError   - klass:&#39; java / lang / NoClassDefFoundError&#39;

1 个答案:

答案 0 :(得分:2)

我认为这个问题与不同ClassLoader提供的隔离有关。特别是代理商的代码和&#34;主要&#34;代码由不同的Classloader加载,因此您无法从&#34; main&#34;中调用Callback。码。解决这个问题的方法是将Callback类移动到另一个jar并使用-Xbootclasspath/a选项将其添加到引导类路径中。

另见类似问题 Java NoClassDefFoundError when calling own class from instrumented method

更新(过滤)

如果您想要过滤掉无法访问Callback的类而不是使用引导程序,那么您可能应该执行类似这样的操作,而不仅仅是检查null

public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
    // check if the class inside the loader can access Callback
    // by analyzing its hierarchy of parent class loaders
    for (ClassLoader curLoader = loader; ; curLoader = curLoader.getParent()) {
        if (curLoader == null) {
            System.out.println("Skip '" + className + "' for " + loader);
            return null;
        } else if (curLoader == Callback.class.getClassLoader())
            break;
    }
    byte[] bytes = instrumentByteCode(classfileBuffer);
    return bytes;
}