I'm trying to invoke a static method whenever an object is being allocated but running into issues. I have reduced my code to smalled working example.
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);
}
}
}
Note:
Apologies if it's a very basic question. I'm a newbie to bytecode manipulation.
Error which I get:
java.lang.NoClassDefFoundError - klass: 'java/lang/NoClassDefFoundError'
I think the issue is related to the isolation provided by different ClassLoader
s. Particularly agent's code and "main" code are loaded by different Classloader
s and thus you can't call your Callback
from the "main" code. The way to work this around is to move your Callback
class to a different jar and add it to the bootstrap classpath using -Xbootclasspath/a
option.
See also similar issue at Java NoClassDefFoundError when calling own class from instrumented method
Update (filtering)
If instead of using bootstrap you want to filter out classes that can't access your Callback
, you probably should do something like this instead of just check for 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;
}