Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented InstrumentationModuleClassLoader for invokedynamic Advice dispatching #9177

Merged
merged 16 commits into from
Aug 21, 2023

Conversation

JonasKunz
Copy link
Contributor

Implements the InstrumentationModuleClassloader with the delegation model described in #8999.
It might also be useful to have a look at the full PoC implementation to see how things fit in the bigger picture.

The classloader also ensures that the lookup of .class resources is consistent with the classloading delegation.
This should avoid the need for manual resource injection in cases such as the SpringWebInstrumentationModule.

@JonasKunz JonasKunz requested a review from a team August 10, 2023 08:30
@linux-foundation-easycla
Copy link

linux-foundation-easycla bot commented Aug 10, 2023

CLA Signed

The committers listed above are authorized under a signed CLA.

if (lastDotIndex != -1) {
String packageName = name.substring(0, lastDotIndex);
if (findPackage(packageName) == null) {
definePackage(packageName, null, null, null, null, null, null, null);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that on jdk8 this might not be safe and may result in IllegalArgumentException from https://github.com/openjdk/jdk8u/blob/587090ddc17c073d56f4d3f52b61f6477d6322b0/jdk/src/share/classes/java/lang/ClassLoader.java#L1587. getPackage searches for package from the parent loader and then from the current loader. As this is a child first loader when it starts to define class parent loader is not locked and can define the same package at the same time which would result in a race where package in parent loader is not define when findPackage is called but already is defined when definePackage is called. For this race to happen this class loader would need to define a class that is in the same package as a class defined in the parent loader at the same time, IDK whether this is really possible. If this were a parallel capable class loader then there could be more ways it could hit a race here and get the same IllegalArgumentException.

Copy link
Contributor Author

@JonasKunz JonasKunz Aug 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any idea how to guard against this?
I can only think of wrapping the definePackage in a try ... catch(IllegalArgumentException) and then printing the occurring exception to the debug logs.

In the elastic-agent we use a bytebuddy ByteArrayInjectingClassloader. This comes with the downside that (a) all the bytecode of injected classes needs to be loaded eagerly and (b) the resource lookup for .class files is either inconsistent or costs heap because the bytecode byte[] need to be kept in memory.
This is something I wanted to improve here by using the ClassCopySource instead. I did my best of basically "inlining" the required logic from ByteArrayClassloader for defining classes here.

You can find the bytebuddy code for defining the package here. It is in theory prone to the same problem, but we have never encountered it in the elastic apm agent so far.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tomcat ignore is https://github.com/apache/tomcat/blob/39c388b51817d346031189a8a942340b6e46b940/java/org/apache/catalina/loader/WebappClassLoaderBase.java#L2280
URLClassLoader catches it and checks whether package is defined before failing https://github.com/openjdk/jdk8u/blob/587090ddc17c073d56f4d3f52b61f6477d6322b0/jdk/src/share/classes/java/net/URLClassLoader.java#L437
I think either of these approaches is fine. In my opinion logging the exception isn't necessary, if you feel it is necessary do make sure to log it at a level where it doesn't get printed to stdout unless debug is enabled.

Copy link
Contributor Author

@JonasKunz JonasKunz Aug 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think reverifying in the catch and rethrowing if the package doesn't exist is the best approach, as this way we won't loose IllegalArgumentExceptions which have a different root cause.

Comment on lines +100 to +103
Map<String, byte[]> appClasses = copyClassesWithMarker("app-cl", A.class, B.class, C.class);
Map<String, byte[]> agentClasses = copyClassesWithMarker("agent-cl", B.class, C.class);
Map<String, byte[]> moduleClasses =
copyClassesWithMarker("module-cl", A.class, B.class, C.class, D.class);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice test, I like the "markers" 👍

JonasKunz and others added 2 commits August 14, 2023 09:14
…ling/instrumentation/indy/InstrumentationModuleClassLoader.java

Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com>
@JonasKunz
Copy link
Contributor Author

I think the test failures are unrelated flaky tests. Would be great if someone with the permissions could re-trigger the build, because I don't have the permissions to do that it seems.

Copy link
Member

@mateuszrzeszutek mateuszrzeszutek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@mateuszrzeszutek mateuszrzeszutek enabled auto-merge (squash) August 21, 2023 11:59
@mateuszrzeszutek mateuszrzeszutek merged commit 5abba34 into open-telemetry:main Aug 21, 2023
@JonasKunz JonasKunz deleted the indy-module-cl branch August 21, 2023 12:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants