Search code examples
javabytecodeinstrumentationjava-bytecode-asm

Instrumenting Java classes through attach API


I am trying to instrument java class through attach API ( I want to instrument java.sql.DriverManager.getConnection() method specifically and record the returning Connection object).

So , I am doing the store and load operations and calling up a method defined in one of my class ( say , foo.TestRecordingClass.myFooRecordingMethod()) .

Now , my problem is that whenever I am trying to call the method mentioned earlier ( through attach API : agentmain is called ) , I am getting a NoClassDefFoundError.

But , the same thing instruments perfectly with premain , a.k.a Xbootclasspath approach .

Thanks in advance :-)

Edit 1 :

Ok , I am able to solve the problem using appendToBootstrapClassLoaderSearch(JarFile) method of instrumentation object.

Now , one more problem I am facing , which is loading one this object onto stack.

Theoretically , if I do aload_0 operation , that should load the current object , then I can pass it to any method , right ?

In the method visitInsn ( i.e while returning from method ) , I am doing this :

super.visitVarInsn(Opcodes.ALOAD, 0); super.visitMethodInsn(Opcodes.INVOKESTATIC, fooClassName, "fooMethodReturnInstrumentor", "(Ljava/lang/Object;)V");

This should load the object itself , but I am not able to retrieve anything.


Solution

  • If you instrument a class and insert new references to other classes these classes must be accessible by the instrumented class which includes that resolving it via its ClassLoader must succeed (compare to this answer) and it must either reside in the same package or have public access modifier. This answer contains some remarks about instrumenting core Java classes.

    So if your foo.TestRecordingClass is public and added to the bootstrap class path, it can be accessed by any instrumented class.

    The first variable contains this only if the method is not static, and not a constructor. In the latter case, the first variable contains an uninitialized this which cannot be passed to a method invocation before the super constructor has been called.

    On the other hand, this assignment is only fixed for the beginning of the method. Within the method, the variable might get reused for other purposes and even become invalid if two branch paths using it for different purposes join.

    So, at the beginning of an instance method which is not a constructor you can indeed put an aload_0 followed by an invokestatic consuming one object, to pass this to the method. Your “I am not able to retrieve anything” problem description is a bit thin. If your method is never called, this might be a simple sign that the instrumented code is never invoked. If you receive null, it is a strong indicator that the first variable does not contain this. Then, verify whether your inserted invocation is really at the beginning of an instance method.

    If you want to place the invocation at a different place you have to check that variable 0 is never overwritten before the method call.