-
Notifications
You must be signed in to change notification settings - Fork 870
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
Allow multiple invokedynamic InstrumentationModules to share classloaders #10015
Conversation
@open-telemetry/java-instrumentation-maintainers Please add the |
b5867b2
to
b443b47
Compare
There is a similar issue with netty instrumentation where other instrumentations use classes from netty instrumentation that currently fails with class cast |
Do you have any pointers for me here by chance to the shared classes? Unfortunately running the tests locally is very cumbersome for me currently, due to unrelated failures. Maybe in case of netty it might make sense to extract interfaces from those shared classes and to expose those by loading them as "shared state" in agent-Classloader instead (see the classloading section of #8999 for details). It depends on whether those extracted interfaces would need references to netty classes (because then they can't be shared). |
...rc/main/java/io/opentelemetry/javaagent/tooling/instrumentation/indy/IndyModuleRegistry.java
Outdated
Show resolved
Hide resolved
...rc/main/java/io/opentelemetry/javaagent/tooling/instrumentation/indy/IndyModuleRegistry.java
Outdated
Show resolved
Hide resolved
if (instrumentedCl != null) { | ||
return instrumentedCl.getResource(resourceName); | ||
} else { | ||
return BOOT_LOADER.getResource(resourceName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't both agent loader and instrumented cl already delegate to boot loader?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the instrumented CL: We don't know, the Classloader could be doing something weird regarding delegation (e.g. OSGi).
For the agent loader I think we use a child/self-first loading, so that e.g. if someone puts bytebuddy on the bootstrap it doesn't affect us, right? That's at least what we do in the elastic-apm-agent, because we had that exact problem before. In that case resources from the agent loader might shadow the resource we actually want to lookup here.
That wouldn't exactly be the problem here, because we try the agent loader before anyway and it returned null
when we reach this code. But if that ever changes, this could easily introduce a subtile bug here. For that reason I'd prefer to keep it explicit here with the BOOT_LOADER
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the instrumented CL: We don't know, the Classloader could be doing something weird regarding delegation (e.g. OSGi).
We instrument class loaders to force boot delegation for packages with our classes to ensure they are accessible with OSGI class loaders.
For the agent loader I think we use a child/self-first loading, so that e.g. if someone puts bytebuddy on the bootstrap it doesn't affect us, right?
Yes. There may still be ways to break it, if the libraries we use have optional components that we don't have in agent then placing them in boot loader could produce unexpected results.
That wouldn't exactly be the problem here, because we try the agent loader before anyway and it returned null when we reach this code. But if that ever changes, this could easily introduce a subtile bug here. For that reason I'd prefer to keep it explicit here with the BOOT_LOADER.
Imo you are overthinking this, if the agent class loader and the instrumented class loader can't find it then it is probably not meant to be found. I think we can keep it for now.
Follow-up for this comment. Part of #8999 .
This PR solves an edge case which to my knowledge is currently only needed by the AWS v2 SDK instrumentation:
When a library we instrument consists of multiple modules, which may or may not be present on the classpath (e.g. SQS in case of AWS), we typically need to create a separate
InstrumentationModule
for each. At the same time, theseInstrumentationModule
s need to communicate with each other, but only for the same classloader they instrumented.With inlined
InstrumentationModule
s this is done by detecting the presence/absence of helper-classes injected by otherInstrumentationModule
into the the target classloader.This PR adds a mechanism with a similar effect:
InstrumentationModules
may declare a "group name". When multiple modules have the same group name AND target the same application classloader, they will share theirInstrumentationModuleClassloader
.By default
InstrumentationModules
will remain isolated, because the default group name is the FQN of the InstrumentationModule.With this PR I've had to also adapt the registration logic for invokedynamic InstrumentationModules:
invokedynamic
instruction is linked, theInstrumentationModuleClassloader
would be initializedInstrumentationModule
is initialized for the instrumented classloader. If the loader for the same module group has already been created, the module will be installed into it.This had to be done because for example the SQS-instrumentation never actually instruments a method: It is just there to match a class and inject corresponding helpers. This new registration approach ensures that this happens in the same way for indy modules.