Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -189,12 +189,8 @@ public static ClassFileTransformer installBytebuddyAgent(
}
}

AbstractTransformerBuilder transformerBuilder;
if (InstrumenterConfig.get().isLegacyInstallerEnabled()) {
transformerBuilder = new LegacyTransformerBuilder(agentBuilder);
} else {
transformerBuilder = new CombiningTransformerBuilder(agentBuilder, maxInstrumentationId);
}
CombiningTransformerBuilder transformerBuilder =
new CombiningTransformerBuilder(agentBuilder, maxInstrumentationId);

int installedCount = 0;
for (Instrumenter instrumenter : instrumenters) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

import static datadog.trace.agent.tooling.bytebuddy.DDTransformers.defaultTransformers;
import static datadog.trace.agent.tooling.bytebuddy.matcher.ClassLoaderMatchers.ANY_CLASS_LOADER;
import static datadog.trace.agent.tooling.bytebuddy.matcher.ClassLoaderMatchers.hasClassNamed;
import static datadog.trace.agent.tooling.bytebuddy.matcher.ClassLoaderMatchers.hasClassNamedOneOf;
import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.declaresAnnotation;
import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.namedOneOf;
import static net.bytebuddy.matcher.ElementMatchers.not;

Expand All @@ -14,19 +17,38 @@
import datadog.trace.api.InstrumenterConfig;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.AsmVisitorWrapper;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.utility.JavaModule;

/** Builds multiple instrumentations into a single combining-matcher and splitting-transformer. */
public final class CombiningTransformerBuilder extends AbstractTransformerBuilder {
public final class CombiningTransformerBuilder
implements Instrumenter.TypeTransformer, Instrumenter.MethodTransformer {

// Added here instead of byte-buddy's ignores because it's relatively
// expensive. https://github.com/DataDog/dd-trace-java/pull/1045
private static final ElementMatcher.Junction<TypeDescription> NOT_DECORATOR_MATCHER =
not(
declaresAnnotation(
namedOneOf("javax.decorator.Decorator", "jakarta.decorator.Decorator")));

/** Associates context stores with the class-loader matchers to activate them. */
private final Map<Map.Entry<String, String>, ElementMatcher<ClassLoader>> contextStoreInjection =
new HashMap<>();

private final AgentBuilder agentBuilder;

private final List<MatchRecorder> matchers = new ArrayList<>();
Expand All @@ -52,8 +74,21 @@ public CombiningTransformerBuilder(AgentBuilder agentBuilder, int maxInstrumenta
this.nextSupplementaryId = maxInstrumentationId + 1;
}

@Override
protected void buildInstrumentation(InstrumenterModule module, Instrumenter member) {
public void applyInstrumentation(Instrumenter instrumenter) {
if (instrumenter instanceof InstrumenterModule) {
InstrumenterModule module = (InstrumenterModule) instrumenter;
if (module.isEnabled()) {
InstrumenterState.registerInstrumentation(module);
for (Instrumenter member : module.typeInstrumentations()) {
buildInstrumentation(module, member);
}
}
} else {
throw new IllegalArgumentException("Unexpected Instrumenter type");
}
}

private void buildInstrumentation(InstrumenterModule module, Instrumenter member) {

int id = module.instrumentationId();
if (module != member) {
Expand Down Expand Up @@ -161,8 +196,51 @@ public void applyAdvice(ElementMatcher<? super MethodDescription> matcher, Strin
.advice(not(ignoredMethods).and(matcher), className));
}

@Override
protected void applyContextStoreInjection(
/** Counts the number of distinct context store injections registered with this builder. */
private int contextStoreCount() {
return contextStoreInjection.size();
}

/** Applies each context store injection, guarded by the associated class-loader matcher. */
private void applyContextStoreInjection() {
contextStoreInjection.forEach(this::applyContextStoreInjection);
}

/** Tracks which class-loader matchers are associated with each store request. */
private void registerContextStoreInjection(
InstrumenterModule module, Instrumenter member, Map<String, String> contextStore) {
ElementMatcher<ClassLoader> activation;

if (member instanceof Instrumenter.ForBootstrap) {
activation = ANY_CLASS_LOADER;
} else if (member instanceof Instrumenter.ForTypeHierarchy) {
String hierarchyHint = ((Instrumenter.ForTypeHierarchy) member).hierarchyMarkerType();
activation = null != hierarchyHint ? hasClassNamed(hierarchyHint) : ANY_CLASS_LOADER;
} else if (member instanceof Instrumenter.ForSingleType) {
activation = hasClassNamed(((Instrumenter.ForSingleType) member).instrumentedType());
} else if (member instanceof Instrumenter.ForKnownTypes) {
activation = hasClassNamedOneOf(((Instrumenter.ForKnownTypes) member).knownMatchingTypes());
} else {
activation = ANY_CLASS_LOADER;
}

activation = requireBoth(activation, module.classLoaderMatcher());

for (Map.Entry<String, String> storeEntry : contextStore.entrySet()) {
ElementMatcher<ClassLoader> oldActivation = contextStoreInjection.get(storeEntry);
// optimization: treat 'any' as if there wasn't an old matcher
if (null == oldActivation || ANY_CLASS_LOADER == activation) {
contextStoreInjection.put(storeEntry, activation);
} else if (ANY_CLASS_LOADER != oldActivation) {
// store can be activated by either the old OR new matcher
contextStoreInjection.put(
storeEntry, new ElementMatcher.Junction.Disjunction<>(oldActivation, activation));
}
}
}

/** Arranges for a context value field to be injected into types extending the context key. */
private void applyContextStoreInjection(
Map.Entry<String, String> contextStore, ElementMatcher<ClassLoader> activation) {
String keyClassName = contextStore.getKey();
String contextClassName = contextStore.getValue();
Expand All @@ -178,7 +256,6 @@ protected void applyContextStoreInjection(
transformers[id] = new AdviceStack(new VisitingTransformer(contextAdvice));
}

@Override
public ClassFileTransformer installOn(Instrumentation instrumentation) {
if (InstrumenterConfig.get().isRuntimeContextFieldInjection()) {
// expand so we have enough space for a context injecting transformer for each store
Expand All @@ -193,4 +270,39 @@ public ClassFileTransformer installOn(Instrumentation instrumentation) {
.transform(new SplittingTransformer(transformers))
.installOn(instrumentation);
}

static final class VisitingTransformer implements AgentBuilder.Transformer {
private final AsmVisitorWrapper visitor;

VisitingTransformer(AsmVisitorWrapper visitor) {
this.visitor = visitor;
}

@Override
public DynamicType.Builder<?> transform(
DynamicType.Builder<?> builder,
TypeDescription typeDescription,
ClassLoader classLoader,
JavaModule module,
ProtectionDomain pd) {
return builder.visit(visitor);
}
}

static final class HelperTransformer extends HelperInjector implements AgentBuilder.Transformer {
HelperTransformer(String requestingName, String... helperClassNames) {
super(requestingName, helperClassNames);
}
}

static ElementMatcher<ClassLoader> requireBoth(
ElementMatcher<ClassLoader> lhs, ElementMatcher<ClassLoader> rhs) {
if (ANY_CLASS_LOADER == lhs) {
return rhs;
} else if (ANY_CLASS_LOADER == rhs) {
return lhs;
} else {
return new ElementMatcher.Junction.Conjunction<>(lhs, rhs);
}
}
}
Loading