Merge pull request #1261 from DataDog/mar-kolya/speedup-context-injection-matchers

speedup context injection matchers
This commit is contained in:
Nikolay Martynov 2020-02-28 03:04:05 +01:00 committed by GitHub
commit a26f08957f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 55 additions and 17 deletions

View File

@ -7,6 +7,7 @@ import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.none; import static net.bytebuddy.matcher.ElementMatchers.none;
import datadog.trace.agent.tooling.context.FieldBackedProvider;
import datadog.trace.api.Config; import datadog.trace.api.Config;
import java.lang.instrument.Instrumentation; import java.lang.instrument.Instrumentation;
import java.util.ArrayList; import java.util.ArrayList;
@ -53,6 +54,8 @@ public class AgentInstaller {
addByteBuddyRawSetting(); addByteBuddyRawSetting();
FieldBackedProvider.resetContextMatchers();
AgentBuilder agentBuilder = AgentBuilder agentBuilder =
new AgentBuilder.Default() new AgentBuilder.Default()
.disableClassFormatChanges() .disableClassFormatChanges()

View File

@ -2,7 +2,6 @@ package datadog.trace.agent.tooling.context;
import static datadog.trace.agent.tooling.ClassLoaderMatcher.BOOTSTRAP_CLASSLOADER; import static datadog.trace.agent.tooling.ClassLoaderMatcher.BOOTSTRAP_CLASSLOADER;
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.safeHasSuperType; import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.safeHasSuperType;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import datadog.trace.agent.tooling.HelperInjector; import datadog.trace.agent.tooling.HelperInjector;
@ -20,6 +19,7 @@ import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -341,6 +341,20 @@ public class FieldBackedProvider implements InstrumentationContextProvider {
}; };
} }
/*
Set of pairs (context holder, context class) for which we have matchers installed.
We use this to make sure we do not install matchers repeatedly for cases when same
context class is used by multiple instrumentations.
*/
private static final Set<Map.Entry<String, String>> INSTALLED_CONTEXT_MATCHERS = new HashSet<>();
/** Clear set that prevents multiple matchers for same context class */
public static void resetContextMatchers() {
synchronized (INSTALLED_CONTEXT_MATCHERS) {
INSTALLED_CONTEXT_MATCHERS.clear();
}
}
@Override @Override
public AgentBuilder.Identified.Extendable additionalInstrumentation( public AgentBuilder.Identified.Extendable additionalInstrumentation(
AgentBuilder.Identified.Extendable builder) { AgentBuilder.Identified.Extendable builder) {
@ -350,25 +364,46 @@ public class FieldBackedProvider implements InstrumentationContextProvider {
/* /*
* For each context store defined in a current instrumentation we create an agent builder * For each context store defined in a current instrumentation we create an agent builder
* that injects necessary fields. * that injects necessary fields.
* Note: this synchronization should not have any impact on performance
* since this is done when agent builder is being made, it doesn't affect actual
* class transformation.
*/ */
builder = synchronized (INSTALLED_CONTEXT_MATCHERS) {
builder // FIXME: This makes an assumption that class loader matchers for instrumenters that use
.type(safeHasSuperType(named(entry.getKey())), instrumenter.classLoaderMatcher()) // same context classes should be the same - which seems reasonable, but is not checked.
.and(safeToInjectFieldsMatcher()) // Addressing this properly requires some notion of 'compound intrumenters' which we
.and(Default.NOT_DECORATOR_MATCHER) // currently do not have.
.transform(AgentBuilder.Transformer.NoOp.INSTANCE); if (INSTALLED_CONTEXT_MATCHERS.contains(entry)) {
log.debug("Skipping builder for {} {}", instrumenter.getClass().getName(), entry);
continue;
}
/* log.debug("Making builder for {} {}", instrumenter.getClass().getName(), entry);
* We inject helpers here as well as when instrumentation is applied to ensure that helpers INSTALLED_CONTEXT_MATCHERS.add(entry);
* are present even if instrumented classes are not loaded, but classes with state fields
* added are loaded (e.g. sun.net.www.protocol.https.HttpsURLConnectionImpl).
*/
builder = injectHelpersIntoBootstrapClassloader(builder);
builder = /*
builder.transform( * For each context store defined in a current instrumentation we create an agent builder
getTransformerForASMVisitor( * that injects necessary fields.
getFieldInjectionVisitor(entry.getKey(), entry.getValue()))); */
builder =
builder
.type(safeHasSuperType(named(entry.getKey())), instrumenter.classLoaderMatcher())
.and(safeToInjectFieldsMatcher())
.and(Default.NOT_DECORATOR_MATCHER)
.transform(AgentBuilder.Transformer.NoOp.INSTANCE);
/*
* We inject helpers here as well as when instrumentation is applied to ensure that
* helpers are present even if instrumented classes are not loaded, but classes with state
* fields added are loaded (e.g. sun.net.www.protocol.https.HttpsURLConnectionImpl).
*/
builder = injectHelpersIntoBootstrapClassloader(builder);
builder =
builder.transform(
getTransformerForASMVisitor(
getFieldInjectionVisitor(entry.getKey(), entry.getValue())));
}
} }
} }
return builder; return builder;