Jabis
Jabis

Reputation: 41

Java agent : transform() not applied on all classes

I have been trying to use a Java agent to apply a bytecode transformation with ASM.

I implemented an Agent with the premain method adding a transformer to the Instrumentation. I added the "Premain-Class" line in the .jar manifest

Premain-Class: <MyAgentPath>

Then I tried to run the application with the agent.

There I have a problem : my transformer modifies some method calls, so if not all involved classes are modified too, it cannot work. And there are some classes which are not modified, like "org.apache.commons.math3.util.FastMath". Of course then, I got the error :

java.lang.NoSuchMethodError: org.apache.commons.math3.util.FastMath.floor<new_descriptor>

I checked a lot of posts saying it could be the bootstrap loader which does not know the path to this class so I tried to add it using different ways :

None of this changed anything.

I also used the Instrumentation class method getAllLoadedClasses() to see which ones were loaded, and all classes involved in the application process where loaded, including FastMath.

for(Class<?> clazz : MyAgent.getInstInstance().getAllLoadedClasses()){
    buffWrite.write(clazz.getName());

As the class "FastMath" gave an error and as the Bootstrap Loader should have its path, I tried adding some method calls to methods from other classes in the same package. It appears the problem does not show for every class of the package.

For example: MathUtils is transformed and a call to the modified method checkFinite(D)V -> checkFinite<new_descriptor>.

So I guess the problem has nothing to do with the paths given to the bootstrap loader.

If you have some ideas about what is happening, I would be glad to hear about it!

Upvotes: 4

Views: 1275

Answers (1)

Rafael Winterhalter
Rafael Winterhalter

Reputation: 44042

A NoSuchMethodError is most likely not caused by not adding something to the bootstrap class loader. The only chance that this could be a problem would be if there were suddenly two such jars available where one was instrumented and the other was not.

If you call change a method checkFinite(D)V to become another method checkFinite<new_descriptor>, then you need to make sure that any class using FastMath.floor updates the descriptor to this method. This means that you need to walk through every method of every class looking for visitMethodIns calls of ASM. It seems like you are missing some. Since you are changing the layout of the FastMath class, you must instrument it while loading it for the first time and you cannot redefine it.

The Java internal classes do not know of FastMath as this is a third-party dependency. Therefore, it should be possible to instrument any call from your agent. It seems to me like you are loading FastMath prematurely.

Upvotes: 2

Related Questions