22
33import static datadog .trace .agent .tooling .bytebuddy .DDTransformers .defaultTransformers ;
44import static datadog .trace .agent .tooling .bytebuddy .matcher .ClassLoaderMatchers .ANY_CLASS_LOADER ;
5+ import static datadog .trace .agent .tooling .bytebuddy .matcher .ClassLoaderMatchers .hasClassNamed ;
6+ import static datadog .trace .agent .tooling .bytebuddy .matcher .ClassLoaderMatchers .hasClassNamedOneOf ;
7+ import static datadog .trace .agent .tooling .bytebuddy .matcher .HierarchyMatchers .declaresAnnotation ;
58import static datadog .trace .agent .tooling .bytebuddy .matcher .NameMatchers .namedOneOf ;
69import static net .bytebuddy .matcher .ElementMatchers .not ;
710
1417import datadog .trace .api .InstrumenterConfig ;
1518import java .lang .instrument .ClassFileTransformer ;
1619import java .lang .instrument .Instrumentation ;
20+ import java .security .ProtectionDomain ;
1721import java .util .ArrayList ;
1822import java .util .Arrays ;
1923import java .util .BitSet ;
2024import java .util .Collection ;
25+ import java .util .HashMap ;
2126import java .util .List ;
2227import java .util .Map ;
2328import net .bytebuddy .agent .builder .AgentBuilder ;
2429import net .bytebuddy .asm .Advice ;
30+ import net .bytebuddy .asm .AsmVisitorWrapper ;
2531import net .bytebuddy .description .method .MethodDescription ;
32+ import net .bytebuddy .description .type .TypeDescription ;
33+ import net .bytebuddy .dynamic .DynamicType ;
2634import net .bytebuddy .matcher .ElementMatcher ;
35+ import net .bytebuddy .utility .JavaModule ;
2736
2837/** Builds multiple instrumentations into a single combining-matcher and splitting-transformer. */
29- public final class CombiningTransformerBuilder extends AbstractTransformerBuilder {
38+ public final class CombiningTransformerBuilder
39+ implements Instrumenter .TypeTransformer , Instrumenter .MethodTransformer {
40+
41+ // Added here instead of byte-buddy's ignores because it's relatively
42+ // expensive. https://github.com/DataDog/dd-trace-java/pull/1045
43+ private static final ElementMatcher .Junction <TypeDescription > NOT_DECORATOR_MATCHER =
44+ not (
45+ declaresAnnotation (
46+ namedOneOf ("javax.decorator.Decorator" , "jakarta.decorator.Decorator" )));
47+
48+ /** Associates context stores with the class-loader matchers to activate them. */
49+ private final Map <Map .Entry <String , String >, ElementMatcher <ClassLoader >> contextStoreInjection =
50+ new HashMap <>();
51+
3052 private final AgentBuilder agentBuilder ;
3153
3254 private final List <MatchRecorder > matchers = new ArrayList <>();
@@ -52,8 +74,21 @@ public CombiningTransformerBuilder(AgentBuilder agentBuilder, int maxInstrumenta
5274 this .nextSupplementaryId = maxInstrumentationId + 1 ;
5375 }
5476
55- @ Override
56- protected void buildInstrumentation (InstrumenterModule module , Instrumenter member ) {
77+ public void applyInstrumentation (Instrumenter instrumenter ) {
78+ if (instrumenter instanceof InstrumenterModule ) {
79+ InstrumenterModule module = (InstrumenterModule ) instrumenter ;
80+ if (module .isEnabled ()) {
81+ InstrumenterState .registerInstrumentation (module );
82+ for (Instrumenter member : module .typeInstrumentations ()) {
83+ buildInstrumentation (module , member );
84+ }
85+ }
86+ } else {
87+ throw new IllegalArgumentException ("Unexpected Instrumenter type" );
88+ }
89+ }
90+
91+ private void buildInstrumentation (InstrumenterModule module , Instrumenter member ) {
5792
5893 int id = module .instrumentationId ();
5994 if (module != member ) {
@@ -161,8 +196,51 @@ public void applyAdvice(ElementMatcher<? super MethodDescription> matcher, Strin
161196 .advice (not (ignoredMethods ).and (matcher ), className ));
162197 }
163198
164- @ Override
165- protected void applyContextStoreInjection (
199+ /** Counts the number of distinct context store injections registered with this builder. */
200+ private int contextStoreCount () {
201+ return contextStoreInjection .size ();
202+ }
203+
204+ /** Applies each context store injection, guarded by the associated class-loader matcher. */
205+ private void applyContextStoreInjection () {
206+ contextStoreInjection .forEach (this ::applyContextStoreInjection );
207+ }
208+
209+ /** Tracks which class-loader matchers are associated with each store request. */
210+ private void registerContextStoreInjection (
211+ InstrumenterModule module , Instrumenter member , Map <String , String > contextStore ) {
212+ ElementMatcher <ClassLoader > activation ;
213+
214+ if (member instanceof Instrumenter .ForBootstrap ) {
215+ activation = ANY_CLASS_LOADER ;
216+ } else if (member instanceof Instrumenter .ForTypeHierarchy ) {
217+ String hierarchyHint = ((Instrumenter .ForTypeHierarchy ) member ).hierarchyMarkerType ();
218+ activation = null != hierarchyHint ? hasClassNamed (hierarchyHint ) : ANY_CLASS_LOADER ;
219+ } else if (member instanceof Instrumenter .ForSingleType ) {
220+ activation = hasClassNamed (((Instrumenter .ForSingleType ) member ).instrumentedType ());
221+ } else if (member instanceof Instrumenter .ForKnownTypes ) {
222+ activation = hasClassNamedOneOf (((Instrumenter .ForKnownTypes ) member ).knownMatchingTypes ());
223+ } else {
224+ activation = ANY_CLASS_LOADER ;
225+ }
226+
227+ activation = requireBoth (activation , module .classLoaderMatcher ());
228+
229+ for (Map .Entry <String , String > storeEntry : contextStore .entrySet ()) {
230+ ElementMatcher <ClassLoader > oldActivation = contextStoreInjection .get (storeEntry );
231+ // optimization: treat 'any' as if there wasn't an old matcher
232+ if (null == oldActivation || ANY_CLASS_LOADER == activation ) {
233+ contextStoreInjection .put (storeEntry , activation );
234+ } else if (ANY_CLASS_LOADER != oldActivation ) {
235+ // store can be activated by either the old OR new matcher
236+ contextStoreInjection .put (
237+ storeEntry , new ElementMatcher .Junction .Disjunction <>(oldActivation , activation ));
238+ }
239+ }
240+ }
241+
242+ /** Arranges for a context value field to be injected into types extending the context key. */
243+ private void applyContextStoreInjection (
166244 Map .Entry <String , String > contextStore , ElementMatcher <ClassLoader > activation ) {
167245 String keyClassName = contextStore .getKey ();
168246 String contextClassName = contextStore .getValue ();
@@ -178,7 +256,6 @@ protected void applyContextStoreInjection(
178256 transformers [id ] = new AdviceStack (new VisitingTransformer (contextAdvice ));
179257 }
180258
181- @ Override
182259 public ClassFileTransformer installOn (Instrumentation instrumentation ) {
183260 if (InstrumenterConfig .get ().isRuntimeContextFieldInjection ()) {
184261 // expand so we have enough space for a context injecting transformer for each store
@@ -193,4 +270,39 @@ public ClassFileTransformer installOn(Instrumentation instrumentation) {
193270 .transform (new SplittingTransformer (transformers ))
194271 .installOn (instrumentation );
195272 }
273+
274+ static final class VisitingTransformer implements AgentBuilder .Transformer {
275+ private final AsmVisitorWrapper visitor ;
276+
277+ VisitingTransformer (AsmVisitorWrapper visitor ) {
278+ this .visitor = visitor ;
279+ }
280+
281+ @ Override
282+ public DynamicType .Builder <?> transform (
283+ DynamicType .Builder <?> builder ,
284+ TypeDescription typeDescription ,
285+ ClassLoader classLoader ,
286+ JavaModule module ,
287+ ProtectionDomain pd ) {
288+ return builder .visit (visitor );
289+ }
290+ }
291+
292+ static final class HelperTransformer extends HelperInjector implements AgentBuilder .Transformer {
293+ HelperTransformer (String requestingName , String ... helperClassNames ) {
294+ super (requestingName , helperClassNames );
295+ }
296+ }
297+
298+ static ElementMatcher <ClassLoader > requireBoth (
299+ ElementMatcher <ClassLoader > lhs , ElementMatcher <ClassLoader > rhs ) {
300+ if (ANY_CLASS_LOADER == lhs ) {
301+ return rhs ;
302+ } else if (ANY_CLASS_LOADER == rhs ) {
303+ return lhs ;
304+ } else {
305+ return new ElementMatcher .Junction .Conjunction <>(lhs , rhs );
306+ }
307+ }
196308}
0 commit comments