Dynamic bytecode instrumentation - issue?

I did not see any issues when I transformed B before A on the Sun 1.6.0_15 and 1.5.0_17 JREs (I used ASM ). I would double-check the transformation code by running it externally and inspecting the resultant classes (e.g. With javap). I'd also check your classpath configuration to ensure A isn't loaded before your agent for some reason (perhaps check in your premain with getAllLoadedClasses ).

I did not see any issues when I transformed B before A on the Sun 1.6.0_15 and 1.5.0_17 JREs (I used ASM). I would double-check the transformation code by running it externally and inspecting the resultant classes (e.g. With javap). I'd also check your classpath configuration to ensure A isn't loaded before your agent for some reason (perhaps check in your premain with getAllLoadedClasses).

EDIT: If you load class A in your agent like this: Class. ForName("A"); ...then an exception is thrown: Exception in thread "main" java.lang. NoSuchMethodError: B.print()V This makes sense - A becomes a dependency of the agent and it would not make sense for the agent to instrument its own code.

You'd get an infinite loop that resulted in a stack overflow. Therefore, A is not processed by the ClassFileTransformer. For completeness, here is my test code that works without problem.

As mentioned, it depends on the ASM library. The agent: public class ClassModifierAgent implements ClassFileTransformer { public byte transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte classfileBuffer) throws IllegalClassFormatException { System.out. Println("transform: " + className); if ("A".

Equals(className)) { return new AModifier(). Modify(classfileBuffer); } if ("B". Equals(className)) { return new BModifier().

Modify(classfileBuffer); } return classfileBuffer; } /** Agent "main" equivalent */ public static void premain(String agentArguments, Instrumentation instrumentation) { instrumentation. AddTransformer(new ClassModifierAgent()); } } Method injector for A: public class AModifier extends Modifier { @Override protected ClassVisitor createVisitor(ClassVisitor cv) { return new AVisitor(cv); } private static class AVisitor extends ClassAdapter { public AVisitor(ClassVisitor cv) { super(cv); } @Override public void visitEnd() { MethodVisitor mv = cv. VisitMethod(Opcodes.

ACC_PUBLIC, "print", "()V", null, null); mv.visitCode(); mv. VisitFieldInsn(Opcodes. GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); mv.

VisitLdcInsn("X"); mv. VisitMethodInsn(Opcodes. INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V"); mv.

VisitInsn(Opcodes. RETURN); mv. VisitMaxs(2, 1); mv.visitEnd(); super.visitEnd(); } } } Method replacer for B: public class BModifier extends Modifier { @Override protected ClassVisitor createVisitor(ClassVisitor cv) { return new BVisitor(cv); } class BVisitor extends ClassAdapter { public BVisitor(ClassVisitor cv) { super(cv); } @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String exceptions) { if ("foo".

Equals(name)) { MethodVisitor mv = cv. VisitMethod(Opcodes. ACC_PUBLIC, "foo", "()V", null, null); mv.visitCode(); mv.

VisitVarInsn(Opcodes. ALOAD, 0); mv. VisitMethodInsn(Opcodes.

INVOKEVIRTUAL, "B", "print", "()V"); mv. VisitInsn(Opcodes. RETURN); mv.

VisitMaxs(1, 1); mv.visitEnd(); return new EmptyVisitor(); } else { return super. VisitMethod(access, name, desc, signature, exceptions); } } } } Common base code: public abstract class Modifier { protected abstract ClassVisitor createVisitor(ClassVisitor cv); public byte modify(byte data) { ClassReader reader = new ClassReader(data); ClassWriter writer = new ClassWriter(reader, ClassWriter. COMPUTE_FRAMES); ClassVisitor visitor = writer; visitor = new CheckClassAdapter(visitor); visitor = createVisitor(visitor); reader.

Accept(visitor, 0); return writer.toByteArray(); } } For some visible results, I added a System.out. Println('X'); to A.print(). When run on this code: public class MainInstrumented { public static void main(String args) { new B().foo(); } } ...it produces this output: transform: MainInstrumented transform: B transform: A X.

Thx for your answer. Let us view the issue from a different perspective. Assume that both classes, A and B, are empty.

Add a log at the beginning and the end of the transform method, so we can see which class is loaded by the agent and at what point in time. Execute: new B() the result should be: that class B is loaded by the agent and afterwards class A. Can you try now to load class A manually using the Classloader.loadClass() method in your agent when B passes it?

The outcome is: B was loaded through the agent, A was not! Right? Cheers christoph – Christoph Aug 6 '09 at 17:21 First of all, thanks a lot for the answer and the effort.

I appreciate that. U are right! I used Javassist for any transformations.

Javassist recompiles the changes. This results in the compilation failure mentioned above. ASM directly works on the bytecode and there is no need for such thing as recompilation.

Concerning the loading of classes within an agent: I do not understand your answer. I use eclipse and if I add in my Agent: > // if class name is B > Class. ForName("A"); and I follow the execution in the debugger, no Exception is thrown and the agent is not entered – Christoph Aug 7 '09 at 7:58 Again, you are right.

The exception you mentioned is correct. Let's make things even simpler: both classes have no methods and are not intended to be instrumented at all. The only think the agent should do is: if class B passes the agent, Class.

ForName("A"); shall be invoked. This should trigger the proper order of class loading (A first, then B). Try this example.

You will see that only B passes the agent! So the question arises why A does not pass the agent when called as part of an agent. – Christoph Aug 7 '09 at 9:19 +1 for great effort – HerdplattenToni Aug 7 '09 at 13:33.

I cant really gove you an answer,but what I can give you is a way to a solution, that is you have to find the anglde that you relate to or peaks your interest. A good paper is one that people get drawn into because it reaches them ln some way.As for me WW11 to me, I think of the holocaust and the effect it had on the survivors, their families and those who stood by and did nothing until it was too late.

Related Questions