Using Class.forName() in Java Instrumentation Agent

一笑奈何 提交于 2019-12-13 03:58:16

问题


What I understand is that if I use:

Instrumentation#getAllLoadedClasses()

I do get a selection of all loaded classes by the target JVM. But If I do:

Class.forName("my.class.name")

This will not be the same class as the class loaded by VM. Yes, I can add this particular class as a jar in the agent MANIFEST.MF Class-Path - but that does not look the same to me as getAllLoadedClasses().

Could someone please confirm whether this is correct i.e. I would not be able to find a specific class using Class.forName() when instrumenting? My objective was not to iterate over all loaded classes using getAllLoadedClasses() - But if there is no alternative, I guess that's okay for now.

** UPDATE

What I made a mistake in writing is the Boot-Class-Path which I have now corrected in my manifest. Using -verbose:class logging I managed to see that my jars are being loaded as

[Opened C:\fullpath\someother.jar]
[Opened C:\fullpath\another.jar]
[Opened C:\fullpath\different.jar]

But I don't see any corresponding loading information. I tried adding a Class.forName("a.package.in.someother.jar.classname") and got NoClassDefFoundError. As soon as I jump into the agent jar, I cannot use Class.forName() to check if the class is loaded by the target VM. I am getting a NoClassDefFoundError.

FURTHER UPDATE

Okay I have "Fattened" the manifest to look up all classes in my WEB-INF/lib and tomcat's lib directory. What I can see is below:

1) When my custom class MyClass is loaded for the first time. -verbose shows:

[Loaded my.pkg.MyClass from file:/C:/base/webapps/ROOT/WEB-INF/lib/mypkg.jar]

2) If I try to load the class again, it is correctly showing the above order.

3) My agent jar is manifested with all classes for my tomcat lib and my web-inf/lib directory. And I can also confirm that the loader sees the jars correctly.

4) Now I inject the agent, and call Class.forName("my.pkg.MyClass") from within the agent class. I get the below results.

[Loaded my.pkg.MyClass from file:/C:/base/webapps/ROOT/WEB-INF/lib/mypkg.jar]

I acknowledge that it's system class loader loding it inside my agent code as @RafaelWinterhalter pointed out in one of his answers. Is there any way I can force a "Delegation" so that the a different classloader loads the agent class and therefore, correctly redefines a class.

Any help is appreciated.


回答1:


As it is stated in the javadoc:

Invoking this method is equivalent to: Class.forName(className, true, currentLoader) where currentLoader denotes the defining class loader of the current class.

You can also see from the source code that the method is marked @CallerSensitive which means that you get a different result based on the class loader that invokes the method.

When calling Instrumentation::getAllLoadedClasses, the returned array contains classes of any class loader and not only of the current class loader which is the system class loader when running a Java agent. Therefore:

for (Class<?> type : instrumentation.getAllLoadedClasses()) {
  assert type == Class.forName(type.getName());
}

is not generally true.




回答2:


After a bit of run around, and Thanks to @Holger who reminded me what the problem was - incorrect class loader.

before I inject the agent, I have done the following:

// Get the current context class loader, which is app ext. classLoader
ClassLoader original = Thread.currentThread().getContextClassLoader().getSystemClassLoader();

// Set the system classloader to app classloader which won't delegate anything
Field scl = ClassLoader.class.getDeclaredFields();
scl.setAccessible(true);
scl.set(null, Thread.currentThread().getContextClassLoader());

// Now inject agent
try {
    vm.loadAgent(agentPath, args);
} catch (all sorts of errors/exceptions in chain) {
// Log them and throw them back up the stack.
} finally {
  vm.detach();
  // Put back the classLoader linkage
  sc.set(null, original);
}

How I have confirmed

  1. When it goes in my Agent Class - Thread.currentThread().getContextClassLoader() becomes my application extn loader. But the system classloader now becomes `ParallelWebappClassLoader".

  2. I am assuming this is how it works, but could be totally worng:

    i) When I say Class.forName("my.pkg") it will check the system class loader which is pointing to my loader now. If the class is not found (i.e. not loaded), it will go to parents etc. I believe this is more or less the delegation model is.

    ii) In this way, the class is loaded in VM by the same class loader which would also load the class in my webapp under normal circumstances.

    iii) I will not instrument anything else apart from my own classes so the classloader will always be the same.

So far I have not seen any LinkageError happening. But I still feel this is too risky and if I break the link I am screwed.




回答3:


Using Class.forName in a java profiler must be avoided to escape from the NoClassDef error. JVM Loads the class files in the different level of class loaders based on their classpath setting and the class file demand.

Java Cre libraries + Boot path defended libraries will be loaded in bootstrap Level
Java Agent will be loaded in system level and it goes on. Class.forName() will look the class files from the parent loaders, the current loader will not check the child loader (Until unless we implement our own loaders)

Java core classes will be accessible from your application code but our application code will not be accessible by the Java core classes. Its called class loader hierarchy.

You have three options.

  1. Lookup the class files from the Instrumentation.GetLoadedClassFiles()
  2. Through Transformers you can get all the loaders classes and you can track them and look for your class in every loader until you find.
  3. Have the Class.forname implementation in the lowest level of the hierarchy so that it can internally access all the path.

Maintain the hierarchy properly to avoid too many weird errors.



来源:https://stackoverflow.com/questions/46523055/using-class-forname-in-java-instrumentation-agent

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!