-
Notifications
You must be signed in to change notification settings - Fork 867
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
Faster type matching #5724
Faster type matching #5724
Conversation
dd252f8
to
bf1c29f
Compare
I think we could probably disable them by default and explicitly state in our docs that they're very expensive to apply, if you use JAX-RS then you have to enable it manually. |
These expensive instrumentations are only used to create a span for the annotated method. We already have other spans for both JAX-RS and JAX-WS create by framework specific instrumentations that are probably more useful than the expensive one. |
💯 |
If the jax instrumentations are missing classloader matchers, can we add them and still keep the instrumentation enabled by default? |
They have the class loader matchers. The problem is that these apis are present in a lot application servers which will enable these instrumentation even when they aren't really used. I have enable them for now, we can deal with this later. |
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.
❤️❤️❤️
These expensive instrumentations are only used to create a span for the annotated method. We already have other spans for both JAX-RS and JAX-WS create by framework specific instrumentations that are probably more useful than the expensive one.
I'd be interested in seeing the impact to the tests of disabling these expensive ones in a follow-up. I think it would depend on what exactly we lose by having these off-by-default, but I'm very open to it, as startup performance is a big deal.
...t-tooling/src/main/java/io/opentelemetry/javaagent/tooling/util/IgnoreFailedTypeMatcher.java
Outdated
Show resolved
Hide resolved
javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/DefineClassHandler.java
Outdated
Show resolved
Hide resolved
ClassReader cr = new ClassReader(classBytes, offset, length); | ||
String superName = cr.getSuperName(); | ||
if (superName != null) { | ||
Class.forName(superName.replace('/', '.'), false, classLoader); | ||
} | ||
String[] interfaces = cr.getInterfaces(); | ||
for (String interfaceName : interfaces) { | ||
Class.forName(interfaceName.replace('/', '.'), false, classLoader); | ||
} |
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.
oh! for some reason I didn't think it was so cheap to get the superclass and interfaces!
I had a similar optimization to preload super types in glowroot: https://github.com/glowroot/glowroot/blob/main/agent/core/src/main/java/org/glowroot/agent/impl/PreloadSomeSuperTypesCache.java
but it stored the superclass/interfaces into a file cache during loading, and then used that to speed up subsequent startups
(this is so much better 🤩)
javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/DefineClassHandler.java
Outdated
Show resolved
Hide resolved
muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/AgentCachingPoolStrategy.java
Outdated
Show resolved
Hide resolved
muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/AgentCachingPoolStrategy.java
Show resolved
Hide resolved
c789962
to
77dae8d
Compare
I did some rough measurements starting liferay. I measured invocation count & time spent for 2 point
This pr.
Class loader optimization matcher disabled, this ensures that the jax-rs/ws annotation matchers have something to match.
Class loader optimization matcher disabled, jax-rs/ws annotation matchers disabled.
Class loader optimization matcher disabled, jax-rs/ws annotation matchers disabled, matchers that match based on annotation (external annotations, withspan, spring-ws) disabled.
Byte-buddy locates bytes for all annotations on all methods if you have a matcher that matches based on annotation. I hope I can convince byte-buddy author to change this or give us some way to just match based on the name without locating the annotation type. In retrospect I realized that I probably should have counted only the classes located during matching. Then the last test would give
which looks good. |
private static final AgentBuilder.PoolStrategy POOL_STRATEGY = | ||
new AgentCachingPoolStrategy(locationStrategy(bootstrapProxy)); |
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.
I know that this will currently work, but the combination of volatile globals and static class initializers makes me worried - WDYT about constructing the PoolStrategy
in the AgentInstaller
and passing it along as constructor parameter? (this'd require making InstrumentationLoader
a "normal" class, not an SPI implementation)
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.
This is also used from muzzle. WDYT if I add an SPI for getting the bootstrap proxy?
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.
That'd probably solve the problem, but wouldn't just creating a bootstrap proxy CL in the muzzle module be a bit simpler?
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.
It depends on whether muzzle code needs the same bootstrap proxy as agent (as agent jar is appended to bootclasspath getting access to resources in agent needs a bit of work). Having multiple pool strategy instances could complicate this a bit as then they wouldn't share the cache unless some stuff is made static.
muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/AgentCachingPoolStrategy.java
Show resolved
Hide resolved
// unlike byte-buddy implementation we cache the descriptor to avoid having to find | ||
// super class and interfaces multiple times |
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.
👍
wow, those matchers are slow!! (esp. now that the inheritance hierarchy matchers have been optimized in this PR 👍) |
Co-authored-by: Trask Stalnaker <[email protected]>
2311d71
to
8af1e6a
Compare
🎉 |
* Faster type matching * make findLoadedClass accessible on java17 * enable jaxrs instrumentation for quarkus test * fix websphere * fix muzzle * javadoc formating * ignore classes that are know to fail to load for virtual field transforms * add back jaxrs and jaxws annotation instrumentations * Apply suggestions from code review Co-authored-by: Trask Stalnaker <[email protected]> * fix compile error * comments * replace deprecated method usage * add comment * add an spi to get access to bootstrap proxy from muzzle module Co-authored-by: Trask Stalnaker <[email protected]>
Nice matchers match based on the class that is being loaded, very nice matchers just match the class name.
Naughty matcher match based on the class hierarch (does this class extend X, does it implement Y).
Evil matchers match based on the complex criteria that involves other classes (does this class contain a method that overrides a method annotated with A in super class or interfaces). We have 4 such evil matchers: jax-rs 1, jax-rs 2, jax-ws and external annotations. The last one can hopefully be fixed by removing
hasSuperType
(I can't see a reason why it is there in the first place), the rest should be disable by default, deleted or written in some other way.The problem with naughty matchers is that they need to find bytes for super types, parse them and cache them to avoid repeating this. If there are many classes this can be quite costly. This PR introduces an alternative strategy. When a class is loaded, first ensure its super types are loaded. During matching find super type with
ClassLoder.findLoadedClass
and use the already loaded class for navigating type hierarchy. If just the name or the modifiers of the super type are needed then there is no need to find the actual bytes for the class. Additionally using this strategy fixes a shortcoming of current strategy. Based on just the class loader of class being loaded it is not always possible to find bytes for all of its super types as in OSGI environments super type of super type need not be visible to current class. There should be less stack traces fornet.bytebuddy.pool.TypePool$Resolution$NoSuchTypeException
in smoke tests for server thats use OSGI.