-
Notifications
You must be signed in to change notification settings - Fork 879
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
Make more tests run with indy #9729
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,11 +31,14 @@ | |
import io.opentelemetry.javaagent.tooling.util.NamedMatcher; | ||
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; | ||
import java.lang.instrument.Instrumentation; | ||
import java.security.ProtectionDomain; | ||
import java.util.Collections; | ||
import java.util.List; | ||
import net.bytebuddy.agent.builder.AgentBuilder; | ||
import net.bytebuddy.description.annotation.AnnotationSource; | ||
import net.bytebuddy.description.type.TypeDescription; | ||
import net.bytebuddy.matcher.ElementMatcher; | ||
import net.bytebuddy.utility.JavaModule; | ||
|
||
public final class InstrumentationModuleInstaller { | ||
|
||
|
@@ -69,44 +72,87 @@ AgentBuilder install( | |
} | ||
|
||
if (instrumentationModule.isIndyModule()) { | ||
return installIndyModule(instrumentationModule, parentAgentBuilder); | ||
return installIndyModule(instrumentationModule, parentAgentBuilder, config); | ||
} else { | ||
return installInjectingModule(instrumentationModule, parentAgentBuilder, config); | ||
} | ||
} | ||
|
||
private AgentBuilder installIndyModule( | ||
InstrumentationModule instrumentationModule, AgentBuilder parentAgentBuilder) { | ||
|
||
IndyModuleRegistry.registerIndyModule(instrumentationModule); | ||
|
||
InstrumentationModule instrumentationModule, | ||
AgentBuilder parentAgentBuilder, | ||
ConfigProperties config) { | ||
List<String> helperClassNames = | ||
InstrumentationModuleMuzzle.getHelperClassNames(instrumentationModule); | ||
HelperResourceBuilderImpl helperResourceBuilder = new HelperResourceBuilderImpl(); | ||
instrumentationModule.registerHelperResources(helperResourceBuilder); | ||
List<TypeInstrumentation> typeInstrumentations = instrumentationModule.typeInstrumentations(); | ||
if (typeInstrumentations.isEmpty()) { | ||
if (!helperClassNames.isEmpty() || !helperResourceBuilder.getResources().isEmpty()) { | ||
logger.log( | ||
WARNING, | ||
"Helper classes and resources won't be injected if no types are instrumented: {0}", | ||
instrumentationModule.instrumentationName()); | ||
} | ||
|
||
return parentAgentBuilder; | ||
} | ||
|
||
List<String> injectedHelperClassNames = Collections.emptyList(); | ||
if (instrumentationModule instanceof ExperimentalInstrumentationModule) { | ||
ExperimentalInstrumentationModule experimentalInstrumentationModule = | ||
(ExperimentalInstrumentationModule) instrumentationModule; | ||
injectedHelperClassNames = experimentalInstrumentationModule.injectedClassNames(); | ||
helperClassNames.removeAll(injectedHelperClassNames); | ||
laurit marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
IndyModuleRegistry.registerIndyModule(instrumentationModule); | ||
|
||
ClassInjectorImpl injectedClassesCollector = new ClassInjectorImpl(instrumentationModule); | ||
if (instrumentationModule instanceof ExperimentalInstrumentationModule) { | ||
((ExperimentalInstrumentationModule) instrumentationModule) | ||
.injectClasses(injectedClassesCollector); | ||
} | ||
|
||
// TODO (Jonas): Adapt MuzzleMatcher to use the same type lookup strategy as the | ||
laurit marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// InstrumentationModuleClassLoader (see IndyModuleTypePool) | ||
MuzzleMatcher muzzleMatcher = | ||
new MuzzleMatcher(logger, instrumentationModule, config) { | ||
@Override | ||
public boolean matches( | ||
TypeDescription typeDescription, | ||
ClassLoader classLoader, | ||
JavaModule module, | ||
Class<?> classBeingRedefined, | ||
ProtectionDomain protectionDomain) { | ||
ClassLoader instrumentationClassLoader = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If my understanding of As a result, the caching won't really work here and it will also cause good entries to be evicted from the cache if it is fixed-size due to "flooding" the cache with temporary classloader. I think we could avoid these problems and keep the muzzle-matching efficient by instead altering // loader cannot be null, must pass "bootstrap proxy" instead of bootstrap class loader
private TypePool createTypePool(ClassLoader loader) {
// ok to use locationStrategy() without fallback bootstrap proxy here since loader is non-null
TypePool instrumentedClPool = AgentTooling.poolStrategy()
.typePool(AgentTooling.locationStrategy().classFileLocator(loader), loader);
if(instrumentationModule.isIndy()) {
TypePool instrumentedClPool = AgentTooling.poolStrategy()
.typePool(AgentTooling.locationStrategy().classFileLocator(loader), loader);
ClassLoader moduleOrAgentCl = instrumentationModule.getClass().getClassLoader();
TypePool agentOrExtensionPool = AgentTooling.poolStrategy()
.typePool(AgentTooling.locationStrategy().classFileLocator(moduleOrAgentCl), moduleOrAgentCl);
return new TypePool() {
@Override
public Resolution describe(String name) {
if(name.startsWith("io.opentelemetry.javaagent")) {
return agentOrExtensionPool.describe(name);
}
return instrumentedClPool.describe(name);
}
@Override
public void clear() {
instrumentedClPool.clear();
agentOrExtensionPool.clear();
}
};
}
return instrumentedClPool;
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will require further investigation. As far as I understand we don't pass the temporary class loader here but the real instrumentation loader. Keep in mind that having to transform a class is really a special case, relatively few classes get transformed. It should be fine as long as loading classes that don't get transformed does minimal work. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Correct, but we make sure that we never hold a strong reference to that The
Makes sense, so we can keep it as is for now. I just thought that my proposed alternative with the custom There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it make sense to insert a strong reference from application class loader to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think that this is worth the effort and complexity now, because IINM the custom TypePool I suggested would still be better and imo less complex: When caching If we instead use something like the custom type pool I suggested, the I think for now the "risk" of the InstrumentationModuleCL being GCed here is okay. If you want to, I can do a follow-up PR trying to implement that custom type pool. |
||
IndyModuleRegistry.getInstrumentationClassloader( | ||
instrumentationModule.getClass().getName(), classLoader); | ||
laurit marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return super.matches( | ||
typeDescription, | ||
instrumentationClassLoader, | ||
module, | ||
classBeingRedefined, | ||
protectionDomain); | ||
} | ||
}; | ||
AgentBuilder.Transformer helperInjector = | ||
new HelperInjector( | ||
instrumentationModule.instrumentationName(), | ||
injectedHelperClassNames, | ||
injectedClassesCollector.getClassesToInject(), | ||
helperResourceBuilder.getResources(), | ||
instrumentationModule.getClass().getClassLoader(), | ||
instrumentation); | ||
|
||
// TODO (Jonas): Adapt MuzzleMatcher to use the same type lookup strategy as the | ||
// InstrumentationModuleClassLoader (see IndyModuleTypePool) | ||
// MuzzleMatcher muzzleMatcher = new MuzzleMatcher(logger, instrumentationModule, config); | ||
VirtualFieldImplementationInstaller contextProvider = | ||
virtualFieldInstallerFactory.create(instrumentationModule); | ||
|
||
AgentBuilder agentBuilder = parentAgentBuilder; | ||
for (TypeInstrumentation typeInstrumentation : instrumentationModule.typeInstrumentations()) { | ||
AgentBuilder.Identified.Extendable extendableAgentBuilder = | ||
setTypeMatcher(agentBuilder, instrumentationModule, typeInstrumentation) | ||
.and(muzzleMatcher) | ||
.transform(new PatchByteCodeVersionTransformer()) | ||
.transform(helperInjector); | ||
|
||
|
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 personally would have preferred to add a
injector.copy(String classname).inject(InjectionMode.CLASS_ONLY)
method toClassInjector
than a separate method.This would allow to see all injected classes (proxies and copies) with a glance at the
injectClasses
method, allow the injection of the bytecode as a resource if needed and easier future extension.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 think we can change this later. I implemented most of this before the pr with
ClassInjector
was merged. My hope is that if we create enough of a mess with the apis then @mateuszrzeszutek will come and clean all of this up.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.
Fine for me, hopefully for @mateuszrzeszutek too 😆